Swift4 の基本文法をマスターしよう

Xcode9 より Swift4 となりました。Swift4 では辞書(Dictionary)で便利なメソッドがいくつか追加されているので、Swift3 までの文法をすでに学習されている方は、辞書(Dictionary)の項目で知らないメソッドがないか確認してみましょう。

変数

宣言

再代入を許可する場合は var、許可しない場合(=定数)は let を使用します。

var a = 1
a = 2

let a = 1
a = 2 // エラー

データ型を指定して宣言することもできます。

var a: Int
var a: String = "Hello"

Swift のデータ型には以下のものがあります。

データ型 内容
String 文字列
Int -2147483648 ~ 2147483647(32ビット)
-9223372036854775808 ~ 9223372036854775807(64ビット)
Float 浮動小数点(32bit長)
Double 浮動小数点(64bit長)
Bool 真偽値
Dictionary 辞書型
Array 配列
Any 総称型(何でも入る)

何も指定せずに小数を宣言すると Double 型になります。

var a = 100.5 // Double

注意点として、計算処理をする時に、Int + Double のような異なる型との計算はエラーになります。

var a: Int = 10
var b: Double = 10.5
a + b // エラー

こういう場合は、どちらかの型を変換して同じ型同士にする必要があります。以下、よく使用する型変換のパターンを紹介します。

Int -> Double

var a: Int = 100
Double(a) // 100

Int -> String

var a: Int = 100
String(a) // "100"

Double -> Int

var a: Double = 100.5
Int(a) // 100

Double -> String

var a: Double = 100.5
String(a)

String -> Int

var a: String = "100"
Int(a) // 100

String -> Double

var a: String = "100.5"
Double(a) // 100.5

出力

コンソールに出力する場合は print を使用します。

var a = "Hello"
print(a) // "Hello"

文字列の中で出力する場合は \(変数) を使用します。

var a = "Hello"
print("aの値は\(a)です") // "aの値はHelloです"

Optional(オプショナル)型

Swift では変数に nil が入るかどうかを宣言の時に指定します。nil を許可する場合はデータ型の後に ? を付けます。

var a: String?

optional ではない変数に nil が入るとエラーになります。

var a: String
a = nil // エラー

注意点として、var a: String? の 変数a は String ではなく、「optional な String」です。実際に変数を出力してみると、

var a: String?
a = "Hello"
print(a) // Optional("Hello")

print(a + "!") // エラー

となり、Optional つきで出力されています。 optional の変数を元の方と同じように扱うことはできないので、変数a を文字列として他の文字列と結合させようとすると、上記のようにエラーになります。

ただし、optional の変数の後に ! をつけると、元の型として扱うことができます。

var a: String?
a = "Hello"
print(a!) // "Hello"

print(a! + "!") // Hello!

ただし、a が nil の場合に ! をつけるとエラーになるので注意が必要です。必ず値が入っているときのみ使用するようにしましょう。

Optional Binding

値が入っているか(=nilじゃないか)どうかを確かめるには、if 文の条件式で値を代入するやり方があり、これを Optional Binding(オプショナルバインディング)といいます。

var a: String?
a = "Hello"
if let a = a {
  print(a) // "Hello"
}

if 文の中では optional ではない変数として使用できます。a が nil の場合は if 文の中は通りません。ちなみに let a = a は a に a を代入していて、代入された a は if 文の中でのみ使用できます。if 文の外では let a の a は参照できないので、また optional 型になります。変数名は何でもよく、let b = a とすると、if 文の中で b の変数が使用できます。さらに let は var でもよく、var にすると if 文の中で b に値を再代入することができます。

Optional Chaining

optional なクラスのプロパティやメソッドを呼び出すことを Optional Chaining(オプショナルチェーン)といい、? もしくは ! をつける必要があります。値が確実に入っている場合は !、それ以外は ? をつけるようにしましょう。

var a: String?
a.characters // エラー

a?.characters.count // nil
a!.characters.count // エラー

a = "Hello"

a?.characters.count // Optional(5)
a!.characters.count // 5

上記のように、optional のままで呼び出したら結果値も optional になります。

キャスト

値のデータ型をたしかめる時に使用します。特定の optional 型にする場合は as?、 optional ではない型にする場合は as! を使用します。

var a: Any?
a = "Hello"

// as? の後に指定したデータ型が格納されていたら Optional なデータ型、
// 格納されていなければ nil が入る
var b = a as? String // "Optional("Hello")
var c = a as? Int // nil

// as! の後に指定したデータ型が格納されていたらそのデータ型、
// 格納されていなければエラーになる
var b = a as! String // "Hello"
var c = a as! Int // エラー

