Swift移行時に引っかかりそうなことを解決する!

第2回Objective-Cとの違いからSwift言語を学ぶ

はじめに

前回の記事を入稿した直後に、スタンフォード大学がSwiftでのiOSアプリの開発講座をiTunes Uで公開したとの記事を読みました。さらっと観たのですが、SwiftだけではなくXcodeの使い方やStoryboardでのレイアウトの方法など、iOSアプリケーション開発全般の講義となっています。英語ではありますが、無料なので試しに受講してみはいかがでしょうか。

前回はSwiftを学ぶための情報源の紹介と、素早くSwiftを試すことのできるPlaygroundについてご紹介しました。

第2回の今回は、Swift言語について、Objective-Cとの違いを中心にご紹介したいと思います。

変数(varとlet)

// Objective-C
NSInteger i = 0;

// Swift
var i = 0

Swiftの場合、変数の宣言はvarで始まります。型の指定はありませんが、値から型を推論してくれるため省略できます。きちんと書くならば、

// Swift
var i : Int = 0

と言うように、変数の後ろにコロンで区切って型を記述します。また、

// Swift
let n = 10

のように、let で宣言すると、値を変更できない定数として扱われます。

// Objective-C
const NSInteger n = 10;

と同じですが、Swiftのほうがより簡単に記述できます。

Objective-Cでは明らかに定数の場合ぐらいしかconstを使いませんでしたが、Swiftではインスタンス化したオブジェクトにはletを使うケースが多いです。Swiftでは、基本letを使用し、値を変更する可能性がある場合のみvarを使用する、という考えのほうが良いでしょう。

真偽値と数値

Int以外にも一般的な言語で提供されているような数値型が提供されています。

// Swift
var flag          = true      // Bool    (BOOL)
var point : UInt  = 100       // UInt    (NSUInteger)
var star  : float = 3.5       // float   (float)
let pi            = 3.141592  // Double  (double)

上記のうち、BoolとDoubleは型推論が可能なので、宣言を省略できます。ちなみにBool型はYES/NOからtrue/falseになっています。

文字列(String)

Swiftに切り替えることの一番のメリットは、文字列の扱いが楽になったことと言っても過言ではないでしょう。

// Objective-C
NSString *hello      = @"Hello";
NSString *world      = @"World";
NSString *helloWorld = [NSString stringWithFormat:@"%@, %@!", hello, world];

// Swift
let hello      = "Hello"
let world      = "World"
let helloWorld = "\(hello), \(world)!"  // Hello, World!

まず文字列の先頭からアットマーク「@」がなくなりました。そして、文字列内に変数を埋め込むことができるようになりました。上記の例のように、バックスラッシュに続けて括弧の中に変数を直接記述できます。

もちろん、下記のような直感的な文字列演算も可能です。

// Swift
let helloWorld = hello + ", " + world + "!"

文字列はString型で、上記コードでも型推論による宣言の省略がされています。

このStringはNSStringと互換性があり、NSStringを引数に取るメソッドにもそのまま渡すことがきます。また、NSStringのメソッドも(ほぼ?)実装されているので、Objective-Cで使い慣れてきたメソッドも引き続き利用できます。

// Swift
helloWorld.hasPrefix("Hello")  // true

ちなみにご覧のとおり、C言語からの名残だったポインタを示すアスタリスク「*」はSwiftでは不要です。

配列(Array)

配列もシンプルに扱えるようになりました。

// Objective-C
NSArray        *array        = @[@"abc", @"123"];
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"xyz", @"789", nil];
[mutableArray addObjectsFromArray:array];  // [@"xyz", @"789", @"abc", @"123"]

// Swift
let array        = ["abc", "123"]
var mutableArray = ["xyz", "789"]
mutableArray += array                      // ["xyz", "789", "abc", "123"]

NSArrayとNSMutableArrayはArray型となり、その区別(可変かどうか)は、宣言時のletとvarで行うようになりました。このケースでも型推論により型の指定を省略しています。ちゃんと書くと、

// Swift
let array1 : Array<String> = ["abc", "123"]
let array2 : [String]      = ["abc", "123"]

となります。array1の宣言は、C++で言うところのテンプレート、Javaで言うところのジェネリクスに相当するかと思います。<>で、配列の要素がString型である事を示しています。その下のarray2はその省略記法となります。

