開発メモ

開発関係のメモをいろいろと。たぶん。

Xcode 6.0とSwift 1.0で気になったところ

Xcode 6でSwiftを一週間ほどまじめに使ってみて、その間に気になった事のまとめ。細かすぎて伝わらなくても気にしない。

問題を確認した環境

環境 情報
Xcode 6.0.1 (6A317)
Swift 1.0
Date 2014/09/25

条件式の『()』はオプションのはずだけど、付けるとエラーになるパターンが有る

Swiftの資料の説明はこんな感じ。

Use if and switch to make conditionals, and use for-in, for, while, and do-while to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required.

けど、『()』を付けるとコンパイルが通らなくなるパターンが有る。具体的に言うと、『if let』の形を使うパターンでコンパイルが通らなくなる。

コンパイル出来るパターン

var str: String?

// コンパイル出来るパターン
if let tmp = str {
    println("str: \(tmp)")
}

コンパイル出来ないパターン

var str: String?

// コンパイル出来ないパターン
if( let tmp = str ) {
    println("str: \(tmp)")
}

ちなみに、『()』の位置を調整しても駄目。

var str: String?

// コンパイル出来ないパターン
if let( tmp = str ) {
    println("str: \(tmp)")
}

結論

C言語系の流れで、すべての条件式に『()』を付けようとしてるとハマる。そもそも言語が違うんだから、素直に、Swiftに馴染んだほうが良いかと。たぶん。

定数(?)がconvenience initから使えないのが不便

クラスのプロパティに、定数として使うつもりで初期値付きのletを定義しても、これを、 convenience initで使おうとするとエラーになるのが不便。

具体的にはこんな感じ。

class ConstTest {
    let DEFAULT_LEN = 2.0                // 定数として使いたい値
    var length: Double

    init(length: Double) {
        self.length = length
    }

    convenience init() {
        self.init(length: DEFAULT_LEN)   // ここで、コンパイルエラーになる
    }
}

このケースだと、convenienceを外してその場で初期化するようにすれば対応できる。ただ、initの中で複雑な処理をやってる場合、いろいろと面倒なことに。

対応策

定数として使いたい値は、はっきり、定数にしてしまうのがわかりやすいかと。

class ConstTest {
    struct Const {
        static let len = 2.0           // 定数として使いたい値
    }
    var length: Double

    init(length: Double) {
        self.length = length
    }

    convenience init() {
        self.init(length: Const.len) // 問題なくコンパイル可能
    }
}

Swiftでは普通のクラス定数(?)が使えないので、定数を管理するための構造体が必要になるのが面倒かな。複数のクラスで同じ値を使うのであれば、構造体を外に出せば良いかと。

『Plain Text』のファイルにペーストした時、先頭のtabが切り捨てられるようになった

『Plain Text』ってのはファイルの属性で指定するあれ。

f:id:see_ku:20140925140214p:plain

定数の説明に使ったクラスを、Xcode 5.1でペーストした場合。

class ConstTest {
    struct Const {
        static let len = 2.0            // 定数として使いたい値
    }
    var length: Double

    init(length: Double) {
        self.length = length
    }

    convenience init() {
        self.init(length: Const.len)    // 問題なくコンパイル可能
    }
}

Xcode 6でペーストした場合。

class ConstTest {
struct Const {
static let len = 2.0            // 定数として使いたい値
}
var length: Double

init(length: Double) {
self.length = length
}

convenience init() {
self.init(length: Const.len)    // 問題なくコンパイル可能
}
}

対応策はこんな感じ。

  1. 諦める
    先頭のtabが消えても気にしない。
  2. 『Plain Text』を使わないようにする
    『Swift Source』に変更するとか。

そもそも、こんな事を気にする人間がどれぐらい居るのか・・・

『YES』と『NO』が無くなった

Objective-Cで多用されてた『YES』と『NO』が無くなった。

Objective-Cの『TRUE』と『FALSE』は、SwiftではBool型の『true』と『false』になる。『YES』と『NO』も同様に、『true』と『false』になるんだけど・・・ わかりにくい?

例えば、ViewControllerを表示する場合。Swiftで書くとこんな感じになる。

presentViewController(vc, animated: true, completion: nil)

Objective-Cみたいに『yes』が使えれば、こんな風に書けたはず。

presentViewController(vc, animated: yes, completion: nil)

では、どうするべきか?

  1. 諦める
    すべて『true』と『false』で書く。違和感は諦める。慣れろ。
  2. グローバル変数で『yes』と『no』を定義する
    これで『yes』と『no』が普通に使えるようになるけど、『no』なんて変数はローカル変数でもよく出てくるので、変数名の重複が問題になる。
  3. その他
    重複しそうにない名前でグローバルに定義する? それはそれで違和感がありそう。

これは、諦めて慣れるのが正解かな・・・

遷移先のNavigation Itemが無くなった?

Navigation Controllerを使って複数のViewControllerをつなげる場合、Xcode 5.1までは2個目以降のViewControllerにもNavigation Itemが自動で追加されていたのが、Xcode 6では自動で追加されなくなった。

例えば、こんな風にViewControllerを接続した場合。

f:id:see_ku:20140925140252p:plain