配列(Array)

初期化

以下4パターンあります

var a: [String] = []
var b: Array<String> = []
var c = [String]()
var d = Array<String>()

var a = [] // データ型なしの空宣言はエラー

宣言と同時に値を入れる場合

// データ型なし
var a = ["a"]

// データ型あり
var a: [String] = ["a"]

// 範囲指定
var a = Array(1...10)
letで宣言すると要素の追加、削除、値の変更ができません

要素の追加

var b: [String] = []

// append メソッドで
b.append("a")

// + 演算子で
b += ["a"]

// 挿入箇所を指定する場合は insert
b.insert("a", at: 0)

// 配列同士の結合
var c = ["a"] + ["b"]

要素の削除

var a = ["a", "b"]

// 場所を指定して削除
a.remove(at: 1)

// 最後の要素を削除
a.removeLast()

// 要素を全部削除
a.removeAll()

その他

isEmpty
要素があれば true、なければ false を返します。

var a: [String] = ["a", "b", "c"]
a.isEmpty // false

count
要素の数を返します。

var a: [String] = ["a", "b", "c"]
a.count // 3

sort/sorted
要素を並び替えます。

var a: [Int] = [2, 3, 1]
a.sort() // 昇順 [1, 2, 3]
a.sorted() // 昇順. a 自体を変更するのではなく、並び替え結果を返り値で返すだけ
a.sort(by: >) // 降順 [3, 2, 1]
a.sorted(by: >) // 降順. a 自体を変更するのではなく、並び替え結果を返り値で返すだけ

contains
要素があれば true、なければ false を返します。

var a: [String] = ["a", "b", "c"]
a.contains("b") // true

filter
条件に一致する要素を返します。

var a: [Int] = [1, 2, 3, 4, 5]
let result = a.filter { $0 % 2 == 0} // 2の倍数
print(result) // [2, 4]

map
全ての要素に特定の処理を加えます。

var a: [Int] = [1, 2, 3, 4, 5]
let result = a.map { $0 * 2} // 2倍にする
print(result) // [2, 4, 6, 8, 10]

// データ型付き
a.map { (a: Int) -> Int in a * 2 }

flatMap
ネストされた配列を1重にしたり、nil を除外します。

let a = ["a", "b", nil].flatMap{ $0 }
print(a) ["a", "b"]

let a = [["a"], ["b"], ["c", "d"]].flatMap{ $0 }
print(a) // ["a", "b", "c", "d"]

reduce
要素をループし、ループごとの処理結果を次のループに渡しながら処理します。reduce の引数に初期値を設定します。

var a: [Int] = [1, 2, 3, 4, 5]
// 最初のループ時の $0 は「0」,$1 は要素の「1」
// 次のループ時の $0 は前のループの結果(0+1)の「1」,$1 は要素の「2」
let result = a.reduce(0) { $0 + $1 } // すべての要素を足し算した結果を求める
print(result) // 15

// データ型付き
a.reduce(0) { (a: Int, b: Int) -> Int in a + b }

Set

複数の値を格納しますが、配列と違い同じ値の重複を許可しません。また、格納される値は順番通りにインデックスに格納されるわけではなく、不順になります。

初期化

以下2パターンあります。

var a: Set = []
var b = Set()

宣言と同時に値を入れる場合

// データ型なし
var a: Set = ["a", "b"]
var b = Set(["a", "b"])

// データ型あり
var a: Set = ["a", "b"]
var b = Set(["a", "b"])
letで宣言すると要素の追加、削除ができません

要素の追加

var a: Set = []
a.insert("a")

要素の削除

var a: Set = ["a", "b"]
a.remove("a")

// 最初の要素を削除. 配列と違い removeLast()はありません
a.removeFirst()

// 要素を全部削除
a.removeAll()

その他

isEmpty
要素があれば true、なければ false を返します。

var a: Set = ["a", "b"]
a.isEmpty // false

count
要素の数を返します。

var a: Set = ["a", "b"]
a.count // 2

sort/sorted
要素を並び替えます。

var a: Set = [2, 3, 1]
a.sorted() // 昇順. a 自体を変更するのではなく、並び替え結果を返り値で返すだけ
a.sorted(by: >) // 降順. a 自体を変更するのではなく、並び替え結果を返り値で返すだけ

contains
要素があれば true、なければ false を返します。

var a: Set = ["a", "b", "c"]
a.contains("b") // true

filter
条件に一致する値を新しいSetで返します。

let a: Set = [1, 2, 3, 4, 5]
let b = a.filter { $0 % 2 == 0 } // 2の倍数
print(b) // { 2, 4 }

辞書(Dictionary)