実は、Swiftの配列は要素の型が決まっていて、すべての要素が同じ型でなければなりません。ただ、SwiftにはObjective-Cのidに相当するAnyObjectという特別な型があり、これを使うことであらゆる型の要素を含む配列にすることが可能です。

// Objective-C
NSArray *array = @[@"abc", @123];

// Swift
let array = ["abc", 123]           // Array<AnyObject>

また上記のように、NSArrayでは整数などのプリミティブ型の値はNSNumberのようなオブジェクトにする必要がありましたが、Swiftではそのまま格納可能です。

NSArrayとArrayは、下記のように変換することができます。NSArrayから変換した場合は、Array<AnyObject>になります。

// Swift
let nsarray = NSArray(array: ["123", "abc"])  // NSArray
let array   = Array(nsarray)                  // Array<AnyObject>

その他の便利なメソッドなどは、Swift Standard Library ReferenceのArrayの項を参照ください。

辞書(Dictionary)

辞書も基本的には配列と同じ扱いです。

// Objective-C
NSMutableDictionary *numOfLegs = @{@"bird" : @2, @"dog" : @4, @"spider" : @8}.mutableCopy;
numOfLegs[@"fish"] = @0;

// Swift
var numOfLegs = ["bird": 2, "dog": 4, "spider": 8]  // Dictionary<String, Int>
numOfLegs["fish"] = 0