1個目のViewControllerにはNavigation Itemが自動で追加されてるが、2個めのViewControllerには無い。Xcode 5.1までは、2個めのViewControllerにもNavigation Itemが自動で追加されていた。

f:id:see_ku:20140925140310p:plain

では、どうすればいいのか? とても単純な話で、自動で追加されないなら手動で追加すれば良い。具体的に言うと、Navigation Itemを自力でViewControllerに追加。

f:id:see_ku:20140925140336p:plain

これで、先頭の画面と同じように、Navigation Itemの設定ができるようになる。

意味のないブロックが作れない

Objective-Cでは、特に意味が無いブロックでも問題なく作成できたので、変数のスコープを管理するのに便利だったんだけど、Swiftでは同じようなことができなくなってる。

具体的にはこんな感じ。

Objective-Cでは問題なし。

- (void)scopeTest
{
    {
        NSString* str = @"Scope 1";
        NSLog( @"Scope: %@", str );
    }
    {
        NSString* str = @"Scope 2";
        NSLog( @"Scope: %@", str );
    }
}

Swiftではコンパイルエラー。

 func scopeTest() {
        {
            let str = "Scope 1"
            println("Scope: \(str)")
        }
        {
            let str = "Scope 2"
            println("Scope: \(str)")
        }
    }

これは、Swiftの言語仕様として、すべてのブロックがクロージャーとして扱われてるのが原因。たぶん。

では、どうするべきか? ・・・諦めるしかないかな。

クラス内extensionは使えない

具体的にはこんな感じ。

class ClassExt {
    struct Info {
    }
    extension Info { // ここでエラーになる
    }
    let info = Info()
}

これが出来れば、クラスの上の方にプロパティや構造体の定義を集めて書いて、関数は下にまとめるって書き方が出来たんだけど・・・ 現時点ではコンパイルエラーに。

単純にコンパイルエラーになるのはもちろん、ソースを保存しただけでSourceKitServiceがクラッシュしたりするのでおすすめできない。

f:id:see_ku:20140925140404p:plain

ちなみに、extensionをクラスの外に出せば普通に使用可能。

class ClassExt {
    struct Info {
    }
    let info = Info()
}

extension ClassExt.Info {
}

class定義にプロパティや構造体の定義を集めて、あとはすべてextentionにするのは可能だけど・・・ これはこれで、わかりにくいかな。

enumの自動補完が甘い

自動補完して欲しいのに、してくれないパターンが有る。具体的にはこんな感じ。

enum ColorPattern {
    case Red
    case Blue
    case Green
}

func test(pat: ColorPattern) {
    if pat == .                // ここで『.』を入力した瞬間に補完して欲しい

}

enumの名称(この場合はColorPattern)を先に入力しておけば自動補完が効くけど・・・ それは、あまり美しくないような。

もっと言うと、switchの条件式でenumの値を使う時は、自動ですべての条件に対応するcase文を補完して欲しいところ。Xcode 7.1ぐらいで出来るようになるかな?

小ネタ集

以下、本当に細かいネタ。たぶん。

ソースコードの区切りはどうなった?

Objective-Cの場合はこんな感じ。

#pragma mark - now

Swiftではこんな風に書けば、同じ効果になる。

 // MARK: - now

ソースコードディレクトリの中に入れても大丈夫?

Swiftのソースコードを、サブディレクトリを作って管理してみたけど特に問題なさそう。

デバッグ実行が全体的におかしい?

ステップオーバーの挙動がソースコードと一致しない。Xcodeのバージョンが上がれば解決する問題・・・ かな?

コンパイルが重い

Objective-Cよりかなり重い。同じ内容をObjective-CからSwiftに書き換えて、Objective-Cの時代は特に何も感じなかったのが、Swiftだとコンパイルで待たされるように。CPUパワー(?)をがんがん使ってファンまで回るように。

一昔前のMacBook Airで開発するのは厳しい時代になったか・・・

if letの連結はできない

以下の様な書き方は出来ない。今のところ。

var v0: UIView?
var v1: UIView?

// ng
if let t0 = v0 && let t1 = v1 {
    println("ng")
}

素直に、二重に書くしか無い。

var v0: UIView?
var v1: UIView?

// ok
if let t0 = v0 {
    if let t1 = v1 {
        println("ok")
    }
}

基本的な構造体でも演算子が定義されてない

例えば、『CGPoint + CGPoint -> CGPoint』とか。あっても良さそうな物がない。すべて自力で定義すれば済む話だけど・・・ 最初から、公式で用意してくれたほうが、いろいろ便利なんだけど。

リファクタリングが使えない

さらっと書いてるけど、たぶん、現時点での最大の問題点。

f:id:see_ku:20140925140921p:plain

すべて、手動でどうにかするしか無い。クラス名の変更とか、面倒すぎるな。

はてなブログmarkdownはいつになったらswiftに対応するのか?

やる気はあるの?

資料

iTunes - ブック - Apple Inc.「The Swift Programming Language」
https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11

iTunes - ブック - Apple Inc.「Using Swift with Cocoa and Objective-C
https://itunes.apple.com/jp/book/using-swift-cocoa-objective/id888894773?mt=11

iOS Developer Library
https://developer.apple.com/library/prerelease/ios/navigation/


(2014/09/25 作成)