key/value の形でデータを保存します。

初期化

以下2パターンあります。

var a: [String: Int] = [:]
var b = [String: Int]()

var c = [:] // データ型なしの空宣言はエラー

宣言と同時に値を入れる場合

// データ型なし
var a = ["a": 100, "b": 200]

// データ型あり
var b: [String: Int] = ["a": 100, "b": 200] 

キーの追加

var a: [String: Int] = [:]
a["a"] = 100

キーの削除

var a: [String: Int] = ["a": 100, "b": 200]

// 特定のキーを削除
a.removeValue(forKey: "a")

// すべてのキーを削除
a.removeAll()

その他

isEmpty
要素があれば true、なければ false を返します。

let a: [String: Int] = ["a": 100, "b": 200]
a.isEmpty // false

count
キーの数を返します。

let a: [String: Int] = ["a": 100, "b": 200]
a.count // 2

keys.sorted / values.sorted
キーまたは値を並び替えた結果を配列で返します。

let a = ["c": 300, "a": 100, "b": 200]
// 昇順
a.keys.sorted() // ["a", "b", "c"]
a.values.sorted() // [100, 200, 300]

// 降順
a.keys.sorted(by: >) // ["c", "b", "a"]
a.values.sorted(by: >) // [300, 200, 100]

keys.contains / values.contains
指定するキーまたは値があれば true、なければ false を返します。

let a = ["a": 100, "b": 200]
a.keys.contains("b") // true
a.values.contains(100) // true

filter
条件に一致する key/ value を返します。

let a = ["a": 100, "b": 200]
let result = a.filter { $0.value > 100 } // 値が100以上
print(result) // ["b": 200]

mapValues
値だけを map させて何かしらの処理を加え、キーはそのままの状態で新しい Dictionary を返します。

let a = ["a": 100, "b": 200]
let b = a.mapValues { $0 * 2 }
print(b) // ["a": 200, "b": 400]

default
指定したキーがなかった場合の値を指定できます。

let a = ["a": 100]
let b = a["b", default: 200]
print(b) // 200

merge
Dictionary 同士を結合します。

var a = ["a": 100]
var b = ["b": 200]
a.merge(b) { (current, _) in current }
print(a) // ["a": 100, "b": 200]

merge のクロージャには、同じキーがあった場合にどちらを採用するかをクロージャーの返り値で指定します。第1引数は自身の値で、第2引数は結合対象(ここではb)の値です。ここでは自身の値(current)をそのまま返しているので、a の値が採用されます。その他のパターンも紹介しておきます。

var a = ["a": 100]
var b = ["a": 300, "b": 200]

// b の値を採用する場合
a.merge(b) { (_, new) in new }
print(a) // ["a": 300, "b": 200]

// 値の小さい方を採用する場合
a.merge(b) { (current, new) in current < new ? current : new }
print(a) // ["a": 100, "b": 200]
_(アンダーバー) は、変数を参照しない場合のエイリアスです。

init(grouping:by:)

配列を使用して Dictonary を初期化します。配列を一定のルールでグルーピングします。

let a = ["a", "abc", "bcd"]

// 最初の文字でグルーピング
let b = Dictionary(grouping: a, by: { $0.first! })
print(b) // ["a": ["a", "abc"], "b": ["bcd"]]

// 文字数でグルーピング
let c = Dictionary(grouping: a, by: { $0.count })
print(c) // [1: ["a"], 3: ["abc", "bcd"]]

タプル(tuple)

1つの変数に複数の値を格納できます。配列のように値だけ、もしくは辞書の key/value
のように名前付きで格納することができます。ただし、値の追加、削除はできません。

初期化

// データ型なし
let a = ("a", 100)
a.0 // "a"
a.1 // 100

// データ型なし かつ 名前付き
let b = (name: "taro", age: 10)
b.name // "taro"
b.age // 10

// データ型あり
let c:(String, Int) = ("a", 100)
c.0 // "a"
c.1 // 100

// データ型あり かつ 名前付き
let d:(name: String, age: Int)
d.name = "taro"
d.age = 10

タプルをタプルに代入することで、複数の変数の値を一度に設定することもできます。

let (a, b) = ("a", 100)
print(a) // "a"
print(b) // 100

?演算子

三項演算子

条件式 ? a else b の形で使用し、条件式 が true の場合は a、false の場合は b を返します。

let a = ""
a == "" ? "空です" : "空以外" // "空です"

論理演算子

a ?? b の形で使用すると、 a が nil の場合は b を返します。

var a: String?
var b = a ?? ""
print(b) // ""

条件分岐

if 文

基本形は以下の3パターンです。

if 条件式 {
  ...
}