表記はJSONに似た感じですが、キーはStringだけでなく、IntやDoubleも使えるようです(使い道がわかりませんが……⁠⁠。そして、キーと値の型の組み合わせは、すべての要素で同じ必要があります。ただ、配列同様AnyObjectを利用することで、下記のようなデータを保持することも可能です。

// Swift
var person = ["name": "taro", "age": 32, "married": true]  // Dictionary<AnyObject>

ループ

他の言語と同様の for ループはもちろん使えます。

// Swift
for var i = 0 ; i < 10 ; i++ {  // i = 0, 1, ..., 9
    ...
}

変数の定義(上記のi)には、varが必要です。括弧は不要ですが、あっても問題ないです。

よりSwiftらしくするならば、下記のように記述します。

// Swift
for i in 0..<10 {  // i = 0, 1, ..., 9
    ...
}

0..<10の表記は範囲(右の値を含まない)を指定するものです。単純なループの場合は、この表記のほうが楽に書けます。

範囲の代わりに配列や辞書を指定することで、すべての要素をイテレーションすることが可能です。

// Swift

// Array
for value in ["bird", "dog", "spider"] {
    ...
}
// Dictionary
for (key, value) in ["bird": 2, "dog": 4, "spider": 8] {
    ...
}

辞書の場合は特殊で、キーと値がそれぞれ、key、valueに格納されます。これはSwiftのタプル(Tuple)と言う、1度に複数の値を返す仕組みを使っています。そのため、ここの括弧は省略できません。

whileやdo whileなどは、Objective-Cと同じ構文で記述できます。

条件分岐

if文については目新しいものはないですが、switch文は様々な拡張がされており、より便利で、より安全な記述ができるようになっています。

  • case条件を複数列挙したり、範囲で指定することができる。
  • 文字列での比較が可能。
  • breakを省略可能(ただし1行も命令が無い場合は、breakを記述する⁠⁠。
  • すべてのケースをカバーしないとコンパイルエラーとなる。カバーできていない場合はdefaultを記述する必要がある。

これら、サンプルコードで見て行きましょう。

// Swift
let number = 11
var card   = ""
switch number {
    case 1:
        card = "A"        // breakは書かなくてOK
    case 2...10:          // 範囲での指定が可能
        card = String(i)
    case 11:
        card = "J"
    case 12:
        card = "Q"
    case 13:
        card = "K"
    default:              // 全ての整数をカバーするために、defaultが必須
        break             // 1行も命令が無いのでbreakを記述
}
// card は "J" となる
// Swift
let card   = "J"
var number = 0
switch card {
    case "A", "a":              // 列挙での指定が可能
        number = 1
    case "2"..."9", "10":       // 範囲と列挙の組み合わせでの指定も可能
        number = card.toInt()!
    case "J", "j":
        number = 11
    case "Q", "q":
        number = 12
    case "K", "k":
        number = 13
    default:
        break
}
// number は 11 となる

このように、柔軟でかつ安全な記述が出来るようになりました。

列挙型(enum)

列挙型も大幅に拡張されましたが、まずは基本的なところを押さえておきたいと思います。

// Swift
enum Color {
  case Red
  case Green
  case Blue
}

caseの後にメンバを定義をします。Objective-Cのように、これらに自動的に数値が割り当てられてその数値で比較するのではなく、メンバをそのまま比較に使うことができます。

// Swift
let color = Color.Green
var label = ""
switch color {
  case .Red:  // colorの型がColorと推定できるため、表記を省略できる。
    label = "Red"
  case .Green:
    label = "Green"
  case .Blue:
    label = "Blue"
}

これが一番シンプルな使い方です。もちろん、値を割り当てることもできます。IntだけではなくStringなども可能です。

// Swift
enum Color : String {
  case Red   = "赤"
  case Green = "緑"
  case Blue  = "青"
}
var label = Color.Green.rawValue  // "緑"

割り当てた値を使うためには、上記のように.rawValueを付け加えて値を取り出す必要があります。

これ以外にも、メンバに関数を定義したり、クラスのようなこともできますが、ひとまず上記を押さえておけば良いでしょう。

オプショナル型(Optional)

オプショナル型は、Swift言語の最も特徴的な仕様であり、一番つっかかるポイントだと思います。筆者も、いまだに悩むことが少なくないです。

まず、Swiftでは通常、nilを代入することはできないことを理解する必要があります。

var i = nil  // コンパイルエラー
var j = 0
j = nil      // コンパイルエラー

どうしてもnilを代入したい場合は、下記のようにします。

var i : Optional<Int> = nil
var j : Optional<Int> = 0
j = nil

型はIntではなく、Optional<Int>とします。Intとは違う完全に別の型です。ただこれだと面倒なので、

var i : Int? = nil
var j : Int? = 0
j = nil

のように省略して書くことができます。おそらく、最初からこのように説明されることが多いと思いますが、あくまでOptional型であることを意識しておく必要があります。

では、演算をしてみましょう。

var i : Int  = 1
var j : Int? = 2
i + j             // コンパイルエラー

演算のところでコンパイルエラーとなります。これは、jがInt型ではなく、Optional型であるためです。そのため、このOptional型からInt型の値を取り出す必要があります。これを「アンラップ」と良います。

i + j!            // 3

Optional型の変数の後ろに「!」をつけることで、値を取り出す(アンラップする)ことができます。ただし、この時jがnilの場合には実行時にエラーとなるため、事前にnilではないことを保証しておく必要があります。

その1つの手法として下記のような記述ができます。ifの条件式で代入するとアンラップされ、nilの場合にはfalseを返す、と言うものです。

if let unwrapped_j = j { // 代入時にアンラップされ、nilの場合はfalseとなる
  i + unwrapped_j
}
else {
  i                      // jはnilだった
}

また、⁠??」演算子を使ってnilの時の代替値を指定することもできます。

i + (j ?? 0)  // jがnilの場合には代わりに0として演算して、
              // nilでなければアンラップする

この演算子はとても便利なのですが、ググるのがかなり難しいので忘れないようにしておきましょう。

実はこれ以外にも、オプショナル型に関してはいろいろ知っておくことがあるのですが、続きは第4回の実践編の中で説明したいと思います。

こんなにも複雑なのにオプショナル型が採用されているのは、より安全なコードが書けるようになるためで、マスターすればシンプルかつ安全なコードが書けるようになります。最初はピンとこないと思いますが、使って行くうちに慣れると思うので、実践の中でマスターしていくのが良いでしょう。

次回は?

今回はSwift言語の基本的な仕様について、Objective-Cと比較しながら説明しました。最初慣れは必要ですが、より簡素に記述できることがおわかりいただけたかと思います。

Swiftには他にもいろいろな仕様がありますが、残りは第4回のiOSアプリ開発の説明の中で解説したいと思います。

次回は、Xcodeのプロジェクトの構成やCocoaPodsの導入方法について解説します。

おすすめ記事

記事・ニュース一覧