if 条件式 {
  ...
} else {
  ...
}

if 条件式 {
  ...
} else if 条件式 {
  ...
} else {
  ...
}

範囲を使った数値判定

let a = 20
if case 0...100 = a {
  print("a は0以上100以下")
}

nil チェック

var a: String?
a = "Hello"
if let _ = a { 
  print("a は nil ではありません")
}

// 条件をつき
var a: String? = "test"
if let _ = a, a.characters.count > 3 { 
  print("a は3文字以上です")
}

guard 文

特定の条件を満たしていない時に、スコープを抜けるための処理(break、return など)を必ず行います。「条件を満たしていなかったらこの先は通しません!」という感じです。

guard 条件式 else {
  ...
  return
}

if 文と同じように nil チェックにも使えますが、 チェック時に代入した値を guard 文の外でもそのまま使用できる点が if 文と異なります。

var a: String?
a = "Hello"

guard let a = a else {
  ...
  return
}

print(a) // "Hello". optional 型になっていません

switch 文

switch-case で場合分けします。条件の数が多くなると、if 文よりこちらの方が楽に書けることもありますが、複雑な条件を設定する場合は逆に向きません。

var a = 100

switch a {
case 1:
    print("1")
// 複数指定
case 50, 75:
    print("50または75")
// 範囲指定
case 76...99:
    print("76〜99")
// 上記以外
default:
    print("その他")
}

ループ

for 文

ループ回数を指定する

// 3回ループする場合
for i in 0...2 {
  print(i)
}

for i in 0..<3 {
  print(i)
}

配列をループ

var a = [1, 2, 3]
for value in a {
  print(value) // 1, 2, 3
}

// インデックス付き
for (i, value) in a.enumerated() {
  print(i) // 0, 1, 2
  print(value) // 1, 2, 3
}

// nil 以外の要素をループ
var a: [String?] = ["1", nil, "2"]
for case let value? in a {
  print(value) // "1", "2"
}

文字列をループ

var a = "abc"
for char in a.characters {
  print(char) // "a", "b", "c"
}

辞書をループ

var a = ["a": 100, "b": 200, "c": 300]
for (key, value) in a {
  print(key) // "a", "b", "c"
  print(value) // 100, 200, 300
}

forEach 文

配列の要素・辞書をループします。

// 配列
var a = [1, 2, 3]
a.forEach { print($0) } // 1, 2, 3

// 辞書
var b = ["a": 100, "b": 200]
b.forEach({ (key: String, value: Any) in
  print(key) // "a", "b"
  print(value) // 100, 200
})

while 文

指定した条件を満たさなくなるまでループします。

var a = 0
while a < 2 {
  print(a) // 0, 1
  a += 1
}

break

ループを途中で中断します。

for i in 0...2 {
  print(i) // 0, 1
  if i == 1 {
    break
  }
}

continue

以降の処理を中断し、次のループから始めます。

for i in 0...2 {
  if i == 1 {
    continue
  }
  print(i) // 0, 2
}

ラベル

ループがネストしている場合、ラベルをつけていると、その場所まで break/continue できます。ラベルは for や while 文の横にラベル名: という形式でつけます。

a:for i in 0...3 {
  b:for j in 0...3 {
    if (j == 1) {
       continue a // b を抜けて a まで戻る
       // または
       // break a // b も a も抜ける
    }
  }
}

例外処理

do-catch で例外をキャッチします。

do {
  ...
} catch {
  // 例外が発生した時に通る
}

例外オブジェクトを catch ブロックで使用する場合


do {
  ...
} catch let error {
    print(error)
}

例外クラスを指定する場合
ErrorTypeに準拠した独自のエラー(Enum)を作成し、それをキャッチすることができます。

enum CustomError: Error {
    case Error1
    case Error2
}

do {
    throw CustomError.Error1
} catch CustomError.Error1 {
  ...
} catch CustomError.Error2 {
  ...
} catch {
  // それ以外
}

throw で意図的に例外を発生させることができます。

throws

メソッドに throws をつけることで、そのメソッド内で例外が発生するかもしれない、ということを宣言することができます。そして、その例外は呼び出し元で処理をする必要があるので、throws 宣言されたメソッドを呼び出す時は、do-catch で囲み、さらに try をつける決まりになっています。

func test() throws {
  ...
}

do {
  try test()
} catch {
  ...
}

try? / try!

throws 宣言されたメソッドを呼び出してもし例外が発生しても、エラーハンドリングをしない場合に使用します。この場合 do-catch で囲む必要はありません。

// 例外が発生しても処理を続ける
try? test()

// 例外が発生したらクラッシュしてアプリを落とす
try! test()