コンテンツにスキップ

プログラミング言語Swift学習ノート(随時更新中)

2026/05/19
(2026/05/20 更新)

macOS/iOSのプログラミング言語Swiftでよく使われる構文を素早く覚えるため、クイックリファレンス的なものをまとめようと書き始めましたが、Swiftならではの記法が多くまとまらなかったので、学習したことを記録するノートにしました。学んだことが増えるたびに記事が増える予定です。

サンプルコードは、XcodePlayground、またはターミナルswift replコマンドを実行して、対話形式で試すことができます。async / awaitを使った並行処理など、Playgroundで正常に動作しない場合は、ファイルに保存してswift <ソースコード名>で実行します。

本記事を作成したときのSwiftのバージョン:

Terminal window
~$ swift --version
swift-driver version: 1.127.14.1 Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Target: arm64-apple-macosx15.0

Swiftに関する情報:

SwiftUIで難儀した話:

文の末尾に;(セミコロン)は不要です。1行に複数のステートメント(文)を書くときは;で結びます。ファイルの拡張子はswiftです。

hello.swift
print("Hello, world!")
print(2 + 3); print(10 - 4)

ソースコードをファイルに保存した場合、ターミナルでswiftコマンドを使って実行できます。

Terminal window
swift hello.swift

Hello, world!56が表示されます。

コメントは、1行コメントと複数行コメントがあります。

// 1行コメント
/*
複数行コメント①の始まり
/*
複数行コメント②
コメントをネストできるのは便利ですね。
*/
複数行コメント①の終わり
*/

Swiftは型推論が強力な静的型付け言語です。


  • var: 変数(変更可能)。
  • let: 定数(変更不可)。コードに格納された値が変化しない場合は、let キーワードで定数として宣言する。
var myVariable = 42 // 42 → Int型と推論される
myVariable = 50 // 変数は変更可能
var x = 0.0, y = 0.0, z = 0.0 // 1行の中に複数の変数(var)・定数(let)を定義できる
let myConstant = 42
myConstant = 50 // エラー: 定数は変更不可
let env = "dev"
let maxNum: Int // 後から定数の初期値を代入できる(構造体などの格納プロパティで頻出)
if env == "dev" {
maxNum = 100
} else {
maxNum = 10
}
// 型注釈(Type Annotation)の書き方
let explicitDouble: Double = 70.0 // `70.`はエラー(`.`はドット構文と見なされる)

変数・定数の名前の規則は以下のとおり。変数・定数の名前に限らず、クラス・構造体・列挙型の名前、タイプエイリアスの名前、whileループなど制御フローのラベルなどにも以下の規則が適用される。

  • Unicode文字も含めた、ほとんどの文字を使用可能。以下は不可な文字。
    • ホワイトスペース、数学記号、矢印、公式ではない Unicode スカラ値、罫線素片、書式文字
  • 数字から始めることは出来ない。
  • Swiftの予約語を識別子として使用することもできる。予約語の前後にバッククォートを置く。
let π = 3.14159
let こんにちは = "こんにちは、世界"
let 😊 = "笑顔"
let `let` = "定数宣言" // 予約語を定数名として使う
let 123abc = 123 // エラー

Swiftには値型参照型があります。クラス、関数(メソッドやクロージャも含む)、アクター以外は値型です。

flowchart LR
  dataType[データ型]
  valueType((値型))
  referenceType((参照型))
  
  struct{{構造体}}
  enum{{列挙型}}
  tuple{{タプル}}
  
  class_{{クラス}}
  function{{関数}}
  actor{{アクター}}

  integer([整数 Int])
  floatingPoint([浮動小数点数 Double/Float])
  boolean([ブール値 Bool])
  string([文字列 String])
  array([配列 Array])
  set([セット Set])
  dictionary([辞書 Dictionary])
  
  optional([オプショナル Optional])

  dataType --> valueType
  dataType --> referenceType

  subgraph ValueTypes [値がコピーされる]
    direction TB
    valueType --> struct
    valueType --> enum
    valueType --> tuple
  end

  subgraph ReferenceTypes [参照が渡される]
    direction TB
    referenceType --> class_
    referenceType --> function
    referenceType --> actor
  end

  struct --> integer
  struct --> floatingPoint
  struct --> boolean
  struct --> string
  struct --> array
  struct --> set
  struct --> dictionary

  enum --> optional

データ型の種類データ型
整数(符号付き)Int, Int32, Int8, Int16, Int64, Int128
整数(符号なし)UInt, UInt32, UInt8, UInt16, UInt64, UInt128
浮動小数点数Double(Float64でも可), Float(Float32でも可), Float16
ブール値Bool
文字列String (1文字のCharacter型もある)

IntUIntはプラットフォーム依存です。

データ型32bitプラットフォーム64bitプラットフォーム
IntInt32と等しいInt64と等しい
UIntUInt32と等しいUInt64と等しい
  • Int: 17, 0b10001, 0o21, 0x11, 1_000_000
  • Double: 倍精度浮動小数点数。例: 12.1875, 1.21875e1, 0xC.3p0, 1_000.000_1
  • Float: 単精度浮動小数点数。Float(3.14)など明示が必要。
  • Bool: true または false
  • String: 文字列。ダブルクォーテーション(")で括る。例: "Some string literal value"
    • 複数行リテラルは3つのダブルクォーテーション(""")で括る。行末の\は改行しない。
    • Character型(1文字のリテラル)もダブルクォーテーション(")で括る。
let singleLine = "1行の文字列"
let multipleLines = /* 次の行から文字列を書く */ """
この行は空白で始まりません。
この行は4文字の空白で始まります。\
この行は前の行の後ろに続きます。
Unicode文字の例: \u{1f496}
""" // 終了クォーテーションマーク前の空白がインデントと見なされる
let singleLineExt1 = #"拡張区切り文字を使うと特殊文字("や\nなど)はただの文字になります。"#
let singleLineExt2 = #"ただし、\記号の直後に#を書くと\#n特殊文字のままになります。"#
let singleLineExt3 = ###"#記号は\###n2つ以上書くことができます。"###
let multipleLinesExt = #"""
特殊文字("や\nなど)は
ただの文字になります。
"""#
let cat: Character = "🐱" // 1文字のリテラル
let dogs: Character = "🐕🐕‍🦺" // エラー

型名に?が付いているものはオプショナルのデータ型です。値がない状態(nil)を持ちます。


既存の型名に別の名前を定義できます。

typealias Byte = UInt8
var buffer: [Byte] = [Byte.min, Byte.max] // [0x00, 0xff]と同じ

暗黙の型変換はありません。

let a = 3 // Int型
let b = 0.14 // Double型: `.14`という書き方はエラーになる
// let c = a + b // エラー: Int + Double は不可
let c = Double(a) + b // OK: `a`からDouble型の値を作る

文字列は配列と同じような処理ができます(CollectionプロトコルやRangeReplaceableCollectionプロトコル)が、サブスクリプト構文ではString.Index型を渡す必要があるため、hello[2]のような書き方ができません。

var hello = "こんばんは"
hello += "、世界" // "こんばんは、世界"
var index = hello.index(hello.startIndex, offsetBy: 2)
assert(hello[index] == "")
// 全ての文字のインデックスを取得しても、上とあまり変わらない
var indices = hello.indices
index = indices.index(indices.startIndex, offsetBy: 2)
assert(hello[index] == "")
// 何度も文字単位でアクセスするような処理では、Character型の配列にしてしまうほうが楽なのでは?
var chars: [Character] = Array(hello)
assert(chars[2] == "")
assert(chars[6] == "")
// insertやremove、範囲指定演算子なども使えるが、String.Index型で指定する
var begin = hello.index(hello.startIndex, offsetBy: 2)
var end = hello.index(after: begin)
hello.replaceSubrange(begin...end, with: "にち")
index = hello.index(hello.startIndex, offsetBy: 4)
let hello2: Substring = hello[hello.startIndex...index] // `Substring`型
assert(hello2 == "こんにちは")

SwiftのString型は、内部ではUnicodeスカラから構築されています。Unicodeスカラは21bitの文字と修飾子で構成されます。

// Unicode表現
let utf8 = Array<UInt8>(hello.utf8)
let utf16 = Array<UInt16>(hello.utf16)
let unicodeScalars = Array<Unicode.Scalar>(hello.unicodeScalars)

String(format:_:)で指定できるフォーマットの詳細(%@, %d, %x, %%など)は、String Format Specifiersを参照してください。%の直後にn$を書いて引数の位置を指定できます。

let dec: Int? = Int("123") // 10進数→Int
let hex: Int? = Int("7f", radix: 16) // 16進数→Int
let fp: Double? = Double("123.456") // 10進数→Double
let decStr = String(dec!) // Int→10進数
let hexStr = String(hex!, radix: 16) // Int→16進数
import Foundation // これをインポートしないとString(format:_:)が使えない💢
let fpStr = String(format: "%.5f", fp!)
let hexStr2 = String(format: "%08x", hex!)
let formattedMessage1 = String(format: "%@ が %d 個あります。", "🍎", 3)
let formattedMessage2 = String(format: "%2$d 個の %1$@ があります。", "🍎", 3)

Swiftのコレクション(配列, 辞書, セット)はすべてジェネリクスで実装された構造体(値型)です。一方、タプルはコレクションではない点がPythonのタプルと異なります。

コレクションとタプルの可変性は、var宣言 (変数、ミュータブル) / let宣言 (定数、イミュータブル) で決まります。


糖衣構文(シンタックスシュガー)を使って、Array<T>, Dictionary<T1, T2>はそれぞれ[T], [T1: T2]と書けます。 Set<T>のシンタックスシュガーはありません。

var someInts: [Int] = [] // 空の配列の作成
// var someInts = [Int]() // 同上
// count: 配列のアイテムの数
print("someIntsは\(someInts.count)個の要素を持ちます。")
// isEmpty: countプロパティが0か否か
// 三項演算子の`?`の前に空白を入れないとオプショナルチェーンに見なされてエラーになる
print(
someInts.isEmpty ? "someIntsは空です" : "someIntsは空ではありません"
)
// repeatingはデフォルトの要素の値、countは繰り返し回数
var threeInts = Array(repeating: 0, count: 3)
// 要素の追加
var fruits = ["Apple", "Banana"] // [String]型の配列
fruits.append("Orange")
fruits += ["Pineapple", "Mango"]
fruits.append(contentsOf: ["Grape", "Melon"])
// サブスクリプト構文
fruits[0] = "🍎" // JS風の`fruits[fruits.count] = "🍒"`はインデックス範囲外(実行時エラー)
print("2番目から4番目の果物は\(fruits[1...3])です。") // インデックスの範囲指定
fruits[1...3] = ["🍌", "🍊", "🍍", "🍑", "🍋"]
print(fruits)
// ["🍎", "🍌", "🍊", "🍍", "🍑", "🍋", "Mango", "Grape", "Melon"]
// 要素の挿入・削除
fruits.insert("🍓", at: 1)
let removedFruit = fruits.remove(at: 3) // 🍊
let removedFruit2 = fruits.removeLast() // Melon
assert(fruits.contains("Melon") == false)
print(fruits)
// ["🍎", "🍓", "🍌", "🍍", "🍑", "🍋", "Mango", "Grape"]
// 定数
let animals = ["Dog", "Cat"]
animals.append("Bird") // エラー: letなので変更不可(イミュータブル)

多次元配列は配列の要素に配列を入れて作ります。

let matrix: [[Int]] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
assert(matrix[1][2] == 6)
var namesOfNumbers: [Int: String] = [:] // 空の辞書の作成
// var namesOfNumbers = [Int: String]() // 同上
var airports = ["HND": "羽田空港", "KMQ": "小松空港"] // [String: String]型の辞書
// count: 辞書のアイテムの数
print("airportsは\(airports.count)個の要素を持ちます。")
// isEmpty: countプロパティが0か否か
print(
airports.isEmpty ? "airportsは空(から)です" : "airportsは空(から)ではありません"
)
// キーと値の追加・変更・削除
namesOfNumbers.updateValue("十五", forKey: 15)
let previousValue = namesOfNumbers.updateValue("fifteen", forKey: 15) // 十五
let removedValue = namesOfNumbers.removeValue(forKey: 15) // fifteen
// サブスクリプト構文
airports["NRT"] = "成田空港"
if let airportName = airports["ABC"] { // 値はオプショナル値のため、オプショナルバインディングで値を取得する
print("空港名は\(airportName)です。")
} else {
print("該当する空港名はありません。")
}
airports["KMQ"] = nil // nilが格納されるのではなく、削除される (PythonやJSと異なる動作)
print(airports)
// ["NRT": "成田空港", "HND": "羽田空港"]
var letters = Set<Character>() // 空のセットの作成
// 型注釈を書かないとArray<String>になるので注意
var genres: Set<String> = ["ロック", "クラシック", "J-POP"]
// count: セットのアイテムの数
print("genresは\(genres.count)個の要素を持ちます。")
// isEmpty: countプロパティが0か否か
print(
genres.isEmpty ? "genresは空です" : "genresは空ではありません"
)
// 要素の挿入・削除
genres.insert("ジャズ")
let removedGenre = genres.remove("クラシック") // クラシック
let removedGenre2 = genres.remove("童謡") // nil
print(genres.contains("ロック") ? "含まれます" : "含まれません")
// 集合の演算、letを使っているので以下は全て不変
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
let 和集合 = oddDigits.union(evenDigits)
let 積集合 = oddDigits.intersection(evenDigits)
let 差集合 = oddDigits.subtracting(singleDigitPrimeNumbers)
let 対称差 = oddDigits.symmetricDifference(singleDigitPrimeNumbers) // 排他的論理和の集合
// セットの要素と等価性
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
print(houseAnimals.isSubset(of: farmAnimals) ? "farmAnimalsに含まれる" : "farmAnimalsに含まれない")
print(farmAnimals.isSuperset(of: houseAnimals) ? "houseAnimalsを含む" : "houseAnimalsを含まない")
print(farmAnimals.isDisjoint(with: cityAnimals) ? "共通要素が無い" : "共通要素がある")

Swiftのタプルは 名前のない軽量な構造体 のようなもので、異なる型を入れることができます。Pythonのタプルと異なり、サブスクリプト構文やループ処理を使ってタプルの各要素にアクセスすることはできません。

// 定義
let httpError = (404, "Not Found") // (Int, String) 型のタプル
// 左辺でタプルを展開できる
let (code, message) = httpError
let (justCode, _) = httpError // 不要な値は `_` で捨てられる
// インデックスアクセス
let statusCode = httpError.0
let statusMessage = httpError.1
// 名前付きタプル
var user = (id: 1, name: "Alice")
user.name = "Smith" // 変数の場合、タプルの値を変更できる
assert(user == (1, "Smith"))
// タプルの比較: 左から順に値を比較
assert((1, "zebra") < (2, "apple"))
assert((3, "apple") < (3, "bird"))
assert((4, "dog") == (4, "dog"))
assert(("blue", false) < ("purple", true)) // エラー: Boolは`<`で比較できない
// 蛇足: Pythonは以下の右辺式でタプルになるが、Swiftではタプルにはならない
let value = (1, )
assert(type(of: value) == Int.self) // valueはInt型

CやC#などのように、列挙ケース(列挙値)に暗黙の整数(0, 1, 2, ...)は割り当てられません。列挙ケースは、型推論によって型名を省略できます。

enum CompassPoint {
case north
case south
case east, west // カンマ区切りで列挙ケースを複数定義できる
}
let direction: CompassPoint = .north // 型推論により型名は省略できる
// `switch`を式として使うこともできる
let japaneseCompassPoint = switch direction {
case .north:
""
case .south:
""
case .east:
""
case .west:
"西"
}
print(japaneseCompassPoint)
// `CaseIterable`を継承すると`allCases`プロパティが使える
enum Beverage: CaseIterable {
case coffee, tea, juice
}
for i in Beverage.allCases { // Array<Beverage>型のコレクション
print(i)
}

構造体のように、列挙型にもメソッドなどを定義できます。詳細はオブジェクト指向の章を参照。

enum CompassPoint {
case north, south, east, west
// `self`を変更するメソッドは`mutating`で修飾する(self.propertyNameへの変更も含まれる)
mutating func turn(_ direction: CompassPoint) {
self = direction
}
}
var direction = CompassPoint.south
direction.turn(.north)
print("方角: \(direction)")

列挙ケースに関連値と呼ばれる追加情報を与えることができます。関連値は、caseのパターンマッチを使って抽出できます。

enum Barcode {
case upc(Int, Int, Int, Int) // UPC形式の1次元バーコード (関連値は4つの整数を持つタプル)
case qrCode(String) // QRコード形式の2次元バーコード (関連値は文字列)
}
var productBarcodes = [
Barcode.upc(8, 85909, 51226, 3),
Barcode.qrCode("ABCDEFGHIJKLMNOP")
]
for barcode in productBarcodes {
switch barcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check)")
case let .qrCode(productCode):
print("QR code: \(productCode)")
}
}
// 再帰的列挙型 (indirect)
// 以下の例では`case`ごとに`indirect`を書いているが、
// `enum`の前に`indirect`と書くこともできる(`case`ごとの`indirect`は不要になる)
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression) // 自身の列挙型を関連値として持つ
indirect case multiplication(ArithmeticExpression, ArithmeticExpression) // 同上
}
let three = ArithmeticExpression.number(3)
let seven = ArithmeticExpression.number(7)
let five = ArithmeticExpression.number(5)
let sum = ArithmeticExpression.addition(three, seven)
let product = ArithmeticExpression.multiplication(sum, five)
func eval(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value): // numberの関連値(Int型)を抽出している
return value
case let .addition(left, right): // additionの関連値を抽出している
// valueにマッチするまでeval関数を再帰的に呼び出す
return eval(left) + eval(right)
case let .multiplication(left, right): // multiplicationの関連値を抽出している
// valueにマッチするまでeval関数を再帰的に呼び出す
return eval(left) * eval(right)
}
}
assert(eval(product) == 50)

列挙ケースに、全て同じ型の値(Raw Value)を事前に定義できます。Raw Valueは、文字列・文字・整数型・浮動小数点数型のいずれかの型になります。

// enum宣言の後ろに`:`+`型名`を付ける
enum Planet: Int, CaseIterable {
case mercury, venus, mars = 3, jupiter, saturn, uranus, neptune
}
print(Planet.allCases.map {$0.rawValue}) // [0, 1, 3, 4, 5, 6, 7]
let earth = Planet(rawValue: 2) // rawValueから列挙型の値を得ることもできる
assert(earth == nil) // nilを返す場合もある(Planet?型)
enum CompassPoint: String {
case north, south, east, west
}
assert(CompassPoint.north.rawValue == "north")
assert(CompassPoint.south.rawValue == "south")
assert(CompassPoint.east.rawValue == "east")
assert(CompassPoint.west.rawValue == "west")
// 整数や文字列のRaw ValueはSwiftが自動的に値を割り当てるが、
// 浮動小数点数型や文字はRaw Valueを明示する必要がある
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}

条件式の括弧 ( ) は不要で、ブロックの中括弧 { } は必要です。


switchifは式としても使えます。

暗黙でフォールスルー(次のcaseに通り抜けること)をしないので、末尾のbreakは不要です。途中でbreakは可能。

caseはパターンマッチで値を比較します。最初に一致したパターンのcaseを実行します。カンマ区切りで複合ケースを書くと、その中のいずれか1つにマッチすることを意味します。

caseによるパターンマッチはiffor-inでも使えます。

let score = 75
switch score {
case 0:
print("Zero")
case 1..<50: // Range (1から49)
print("Keep trying")
case 50...100: // Range (50から100)
print("Good")
case ..<0:
fallthrough // フォールスルー
default:
print("Invalid")
}
let letter = "A"
let result = switch letter {
case "a", "A": // 複合ケース("a"または"A"にマッチ)
"Aです"
default:
"A以外の文字です"
}
assert(result == "Aです")

タプルでは、_はワイルドカードとして任意の値にマッチし、letで値を定数にバインディングします(値バインディング)。列挙型では、マッチした列挙ケースの関連値をバインディングできます。

whereで条件式をパターンに追加できます。

let point = (x: -2, y: -2)
switch point {
case (0, 0):
print("原点")
case (_, 0): // `_`は任意の値にマッチする
print("X軸上の点")
case (0, let y):
print("Y軸上のy=\(y)の位置の点")
case (-1...1, -1...1):
print("-1以上1以下の範囲内の点")
case let (x, y) where x > 0 && y > 0:
print("第1象限にある点")
case let (x, y) where x < 0 && y < 0:
print("第3象限にある点")
default:
break // case・default内のコードは空に出来ないのでbreakを書く
}
let score = 75
if score >= 80 {
print("A判定")
} else if score >= 60 {
print("B判定")
} else {
print("C判定")
}
// 式としても使える
let result = if score >= 80 {
"A判定" // ブロック内は単一式
} else if score >= 60 {
"B判定"
} else {
nil as String?
}
// パターンを使う
let point = (x: 10, y: 20)
if case (10, let y) = point { // `==`ではない
print("y=\(y)")
}

条件を満たさない場合は早期リターンするための構文です。

func greet(person: [String: String]) {
guard let name = person["name"] else {
return // nameがない場合はここで終了
}
print("Hello \(name)!") // 以降、nameが使える
}
// パターンを使う
func test(_ point: (x: Int, y: Int)) {
guard case (11, let y) = point else {
print("x=11ではありません。")
return
}
print("x=11, y=\(y)")
}
test((10, 20)) // x=11ではありません。
test((11, 22)) // x=11, y=22

配列のループ

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Pythonのenumerate相当
for (index, name) in names.enumerated() {
print("No. \(index): \(name)")
}
// パターンを使う(`where`の位置は`in`の後ろ)
for case let name in names where name.count < 5 {
print(name)
}

セットのループ

let nameSet = Set(names)
for (index, name) in nameSet.sorted().enumerated() {
print("No. \(index): \(name)")
}

辞書のループ

var airports = ["HND": "羽田空港", "KMQ": "小松空港"]
// キーと値のペアでループ
for (code, name) in airports {
print("\(code): \(name)")
}
// キーだけでループ
for code in airports.keys {
print("\(code): \(airports[code]!)") // オプショナルの強制アンラップが必要
}

文字列から1文字ずつ取り出す

for (index, char) in "こんにちは".enumerated() {
print("\(index + 1)文字目: \(char)")
}

範囲ループ

for i in 0..<5 { // 0, 1, 2, 3, 4
print(i)
}
// Pythonのrange(start, stop, step)相当
for i in stride(from: 0, to: 60, by: 5) { // 0, 5, 10, ..., 55
print(i)
}
// throughを使うと、それ自身の値も含む
for i in stride(from: 0, through: 60, by: 5) { // 0, 5, 10, ..., 60
print(i)
}
// strideは浮動小数点数にも使える
for i in stride(from: 0.1, through: 1.0, by: 0.1) { // 0.1, 0.2, ..., 1.0
print(i)
}

whileループ

var n = 1
while n < 100 {
n *= 2
}
// Cなどのdo-whileループに相当
repeat {
n /= 2
} while n > 1

ループ構文やswitchでは、breakcontinueにラベルが使えます。

input.swift
mainLoop: while true {
print("何か入力してください(endで終了): ", terminator: "")
let line: String? = readLine(strippingNewline: true)
if let line {
switch line {
case "end":
print("終了します")
break mainLoop
case let line where line.count == 0:
continue mainLoop
default:
print("入力値: \(line)")
}
} else {
print("強制終了します")
break // `break mainLoop`と同じ
}
}


関数の定義はfunc 名前(引数) -> 戻り値の型名です。 引数は引数ラベル パラメータ名: 型名の形式で記述します。

呼び出し側では、引数に引数ラベルを記述します。引数ラベルが_の場合、呼び出し側の引数ラベルは省略します。

// 定義: func 名前(引数ラベル パラメータ名: 型名) -> 戻り値の型名
func greet(person name: String, from city: String) -> String {
return "Hello \(name), Glad you could visit from \(city)."
}
greet(person: "Bob", from: "Sendai")
// 引数ラベル省略 (`_` を使う)
// 関数が単一式の場合、`return`を省略できる (getプロパティやクロージャも同じ)
func addSub(_ a: Int, _ b: Int) -> (Int, Int) {
(a + b, a - b)
}
assert(addSub(1, 2) == (3, -1))
// 引数ラベルを書かない場合は、パラメータ名が引数ラベルになる
func greet(person: String, dayOfWeek: String) { // 戻り値の`Void`は省略できる(Voidは空タプル)
print("こんにちは\(person)さん、今日は\(dayOfWeek)です。")
}
greet(person: "山田", dayOfWeek: "金曜日") // 引数ラベルが違えばオーバーロードできる
// デフォルトパラメータ値
func 消費税込価格(_ price: Int, tax: Double = 0.10) -> Int {
Int(Double(price) * (1.0 + tax))
}
assert(消費税込価格(1000) == 1100)
// 可変長パラメータ
func sum(_ numbers: Double...) -> Double {
var total = 0.0
for n in numbers {
total += n
}
return total
}
assert(sum(1, 2, 3.4) == 6.4)
// In-Outパラメータ
func swapVars(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x: Int = 100, y: Int = 200
swapVars(&x, &y) // In-Outパラメータに渡す変数の前には`&`を付ける
print("x=\(x), y=\(y)")

全ての関数には関数型があります。

var f: (inout Int, inout Int) -> Void = swapVars
f(&x, &y)
print("x=\(x), y=\(y)")
// 関数のパラメータの型として関数型を使う
func printNumber(_ number: Int) {
print("値は\(number)です。")
}
func loop(count: Int, _ callback: (Int) -> Void) {
for i in 1...count {
callback(i)
}
}
loop(count: 10, printNumber)
// 関数の戻り値として関数型を使う
func getOffsetFunction(_ offset: Int) -> (Int) -> Int {
func offsetFunction(_ number: Int) -> Int { // ネスト関数 (関数の中で関数を定義)
number + offset // offsetをキャプチャしている
}
return offsetFunction
}
let offset5 = getOffsetFunction(5)
assert(offset5(10) == 15)

他言語の匿名関数に相当します。 { (引数) -> 戻り値 in 処理 } の形式で記述しますが、型推論や省略引数名($0, $1, $2, ...)により簡略化できます。

let numbers = [1, 2, 3]
let doubled = numbers.map({ (number: Int) -> Int in
return number * 2
})
// 省略記法 (型推論で引数の型と`( )`と戻り値を省略、さらにreturnも省略)
let tripled = numbers.map({number in number * 3})
// 引数は$0, $1, $2, ...で省略でき、中身のない`()`も省略できる
let simpleDoubled = numbers.map({ $0 * 2 })
let descending = numbers.sorted(by: {$0 > $1})
// 参考: `>`は`(Self, Self) -> Bool`型の関数なので
// {$0 > $1}のようなクロージャを作らなくとも
// 直接`by`パラメータに渡すことができる
let shortDescending = numbers.sorted(by: >)
assert(descending == shortDescending)
// クロージャを戻り値として返す
func getOffsetFunction(_ offset: Int) -> (Int) -> Int {
return {(_ number: Int) -> Int in
// offsetはクロージャの外からキャプチャしている
number + offset
}
}
let offset5 = getOffsetFunction(5)
assert(offset5(10) == 15)

末尾クロージャは、関数引数の括弧( )の後ろに記述します。

// 末尾クロージャを持つ関数の定義
func doLoop(count n: Int = 5, body: (_ number: Int) -> Void) {
for i in 1...n {
body(i)
}
}
// 末尾クロージャとして記述する
doLoop(count: 3) { number in
print("\(number): Hello world")
}
// 空の`()`は省略できる
doLoop { number in
print("\(number): Hello world")
}
// 複数の末尾クロージャを持つ関数
func dumpList(_ list: [String], header: () -> String, body: (String) -> String, footer: () -> String) {
print("\(header())")
for i in list {print("\(body(i))")}
print("\(footer())")
}
dumpList(["A", "B", "C"]) { // 最初の末尾クロージャは引数ラベルを付けない
">>>>> 開始 >>>>>"
} body: { // 2つ目以降の末尾クロージャは引数ラベルを付ける
"値は\($0)です"
} footer: {
"<<<<< 終了 <<<<<"
}

関数の引数に渡されたクロージャが関数から抜けた後に呼びされるような使い方をするときは、@escapingを付けて関数をエスケープします。

var eventHandlers: Array<() -> Void> = [] // Arrayの部分は[() -> Void]とも書ける
// Playgroundでは以下のエラーが発生するため、`@MainActor`を付ける
// `.swift`ファイルに保存して実行するときは`@MainActor`を外すこと
// `main actor-isolated var 'eventHandlers' can not be referenced from a nonisolated context`
@MainActor
func registerEventHandler(eventHandler: @escaping () -> Void) {
// registerEventHandler関数を抜けた後、
// eventHandlerはeventHandlers経由で呼び出される
eventHandlers.append(eventHandler)
}
registerEventHandler { print("Hello world!") }
@MainActor
func invokeEventHandlers() {
for handler in eventHandlers { handler() }
}
invokeEventHandlers()

クラスの中でエスケープクロージャを使う場合、self.を明示的に書くか、クロージャのキャプチャリストselfを入れます。

@MainActor
class SampleCounter {
var value = 0
init() {
// selfがクラスの場合、
// エスケープクロージャには`self.`を明示的に書く
registerEventHandler { self.value += 1 }
// あるいはクロージャのキャプチャリストにselfを入れる
// registerEventHandler { [self] in value += 1 }
}
func print() {
Swift.print("値は\(value)です") // グローバル関数のprintを呼び出す
}
}
let sampleCounter = SampleCounter()
invokeEventHandlers()
sampleCounter.print()

エスケープクロージャはselfを変更可能(mutating)でキャプチャするので、構造体や列挙型ではあまり使えそうにありません。

@MainActor
struct SampleCounter {
var value = 0
init() {
registerEventHandler { Swift.print(value) } // エラー: escaping closure captures mutating 'self' parameter
registerEventHandler { Swift.print("init") } // OK
registerEventHandler(eventHandler: print) // OK
}
func print() {
Swift.print("値は\(value)です")
}
}
let sampleCounter = SampleCounter()
invokeEventHandlers()

自動クロージャは、関数の引数として渡された式をラップして自動的に作成されるクロージャです。

assert(condition:message:file:line:)conditionmessageは自動クロージャで引数を受け取ります。

func testCondition(_ condition: @autoclosure () -> Bool, then: () -> String, or: () -> String) ->String {
condition() ? then() : or()
}
for i in 0..<10 {
let result = testCondition(i < 5) {
"\(i)は5より小さい"
} or: {
"\(i)は5以上"
}
print(result)
}

asyncで非同期関数(非同期メソッドも同様)を定義して、呼び出し側ではawaitを書きます。

async.swift
func listPhotos(album name: String) async throws -> [String] {
let library = [
"風景": ["IMG101", "IMG102", "IMG103", ],
"人物": ["IMG201", "IMG202", "IMG203", ],
"乗り物": ["IMG301", "IMG302", "IMG303", ],
]
// DBなどから写真の一覧を非同期で取得する処理の代替
try await Task.sleep(for: .seconds(1.0))
if let photos = library[name] {
return photos
} else {
return [] // `[String]()`に型推論される
}
}
do {
let data = try await listPhotos(album: "人物")
print(data)
} catch {
print(error)
}
// 非同期関数を並行に実行する
do {
async let scenery = listPhotos(album: "風景")
async let people = listPhotos(album: "人物")
async let vehicle = listPhotos(album: "乗り物")
// 右辺式は単純に`try await [scenery, people, vehicle]`と書いてもよい
let data = try await [ "風景": scenery, "人物": people, "乗り物": vehicle ]
print(data)
} catch {
print(error)
}

不特定数の非同期関数を並行に実行するのであれば、withTaskGroupwithThrowingTaskGroupでタスクグループを使います。

async.swiftdo-catchの部分は、以下のように書き換えできます。

do {
let data = try await withThrowingTaskGroup(of: (String, [String]).self) { group in
for i in ["風景", "乗り物", "人物"] {
group.addTask {try await (i, listPhotos(album: i))}
}
var photos: [String: [String]] = [:]
for try await (i, p) in group {
photos[i] = p
}
return photos
}
print(data)
} catch {
print(error)
}

非同期関数を中断できるようにするには、Task.isCancelledでキャンセル状態を把握するか、Task.checkCancellation()でキャンセル済みのときはエラーをスローする処理を入れます。

TaskCancel.swift
import Foundation // fflush用
// 処理時間の長い非同期関数
func longTermWorker(minutes: Int) async throws -> String {
for i in 1...minutes * 60 {
try Task.checkCancellation()
print("...", terminator:""); fflush(stdout)
try? await Task.sleep(for: .seconds(1.0)) // キャンセル時のスローを抑止
print(Task.isCancelled ? "\(i)秒目で中断" : "\(i)秒経過")
}
return "longTermWorker completed"
}
do {
// 処理時間の長い非同期関数の実行
let task = Task {
let result = try await longTermWorker(minutes: 10)
return result
}
// 5.5秒後に中断する
try await Task.sleep(for: .seconds(5.5))
task.cancel()
// 処理時間の長い非同期関数の結果待ち
let result = await task.value
print("処理結果: \(result)")
} catch {
print("エラー: \(error)")
}

Swiftには構造体(struct)とクラス(class)があります。共に以下の特徴があります。

  • プロパティとメソッドを定義できる(インスタンス変数の概念は無い)
  • サブスクリプトを定義できる
  • イニシャライザで初期化できる
  • extensionで機能を拡張できる
  • プロトコルに準拠できる

一方で、以下の違いがあります。

特徴classstruct
代入参照が渡されるコピーが渡される
同値演算子===, !==が使える左記の演算子は使えない
継承別のクラスを継承できる継承できない
イニシャライザ未定義の場合デフォルトイニシャライザメンバワイズイニシャライザ
convenienceイニシャライザ定義できる定義できない
デイニシャライザ定義できる定義できない
型キャストできるできない

Swiftでは継承が必要ない限り、struct(値型) をデフォルトで使います。

以下ではstructclassを中心にまとめていますが、列挙型のenumにも以下の機能が備わっています。

  • 計算プロパティ(格納プロパティは無い)
  • 型プロパティ(格納/計算の両方で使える)
  • メソッド
  • イニシャライザ
  • サブスクリプト

varまたはletを使って格納プロパティを定義します。

struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
let name: String
init(name: String) {
self.name = name // 初期化時に設定した後は変更不可能
}
}
// インスタンス生成
var someVideoMode = VideoMode(name: "何らかのビデオモード")
someVideoMode.name = "別のビデオモード" // エラー: nameは変更できない
var someResolution = Resolution()
(someResolution.width, someResolution.height) = (1920, 1080)
someVideoMode.resolution = someResolution // 値のコピー
someResolution.width = 1280
assert(someVideoMode.resolution.width == 1920) // コピーなので、someResolutionとは別の値を持つ
someVideoMode.frameRate = 60.0
var anotherVideoMode = someVideoMode
anotherVideoMode.frameRate = 30.0
assert(someVideoMode.frameRate == 30.0) // anotherVideoModeと同じインスタンスを指す

varの前にlazyを指定すると遅延格納プロパティになり、プロパティにアクセスするまでインスタンスが作成されません。lazyletでは使えません。

struct A {
var value: Int = 0
init() {
print("A.init()")
}
mutating func setValue(_ value: Int) {
print("A.setValue()")
self.value = value
}
}
struct B {
lazy var a = A()
var value: Int = 0
init() {
print("B.init()")
}
mutating func setValue(_ value: Int) {
print("B.setValue()")
self.value = value
}
}
var b = B() // "B.init()"が出力される
b.setValue(5) // "B.setValue()"が出力される
b.a.setValue(10) // "A.init()" "A.setValue()"の順で出力される

getsetを使って定義します。

struct Square {
var sideLength = 0.0
var area: Double {
get { sideLength * sideLength }
set(value) { sideLength = value.squareRoot() }
// あるいは以下のように`newValue`を使って書ける
// set {sideLength = newValue.squareRoot()}
}
var description: String { // 読み取り専用プロパティ(set無し)は`get`の記述が不要
"辺の長さ=\(sideLength) 面積=\(area)"
}
}
var square = Square(sideLength: 10)
assert(square.area == 10 * 10)
square.area = 21 * 21
assert(square.sideLength == 21)
print(square.description)

willSetはプロパティ値が格納される直前に呼び出され、didSetはプロパティ値が格納された直後に呼び出されます。

class A {
var a: Int = 100 {
willSet { // `set`と同じく`newValue`が使える
print("(2) A.a: \(a) -> \(newValue)")
}
didSet { // `oldValue`を使うかパラメータ名を指定する
print("(3) A.a: \(a) <- \(oldValue)")
if a > 500 { // もしプロパティ値を是正してもdidSetは再度呼び出されない
a = 500
}
}
}
}
class B: A {
override var a: Int {
willSet {
print("(1) B.a: \(a) -> \(newValue)")
}
didSet {
print("(4) B.a: \(a) <- \(oldValue)")
}
}
}
var b = B()
b.a = 999
// 実行結果:
// (1) B.a: 100 -> 999
// (2) A.a: 100 -> 999
// (3) A.a: 999 <- 100
// (4) B.a: 500 <- 100

プロパティをラップしてカスタムコードをプロパティに追加します。

  • @propertyWrapper属性を付けてプロパティラッパを実装します。
    • wrappedValueプロパティでプロパティ値がラップされます。
    • projectedValueプロパティでプロパティ値から投影された値を保持します。
      • 投影された値は $プロパティ名 で取得できます。
// projectedValue(プロパティラッパからの投影)用enumの定義
enum ProjectionType {
case 範囲内, 最小値より小さい, 最大値より大きい
}
// 範囲制限付き整数(min〜max)を表すプロパティラッパの定義
@propertyWrapper
struct LimitedNumber {
private var _value: Int = 0 // プロパティの値を格納する
private var _min: Int = Int.min // 最小値
private var _max: Int = Int.max // 最大値
var projectedValue = ProjectionType.範囲内 // プロパティラッパからの投影
// イニシャライザ
init() {
print("(1) init()")
}
init(wrappedValue: Int) {
print("(2) init(wrappedValue:)")
_value = clamp(wrappedValue, min: _min, max: _max)
}
init(wrappedValue: Int = 0, min minValue: Int = Int.min, max maxValue: Int = Int.max) {
print("(3) init(wrappedValue:min:max:)")
_value = clamp(wrappedValue, min: minValue, max: maxValue)
(_min, _max) = (minValue, maxValue)
}
// プロパティの値を設定・取得するwrappedValueメソッドを必ず定義する
var wrappedValue: Int {
get { _value }
set { _value = clamp(newValue, min: _min, max: _max) }
}
// サブルーチン
private mutating func clamp(_ value: Int, min: Int, max: Int) -> Int {
if value < min {
projectedValue = .最小値より小さい
return min
} else if value > max {
projectedValue = .最大値より大きい
return max
} else {
projectedValue = .範囲内
return value
}
}
}
// プロパティラッパを使う
struct A {
@LimitedNumber var a: Int // (1)
@LimitedNumber var b: Int = 100 // (2)
@LimitedNumber(min: 0) var c: Int = 200 // (3)
@LimitedNumber(min: 0, max: 10) var d: Int = 300 // (3)
}
var x = A()
assert(x.a == 0)
assert(x.b == 100)
assert(x.c == 200)
assert(x.d == 10)
x.c = -200
assert(x.c == 0)
// プロパティラッパからの投影
print(x.$a) // 範囲内
print(x.$b) // 範囲内
print(x.$c) // 最小値より小さい
print(x.$d) // 最大値より大きい

型プロパティはC/C++のstaticメンバ変数のように、staticキーワードで静的なプロパティを型に定義します。クラスでオーバーライド可能な型プロパティを定義するときはstaticの代わりにclassを使います。

// Playgroundでは以下のエラーが発生するため、`@MainActor`を付ける
// `Static property 'storedTypeProperty' is not concurrency-safe because it is nonisolated global shared mutable state`
@MainActor
struct A {
static var storedTypeProperty = "struct A"
static var computedTypeProperty: Int {
return 1
}
var value: (String, Int) {
(A.storedTypeProperty, A.computedTypeProperty)
}
}
@MainActor
enum B {
case b1, b2
static var storedTypeProperty = b2
var value: B {
B.storedTypeProperty
}
}
@MainActor
class C {
static var storedTypeProperty = "class C"
static var computedTypeProperty: Int {
return 3
}
class var overridableComputedTypeProperty: Int { // オーバーライド可能な計算プロパティは`class`で定義する
return 30
}
var value: (String, Int, Int) {
(C.storedTypeProperty, C.computedTypeProperty, C.overridableComputedTypeProperty)
}
}
class D: C {
override class var overridableComputedTypeProperty: Int { // オーバーライド可能な計算プロパティは`class`で定義する
return 300
}
override var value: (String, Int, Int) {
(D.storedTypeProperty, D.computedTypeProperty, D.overridableComputedTypeProperty)
}
}
var a1 = A(), a2 = A()
A.storedTypeProperty = "上書きした値"
print(a1.value, a2.value) // ("上書きした値", 1) ("上書きした値", 1)
var b1 = B.b1, b2 = B.b2
B.storedTypeProperty = .b1
print(b1.value, b2.value) // b1 b1
var c1 = C(), d1 = D()
D.storedTypeProperty = "Dに上書きした値"
print(c1.value, d1.value) // ("Dに上書きした値", 3, 30) ("Dに上書きした値", 3, 300)

structclassenumの定義内に関数と同じ構文で記述します。

  • インスタンス自身はselfで参照可能 (thisではない)
  • structenumのメソッドがインスタンスの値を変更する場合、mutatingが必要
    • self自体に新しいインスタンスを割り当てることも可能 (self = newValueのように)
enum State: CustomStringConvertible { // `description`プロパティでtoStringや__str__相当を実装できる
case ready, running, emergency
mutating func change(to newValue: State) {
self = newValue
}
static let displayTexts: [State: String] = [
.ready: "準備中",
.running: "運行中",
.emergency: "緊急停止中"
]
var description: String { // CustomStringConvertibleプロトコル
return State.displayTexts[self]!
}
}
struct A { // `class`にするとメソッド定義の`mutating`は不要
private var state: State = .ready
mutating func start() {
state.change(to: .running)
self.print()
}
mutating func stop() {
state.change(to: .ready)
self.print()
}
mutating func emergencyStop() {
state.change(to: .emergency)
self.print()
}
mutating func reset() {
self = A()
self.print()
}
func print() {
Swift.print("現在の状態: \(state)")
}
}
var a = A()
a.start()
a.emergencyStop()
a.reset()
let a2 = A()
a2.start() // エラー: a2は定数のためmutatingメソッドを呼び出せない

型メソッドはC/C++のstaticメンバ関数のように、staticキーワードで静的なメソッドを型に定義します。クラスでオーバーライド可能な型メソッドを定義するときはstaticの代わりにclassを使います。

@MainActor
class LimitedCounter {
static var maxCount: Int = 5
var count: Int = 0
static func setMaxCount(_ maxCount: Int) {
// 型メソッドからは、`self`または`Self`で型プロパティにアクセスできる
self.maxCount = maxCount
}
class func reset() { // オーバーライド可能な型メソッド
maxCount = 5
}
func increment() {
// インスタンスメソッドからは、型名か`Self`で型プロパティにアクセスできる
if count < Self.maxCount {
count += 1
}
}
}
@MainActor
class CustomLimitedCounter: LimitedCounter {
override class func reset() {
maxCount = 3
}
}
var counter = LimitedCounter()
LimitedCounter.setMaxCount(7)
for i in 1...10 {
counter.increment()
print(counter.count)
}
var customCounter = CustomLimitedCounter()
CustomLimitedCounter.reset()
for i in 1...10 {
customCounter.increment()
print(customCounter.count)
}

structclassenumの定義内にsubscriptを定義すると、コレクション(配列・辞書・セット)のようなサブスクリプト構文が使えます。Pythonの__getitem__風の機能を実装できます。

class Airports {
var airports = ["HND": "羽田空港", "KMQ": "小松空港"]
subscript(code: String) -> String? {
get { airports[code] }
set { airports[code] = newValue }
}
func dump() {
print(airports)
}
}
var airports = Airports()
assert(airports["HND"] == "羽田空港")
airports["HND"] = nil
airports["NRT"] = "成田空港"
airports.dump() // ["KMQ": "小松空港", "NRT": "成田空港"]

サブスクリプトは任意の数のパラメータを渡せます。staticを使って型サブスクリプトも定義できます。

import Foundation
// 8bitのRGBチャンネルを指し示す列挙型
enum Channel: Int {
case r = 0, g, b
static let count: Int = 3
static subscript(channel: Int) -> Channel {
Channel(rawValue: channel)!
}
}
// 8bitのRGBチャンネルを持つ画像データを模倣するクラス
class ImagePixels {
let width: Int, height: Int
var pixels: [UInt8]
init(_ width: Int, _ height: Int) {
(self.width, self.height) = (width, height)
pixels = Array(repeating: UInt8(0), count: width * height * 3)
}
func indexIsValid(x: Int, y: Int) -> Bool {
return x >= 0 && x < width
&& y >= 0 && y < height
}
subscript (x: Int, y: Int, channel: Channel) -> UInt8 { // 複数の引数を持つことができる
get {
assert(indexIsValid(x: x, y: y))
let index = (y * width + x) * 3 + channel.rawValue
return pixels[index]
}
set {
assert(indexIsValid(x: x, y: y))
let index = (y * width + x) * 3 + channel.rawValue
pixels[index] = newValue
}
}
func dump() { // HTMLカラーコード形式で画素値を表示する
for y in 0..<height {
for x in 0..<width {
var colorCode = "#"
for i in 0..<Channel.count {
colorCode += String(format: "%02x", self[x, y, Channel[i]])
}
print(colorCode, terminator: " ")
}
print()
}
}
}
var imagePixels = ImagePixels(8, 8)
imagePixels[7, 0, .r] = 255 //右上に赤い点
imagePixels[0, 7, .g] = 255 //左下に緑の点
imagePixels[7, 7, .b] = 255 //右下に青の点
imagePixels.dump()

クラス・構造体・列挙型・プロトコルなどの型の中に別の型を定義できます。

class A {
// クラスの中に構造体を定義
struct B {
// 構造体の中に列挙型を定義
enum C {
case x, y, z
}
var value: C
}
var data: B
init(_ value: B.C) {
data = B(value: value)
}
func getValue() -> A.B.C {
return data.value
}
}
// テスト
let a = A(A.B.C.x)
print(a.getValue())

継承はclassでのみ使えます。

Swiftでは別のクラスから継承しないクラスを基本クラス(base class)と呼びます。基本クラスには、C#やJavaのObjectクラスや、Pythonのobjectクラスのような全てのクラスで基底となるクラスがありません。(C++のクラスと同様)

overrideを使ってオーバーライドできます。(インスタンスメソッド、型メソッド、インスタンスプロパティ、型プロパティ、サブスクリプトごとに指定可能)

finalを使ってオーバーライドされないようにできます。(インスタンスメソッド、型メソッド、インスタンスプロパティ、型プロパティ、サブスクリプトに加えてclassにも指定可能)

// 基本クラスの定義
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "速度は \(currentSpeed) km/h"
}
func makeNoise() {
// 乗り物は必ずしも騒音を出しません
}
}
// サブクラスの定義
class Train: Vehicle {
override func makeNoise() {
// 電車を想定
print("モーター + VVVFインバータ音")
}
}
class Car: Vehicle {
var gear = 1
override var description: String {
"ギアは \(gear) 速で、" + super.description
}
override func makeNoise() {
print("エンジン音")
}
}
let car = Car()
(car.gear, car.currentSpeed) = (3, 40.0)
print(car.description)
car.makeNoise()
// finalクラスの定義 (これ以上サブクラスを作ることはできない)
final class Prius: Car {
override func makeNoise() {
print("モーター + エンジン音")
}
}
let vehicle: Vehicle = Prius()
vehicle.makeNoise()

型チェック演算子(is)でクラスのインスタンスが特定のサブクラスかどうか確認できます。

assert(vehicle is Prius)
assert(vehicle is Car)
assert(!(vehicle is Train))

サブクラス型へのダウンキャストには型キャスト演算子(as?またはas!)を使用します。

条件付き形式のas?は、以下のように使います。

vehicle.currentSpeed = 30
if let car = vehicle as? Car {
car.gear = 2
}
print(vehicle.description)

強制形式のas!は、以下のように使います。

let myCar = vehicle as! Car // もしCarのサブクラスでない場合、実行時エラー
myCar.currentSpeed = 50
myCar.gear = 3
print(myCar.description)

is及びas?またはas!は、プロトコルにも使用できます。

Anyは、関数型も含めて、あらゆる型のインスタンスを表します。また、AnyObjectは、任意のクラス型のインスタンスを表します。

AnyAnyObjectのデータはisas演算子で型キャストできます。また、switch文のcaseisasでパターンマッチさせることができます。

class A {
let x: Int
init(x: Int) {
self.x = x
}
}
let values: [Any] = [
0,
1.2,
"Hello",
[1, 2, 3],
(1, 2),
A(x: 123),
{ (a: Int, b: Int) -> Int in a + b }
]
if let value = values[0] as? Int {
print("value is \(value) as Int")
}
for value in values {
switch value {
case 0 as Int:
print("value is 0 as Int")
case 1.2 as Double:
print("value is 1.2 as Double")
case let array as [Int]:
print("value is \(array) as [Int]")
case let (x, y) as (Int, Int):
print("value is (\(x), \(y)) as (Int, Int)")
case is A:
print("value is an instance of A")
case let add as (Int, Int) -> Int:
let result = add(1, 2)
print("value is a function: \(result) = add(1, 2)")
default:
print("value is not supported")
}
}

インスタンス生成時の初期化を行うイニシャライザ(init)をstructclassenumで定義できます。

struct 摂氏: Equatable {
var 摂氏温度: Double
init(華氏 華氏温度: Double) {
摂氏温度 = (華氏温度 - 32.0) / 1.8
}
init(K ケルビン: Double) { // 引数の型や数が同じでも引数ラベルが異なるとオーバーロード可能
self.init(ケルビン - 273.15) // イニシャライザの委譲
}
init(_ 摂氏温度: Double) {
self.摂氏温度 = 摂氏温度
}
static func == (lhs: 摂氏, rhs: 摂氏) -> Bool { // `==`演算子の実装
return lhs.摂氏温度 == rhs.摂氏温度
}
}
let 水の沸点 = 摂氏(華氏: 212.0)
assert(水の沸点.摂氏温度 == 100.0)
let 水の凝固点 = 摂氏(K: 273.15)
assert(水の凝固点 == 摂氏(0.0))

初期化に失敗し得るイニシャライザはinit?(nilを返す)を使って失敗可能イニシャライザを定義できます。

struct Counter {
var _counter: Int
init?(from value: Int? = nil) {
if let value {
_counter = value
} else {
return nil
}
}
var count: Int { _counter }
mutating func countUp() {
_counter += 1
}
}
// テスト
var counter: Counter? = Counter()
counter?.countUp()

デフォルトイニシャライザ(クラス)

Section titled “デフォルトイニシャライザ(クラス)”

classでは、initを定義しなければデフォルトイニシャライザが自動生成されます。

class Resolution {
var width: Int = 0 // 全てのプロパティには初期値が必要
var height: Int = 0
// classにinitを追加すると、デフォルトイニシャライザは生成されない
}
let someResolution = Resolution()

メンバワイズイニシャライザ(構造体)

Section titled “メンバワイズイニシャライザ(構造体)”

structでは、initを定義しなければメンバワイズイニシャライザが自動生成されます。

なお、enumにデフォルトイニシャライザやメンバワイズイニシャライザは存在しません。

struct Resolution {
var width: Int = 0
var height: Int // デフォルト値が無いためイニシャライザで初期化が必要
// structにinitを追加すると、メンバワイズイニシャライザは生成されない
}
let fullHD = Resolution(width: 1920, height: 1080)
let someResolution = Resolution(height: 480)

クラスの継承とイニシャライザ

Section titled “クラスの継承とイニシャライザ”

Swiftはクラスに対して2種類のイニシャライザを定義して、サブクラスからスーパークラスまでの全ての格納プロパティを初期化します。

  • 指定イニシャライザ (designatedイニシャライザ): initで定義する
    • 全てのクラスには少なくとも1つの指定イニシャライザが必要になる (デフォルトイニシャライザも含む)
    • 直接スーパークラスの指定イニシャライザを呼び出す必要がある
  • convenienceイニシャライザ: convenience initで定義する
    • 同じクラスの別の指定イニシャライザまたはconvenienceイニシャライザを呼び出す必要がある・・・①
    • 最終的に、同じクラスの指定イニシャライザを呼び出す必要がある・・・②
      • 1つの指定イニシャライザに対して、複数のconvenienceイニシャライザを補助的に定義する (引数のデフォルト値を設定するなど)
      • 不要ならconvenienceイニシャライザは定義しなくてよい
class A {
var n: Int = 0
var s: String = ""
// 指定イニシャライザ
init(n: Int, s: String) {
print("A: init(n: \(n), s: \"\(s)\")")
self.n = n
self.s = s
}
// convenienceイニシャライザ
convenience init(n: Int) {
print("A: convenience init(n: \(n))")
self.init(n: n, s: "Aのコンビニinit") // ②を満たす
}
// convenienceイニシャライザ
convenience init() {
print("A: convenience init()")
self.init(n: 1) // ①を満たす
}
}
var a1 = A()
// A: convenience init()
// A: convenience init(n: 1)
// A: init(n: 1, s: "Aのコンビニinit")
var a2 = A(n: 2)
// A: convenience init(n: 2)
// A: init(n: 2, s: "Aのコンビニinit")
var a3 = A(n: 3, s: "Aの指定init")
// A: init(n: 3, s: "Aの指定init")

Swiftのサブクラスは、デフォルトでスーパークラスのイニシャライザを継承しません。

class B: A {
var b: Int
// 指定イニシャライザ
init(b: Int, n: Int, s: String) {
print("B: init(b: \(b), n: \(n), s: \"\(s)\")")
self.b = b // 先にサブクラスのプロパティを初期化する (第1段階の初期化)
super.init(n: n, s: s) // スーパークラスの初期化
}
// convenienceイニシャライザ (Aのconvenienceイニシャライザをオーバーライドしていない)
convenience init() {
print("B: convenience init()")
self.init(b: 10, n: 11, s: "Bのコンビニinit")
}
}
var b1 = B()
// B: convenience init()
// B: init(b: 10, n: 11, s: "Bのコンビニinit")
// A: init(n: 11, s: "Bのコンビニinit")
var b2 = B(b: 20, n: 21, s: "Bの指定init")
// B: init(b: 20, n: 21, s: "Bの指定init")
// A: init(n: 21, s: "Bの指定init")
var b4 = B(n: 40, s: "") // エラー: Aの指定イニシャライザは継承しない
var b3 = B(n: 30) // エラー: Aのconvenienceイニシャライザは継承しない

スーパークラスの指定イニシャライザをサブクラスでも使えるようにするには、override initでオーバーライドします。

class C: A {
var c: Int
// 指定イニシャライザのオーバーライド
override init(n: Int, s: String) {
print("C: init(n: \(n), s: \(s))")
self.c = 200
super.init(n: n, s: s)
}
}
var c = C(n: 100, s: "Cの指定init")
// C: init(n: 100, s: Cの指定init)
// A: init(n: 100, s: "Cの指定init")

ただし、サブクラスのプロパティがデフォルト値を持っている場合、以下のルールが適用されます。

  • サブクラスが指定イニシャライザを定義していない場合、スーパークラスの指定イニシャライザを全て継承する (下記サンプルのDクラス)
  • サブクラスがスーパークラスの指定イニシャライザを全て継承している(下記サンプルのDクラス)か、全て定義している(下記サンプルのEクラス)場合、スーパークラスのconvenienceイニシャライザを全て継承する

下記のDクラスは指定イニシャライザを定義していないので、スーパークラスの指定イニシャライザとconvenienceイニシャライザを全て継承します。

class D: A {
var d: Int = 0
}
var d1 = D(n: 1000, s: "Aから継承した指定init")
// A: init(n: 1000, s: "Aから継承した指定init")
var d2 = D(n: 2000)
// A: convenience init(n: 2000)
// A: init(n: 2000, s: "Aのコンビニinit")

下記のEクラスは、スーパークラスの指定イニシャライザを全て定義しているので、スーパークラスのconvenienceイニシャライザを全て継承します。

class E: A {
var e: Int
// 指定イニシャライザ
init(e: Int, n: Int, s: String) {
print("E: init(e: \(e), n: \(n), s: \"\(s)\")")
self.e = e
super.init(n: n, s: s)
}
// 指定イニシャライザのオーバーライド
override init(n: Int, s: String) {
print("E: オーバーライドinit(n: \(n), s: \(s))")
self.e = 20000
super.init(n: n, s: s)
}
}
var e1 = E(e: 30000, n: 30001, s: "Eの指定init")
// E: init(e: 30000, n: 30001, s: "Eの指定init")
// A: init(n: 30001, s: "Eの指定init")
var e2 = E(n: 40000, s: "オーバーライドしたEの指定init")
// E: オーバーライドinit(n: 40000, s: オーバーライドしたEの指定init)
// A: init(n: 40000, s: "オーバーライドしたEの指定init")
var e3 = E(n: 50000) // 継承したconvenienceイニシャライザ
// A: convenience init(n: 50000)
// E: オーバーライドinit(n: 50000, s: Aのコンビニinit)
// A: init(n: 50000, s: "Aのコンビニinit")
var e4 = E() // 継承したconvenienceイニシャライザ
// A: convenience init()
// A: convenience init(n: 1)
// E: オーバーライドinit(n: 1, s: Aのコンビニinit)
// A: init(n: 1, s: "Aのコンビニinit")

オーバーライドしたinitをconvenienceにすることもできます。

class F: A {
var f: Int
// 指定イニシャライザ
init(f: Int, n: Int, s: String) {
print("F: init(f: \(f), n: \(n), s: \"\(s)\")")
self.f = f
super.init(n: n, s: s)
}
// 指定イニシャライザをオーバーライドしてconvenienceイニシャライザにする
override convenience init(n: Int, s: String) {
print("F: オーバーライドinit(n: \(n), s: \(s))のconvenience版")
self.init(f: 100000, n: n, s: s) // convenienceなので同じクラスのイニシャライザを呼び出す
}
}
var f1 = F(f: 200000, n: 200001, s: "Fの指定init")
// F: init(f: 200000, n: 200001, s: "Fの指定init")
// A: init(n: 200001, s: "Fの指定init")
var f2 = F(n: 300000, s: "Fのコンビニinit")
// F: オーバーライドinit(n: 300000, s: Fのコンビニinit)のconvenience版
// F: init(f: 100000, n: 300000, s: "Fのコンビニinit")
// A: init(n: 300000, s: "Fのコンビニinit")

サブクラスでイニシャライザの実装を必須とする場合は、required initで必須イニシャライザにします。

class X {
var value: Int = 0
required init() {
// スーパークラスではデフォルト値のままとする
}
func printValue() {
print(value)
}
}
class Y: X {
required init() { // NOTE: このイニシャライザを無くしても動くため、言語仕様の文書が正しくない?
super.init()
value = 100
}
}
// テスト
var x = X()
x.printValue()
var y = Y()
y.printValue()

C/C++のデストラクタのように、インスタンス解放時の終了処理を行うデイニシャライザ(deinit)をclassで1つだけ定義できます。

@MainActor
class A {
static var instanceCount: Int = 0
init() {
Self.instanceCount += 1
print("created: count = \(Self.instanceCount)")
}
@MainActor
deinit {
Self.instanceCount -= 1
print("deleted: count = \(Self.instanceCount)")
}
}
var a1: A? = A() // created: count = 1
var a2: A? = A() // created: count = 2
var a3: A? = A() // created: count = 3
a1 = nil // deleted: count = 2

プロトコルはプロパティやメソッドなどの要件を定義して、クラス・構造体・列挙型にそれらの要件を準拠(conform)させます。

プロトコルは実装を持ちません。拡張を使って実装を追加できます。


  • インスタンスプロパティ、型プロパティ(static)をvarで定義する
    • プロトコルに準拠する型では、letclassを使って実装してもよい
  • var プロパティ名: 型名の後ろに{ get set }または{ get }でプロパティを定義する
    • プロトコルに準拠する型では、計算プロパティや格納プロパティとして実装できる
    • プロトコルの定義が{ get }であっても、準拠する型では{ get set }としてプロパティを実装できる(プロトコルの要件は満たすので)
// プロトコルの定義
protocol MotorVehicle {
static var description: String { get } // 説明文
static var fuelEfficiency: Double { get } // 燃料効率 (km/ℓ)
static var fuelTankCapacity: Double { get } // 燃料タンクの容量
var fuelLevel: Double { get set } // 燃料残量
mutating func fillUp() // 燃料タンクを満タンにする
}
// プロトコルを拡張して実装を追加できる
extension MotorVehicle {
@discardableResult // 呼び出し元で戻り値を使用しない場合もある関数に与える属性(コンパイラ警告を回避)
mutating func drive(distance: Double) -> Bool {
let fuel = distance / Self.fuelEfficiency
if (fuel > fuelLevel) {
print("燃料が\(fuel - fuelLevel)ℓ足りません。")
return false
} else {
fuelLevel -= fuel
print("燃料の残量は\(fuelLevel)ℓです。")
return true
}
}
mutating func fillUp() { // プロトコル要件のデフォルト実装を提供できる
fuelLevel = Self.fuelTankCapacity
}
}
// オートバイ構造体の定義
struct MotorCycle: MotorVehicle {
static let description = "オートバイX"
static let fuelEfficiency = 40.0 // プロトコルではgetのみだが、構造体ではget/setで実装した
static let fuelTankCapacity = 20.0
var fuelLevel = 0.0
}
// 自動車構造体も同様に定義できる
struct Car: MotorVehicle {
static let description = "自動車Y"
static let fuelEfficiency = 15.0
static let fuelTankCapacity = 45.0
var fuelLevel = 0.0
// 以下はCar固有の格納プロパティ
var doorIsOpen = false
}
// テスト
var moto: MotorCycle = MotorCycle() // `let`で定義すると、以降のmutatingメソッドの呼び出しはコンパイルエラーになる
moto.drive(distance: 100)
moto.fillUp()
moto.drive(distance: 100)

メソッド要件・イニシャライザ要件

Section titled “メソッド要件・イニシャライザ要件”
  • インスタンスメソッド、型メソッド(static)を定義する
    • プロトコルに準拠するクラスでは、staticまたはclassを使って実装してもよい
  • func 名前(引数) -> 戻り値の型名で定義し、関数本体のブロックは省略する
    • 引数のデフォルト値は指定できない
    • イニシャライザもinitで定義し、本体のブロックは省略する
      • 失敗可能なinit?でも定義できる
      • クラスの場合、required initで実装する
  • メソッドの前にmutatingを付けて、変更可能なメソッド要件であることを示す
    • 準拠する型がクラスの場合、mutatingを省いて実装する
protocol Counter {
init(from value: Int)
static func getName() -> String
var count: Int { get }
mutating func countUp()
}
// Counterプロトコルに準拠したクラスの例
class ClassCounter: Counter {
var _counter: Int
required init(from value: Int) { // `required`が必要
_counter = value
}
class func getName() -> String { // `class`でもOK
"ClassCounter"
}
var count: Int { _counter }
func countUp() { // `mutating`は不要
_counter += 1
}
}
// Counterプロトコルに準拠した構造体の例
struct StructCounter: Counter {
var _counter: Int
init(from value: Int) {
_counter = value
}
static func getName() -> String {
"StructCounter"
}
var count: Int { _counter }
mutating func countUp() {
_counter += 1
}
}
// テスト
print(ClassCounter.getName())
let classCounter = ClassCounter(from: 0)
classCounter.countUp()
classCounter.countUp()
print(classCounter.count)
print(StructCounter.getName())
var structCounter = StructCounter(from: 1)
structCounter.countUp()
structCounter.countUp()
print(structCounter.count)

セマンティック要件のみのプロトコル

Section titled “セマンティック要件のみのプロトコル”

Swift標準ライブラリに組み込まれている以下のプロトコルは、必須のメソッドやプロパティを持ちません。

  • Sendable: 並行処理ドメイン(concurrency domain, タスクやアクターの内側でミュータブルな状態を含んでいるプログラムの部分)間で共有できる値であることを示す
  • Copyable: 関数に渡すときにコピーできる値であることを示す
  • BitwiseCopyable: ビット単位でコピーできる値であることを示す

なお、これらのプロトコルは、型を定義したときにSwiftが暗黙的に準拠を推論します。暗黙の準拠を抑制する場合は、プロトコル名の前に~を付けます。

struct A1 { // 暗黙的にCopyable, Sendable, BitwiseCopyableへ準拠する
let x = 123
}
struct A2: ~Copyable { // Copyableへの準拠を抑制する
let x = 456
}
let a1: any Copyable = A1()
let a2: any Copyable = A2() // エラー: A2はCopyableに準拠しない

変数・定数・関数・メソッド・プロパティなどの型にプロトコルを使えます。

例として、Animalプロトコルとそれに準拠した構造体、及び列挙型を定義します。

protocol Animal {
var emoji: Character { get }
}
struct Dog: Animal {
var emoji: Character = "🐶"
}
struct Cat: Animal {
var emoji: Character = "🐱"
}
enum AnimalType {
case dog, cat
}

some プロトコルと記述するとOpaque型となって、プロトコルに準拠した特定の1つの型に固定されます。

func getAnimal(_ type: AnimalType) -> some Animal { // エラー: 戻り値が特定の型に定まっていない
switch type {
case .cat:
return Cat()
default:
return Dog()
}
}

some プロトコル型で定義した変数に代入しようとすると、初期化時と同じ型であってもエラーになります。

var animal: some Animal = Dog()
animal = Cat() // エラー: cannot assign value of type 'Cat' to type 'some Animal'
animal = Dog(emoji: "🐕️") // エラー: cannot assign value of type 'Dog' to type 'some Animal'

以下のように型注釈のない変数宣言を行って、戻り値がsome プロトコル型の関数を複数回呼び出すことはOKです。

func getDog(_ emoji: Character? = nil) -> some Animal {
if let emoji {
return Dog(emoji: emoji)
} else {
return Dog()
}
}
var animal = getDog()
animal = getDog("🐕️")

any プロトコルと記述するとBoxプロトコル型となって、Boxing処理により間接的なレイヤーを挟んでプロトコルに準拠する任意の型を扱えるようになりますが、Boxing処理に伴うパフォーマンスコストが増加します。

func getAnimal(_ type: AnimalType) -> any Animal {
switch type {
case .cat:
return Cat()
default:
return Dog()
}
}
var animal: any Animal = getAnimal(.dog)
animal = getAnimal(.cat)

プロトコルはコレクションに格納される型としても使用できます。

let dogs: [some Animal] = [Dog(), Dog(emoji: "🐕️")]
let animals: [any Animal] = [Dog(), Dog(emoji: "🐕️"), Cat(), Cat(emoji: "🐈️")]

準拠すると実装が提供されるプロトコル (Equatable, Hashable, Comparable)

Section titled “準拠すると実装が提供されるプロトコル (Equatable, Hashable, Comparable)”

以下の種類の独自の型では、EquatableHashableに準拠するとデフォルトの実装が提供されて、自前で実装する必要がありません。

  • EquatableHashableに準拠した型の格納プロパティのみで構成される構造体
  • 関連値がEquatableHashableに準拠する型のみの列挙型
  • 関連値のない列挙型

関連値のない列挙型がComparableに準拠すると、デフォルトのComparableの実装が提供されて、自前で実装する必要がありません。

struct Complex: Equatable {
var real: Double
var imaginary: Double
func toString() -> String {
if imaginary == 0 {
return "\(real)"
} else if imaginary < 0 {
return "\(real)\(imaginary)i"
}
return "(\(real)+\(imaginary))i"
}
}
let a = Complex(real: 1.0, imaginary: 2.0)
let b = Complex(real: 3.0, imaginary: -5.0)
assert(a != b)
enum Number: Comparable {
case one, two, three
}
assert(Number.one < Number.two)
enum ReverseNumber: Comparable {
case three, two, one
}
assert(ReverseNumber.one > ReverseNumber.two)

プロトコルは1つ以上の他のプロトコルを継承して、継承する要件に要件を追加できます。

プロトコル継承の一覧にAnyObjectを追加すると、クラス型だけがそのプロトコルに準拠できます。

protocol A {
var a: Int { get set }
}
protocol B {
var b: String { get set }
}
protocol C: A, B {
var value: (Int, String) { get }
}
protocol C2: AnyObject, A, B {
var value: (Int, String) { get }
}
struct X: C {
var a: Int = 0
var b: String = ""
var value: (Int, String) {
(a, b)
}
}
struct X2: C2 { // エラー: classではない
var a: Int = 0
var b: String = ""
var value: (Int, String) {
(a, b)
}
}

ProtocolA & ProtocolBと記述すると、複数のプロトコルに準拠していることを示せます。

protocol A {
var a: Int { get set }
}
protocol B {
var b: String { get set }
}
struct C: A, B {
var a: Int = 0
var b: String = ""
}
class D: A, B {
var a: Int = 0
var b: String = ""
init(a: Int, b: String) {
self.a = a
self.b = b
}
}
func getValue(_ param: A & B) -> (Int, String) {
return (param.a, param.b)
}
let c = getValue(C(a:123, b:"ABC"))
let d = getValue(D(a:-123, b:"def"))

オプショナルのプロトコル要件

Section titled “オプショナルのプロトコル要件”

オプショナルのプロトコル要件としてプロパティやメソッドを定義できます。 ただし@objc属性が必要で、Objective-Cと相互運用するクラスだけが準拠できます。 (Objective-Cと相互運用できないデータ型を使うこともできない)

オプショナルのプロトコル要件であるプロパティやメソッドはオプショナルチェーンで呼び出します。

import Foundation
// NSObjectから派生したクラスは@objc属性の付いたプロトコルで扱うことができる
@objc
class Value: NSObject {
var number: Int = 0
var text: String = ""
init(number: Int = 0, text: String = "") {
self.number = number
self.text = text
}
}
@objc
protocol A {
@objc optional func getValue() -> Value
}
class AImpl: NSObject, A {
var value: Value = Value()
func getValue() -> Value {
value
}
init(number: Int, text: String) {
self.value.number = number
self.value.text = text
}
}
class B {
var a: A!
init(a: A) {
self.a = a
}
}
// テスト
var a: A = AImpl(number: 123, text: "hello")
var b = B(a: a)
if let number = b.a.getValue?().number {
print("number: \(number)")
}
if let text = b.a.getValue?().text {
print("text: \(text)")
}

C++のテンプレートやC#・Javaのジェネリクスと同様の機能です。


サブスクリプト(subscript)もジェネリックで記述できます。

func swapVars<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
// テスト
var (n1, n2): (Int, Int) = (123, 456)
swapVars(&n1, &n2)
print("n1: \(n1), n2: \(n2)")
var (s1, s2): (String, String) = ("ABC", "DEF")
swapVars(&s1, &s2)
print("s1: \(s1), s2: \(s2)")

Array<Element>, Dictionary<Key, Value>などのコレクションもジェネリック型です。

struct Stack<T> {
var items: [T] = []
mutating func push(_ item: T) {
items.append(item)
}
mutating func pop() -> T {
if items.isEmpty { fatalError("pop from empty stack") }
return items.removeLast()
}
}
// テスト
var stack = Stack<Int>()
stack.push(123)
stack.push(456)
print(stack.pop())
print(stack.pop())
print(stack.pop()) // エラー: pop from empty stack

ジェネリック型の拡張を書くときは、型パラメータリストは不要です。

extension Array where Element: Equatable {
func countOf(_ item: Element) -> Int {
var count = 0
for i in self {
if i == item {
count += 1
}
}
return count
}
}
// テスト
let numbers = [1, 2, 1, 2, 3, 2, 3, 4]
print(numbers.countOf(2))

型パラメータは制約できます。 関数パラメータリストと戻り値の間にwhereを使って制約に条件を追加できます。

func countOf<T: Equatable>(_ item: T, in items: Array<T>) -> Int {
var count = 0
for i in items {
if i == item {
count += 1
}
}
return count
}
// テスト
let numbers = [1, 2, 1, 2, 3, 2, 3, 4]
print(countOf(2, in: numbers))

プロトコルでジェネリック型のような書き方(例えばprotocol A<T>)はできません。 associatedtypeを使ってプロトコル内で使用する型にプレースホルダ名を与えます。

制約を付けるときは associatedtype Item: Equatableなどのように書けます。 さらにwhereで制約に条件を追加できます。

protocol Stackable {
associatedtype Item
mutating func push(_ item: Item)
mutating func pop() -> Item
}
struct Stack<T>: Stackable {
var items: [T] = []
mutating func push(_ item: T) {
items.append(item)
}
mutating func pop() -> T {
if items.isEmpty { fatalError("pop from empty stack") }
return items.removeLast()
}
}

extensionで既存のクラス・構造体・列挙型・プロトコルに機能を追加します。IntStringなど、元のソースコードにアクセスできない型を拡張することもできます。

Int型に計算プロパティとイニシャライザを追加する例:

import Foundation
extension Int {
private static var formatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = ","
formatter.groupingSize = 3
return formatter
}
// 3桁区切りの文字列で整数値を取得する計算プロパティ
var formatted: String {
Self.formatter.string(from: NSNumber(value: self)) ?? "\(self)"
}
// 3桁区切りの文字列から整数値を初期化するイニシャライザ
init(formatted: String) {
self = Int(Self.formatter.number(from: formatted)?.intValue ?? 0)
}
}
// テスト
print(123456789.formatted)
print(Int(formatted: "1,234,567,890"))

String型にメソッドとサブスクリプトを追加する例:

extension String {
// 文字列を文字の配列に変換するメソッド
func toChars() -> [Character] {
Array(self)
}
// String.Index型を使わずに番号(0, 1, 2, ...)で文字を取得するサブスクリプト
subscript(index: Int) -> Character {
self[self.index(self.startIndex, offsetBy: index)]
}
}
// テスト
let s = "Hello, World!"
print(s.toChars())
for i in 0..<s.count {
print(s[i], terminator: " ")
}

これらの他にもextensionを使って、既存の型にネスト型を追加したり、既存の型をプロトコルに準拠させたりできます。

protocol Printable {
func print()
}
extension String: Printable {
// ネスト型の定義
enum PrintMode {
case nothing, toUpper, toLower
}
// ネスト型を使ったメソッド
func print(_ printMode: PrintMode) {
switch printMode {
case .toUpper:
Swift.print(self.uppercased())
case .toLower:
Swift.print(self.lowercased())
default:
self.print()
}
}
// プロトコルに準拠
func print() {
Swift.print(self)
}
}
"Abc".print()
"Abc".print(String.PrintMode.toLower)
"Abc".print(.toUpper) // 型推論で型名を省略

型が既に特定のプロトコルの要件を満たしているにも関わらず、そのプロトコルに準拠していることを宣言していない場合、空のextensionでプロトコルに準拠することができます。

struct Person {
var firstName: String = ""
var lastName: String = ""
func print() {
Swift.print("\(firstName) \(lastName)")
}
}
// PersonをPrintableプロトコルに準拠する
protocol Printable {
func print()
}
extension Person: Printable {}
var me: Printable = Person(firstName: "Taro", lastName: "Yamada")
me.print()

ジェネリクスの場合、where句を使って、特定の条件下でのみプロトコルの要件を満たすように拡張することができます。

protocol TextRepresentable {
var text: String { get }
}
extension String: TextRepresentable {
var text: String { return self }
}
// 条件付きでArray<Element>をTextRepresentableプロトコルに準拠する
extension Array: TextRepresentable where Element: TextRepresentable {
var text: String {
return self.map {$0.text}.joined(separator: ", ")
}
}
let a = ["a", "b", "c"]
print(a.text)
let b = [1, 2, 3]
print(b.text) // エラー: intはTextRepresentable準拠ではないため拡張されない

extensionでは格納プロパティを追加することができない (以下のエラー)ので、protocolextensionの組み合わせで抽象クラスを実現することはできないと思います。

Extensions must not contain stored properties

C++やC#、Javaのクラスや構造体のように、publicprivateなどでアクセス制御できます。

アクセスレベルの境界説明
モジュールコード配布の単位。importキーワードで別のモジュールからインポートできるフレームワークまたはアプリケーション。
ソースファイルモジュール内のSwiftのソースコードファイル。
パッケージ1つの単位として開発するモジュールのグループ。Package.swiftファイルでパッケージを定義して、XcodeのPackage Access Identifierでパッケージ名を指定する。

openが最も制限が緩く、privateは最も制限の厳しいアクセスレベルです。 デフォルトのアクセスレベルはinternalです。

アクセスレベル説明
public定義モジュールの任意のソースファイル、及び、定義モジュールをインポートする別のモジュールのソースファイルでも使用できる。
openpublicと同様だが、クラスとクラスメンバにのみ適用され、モジュール外のコードがサブクラス化やオーバーライドできる。
package定義されたパッケージのソースファイル内で使用できる。通常、複数のモジュールで構成されるアプリやフレームワークで使用する。
internalモジュールの任意のソースファイル内で使用できる。
fileprivate定義しているソースファイル内だけで使用できる。
privateその宣言を囲んでいる宣言と、同じファイル内にあるその宣言のextensionだけが使用できる。

別のブログ記事に書きましたが、アプリからインポートできるモジュールは、Xcodeのライブラリで作成できます。

他のプログラミング言語の例外処理(throwraiseで例外オブジェクトをスローして, try-catchtry-except構文で例外オブジェクトを捕捉する)と同様ですが、Swiftでは、列挙値でエラーをthrowして、do-catch構文でエラーを捕捉するか、try?またはtry!でエラーハンドリングします。


Errorプロトコルに準拠した列挙型でエラーを定義します。

enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int) // 関連値でエラーの詳細情報を付加する
case outOfStock
}

エラーを投げる関数・メソッドの定義

Section titled “エラーを投げる関数・メソッドの定義”

関数宣言・メソッド宣言の引数の後ろにthrowsを付けます。型付きスローthrows(列挙型名)を使うとthrowで型名を省略できます。

struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = ["キャンデー": Item(price: 100, count: 5)]
var coinsDeposited = 0
// throws をつけた関数
func vend(itemNamed name: String) throws { // 型名省略時は`throws(any Error)`と同じ
// 1. 商品がない場合
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
// 2. 在庫がない場合
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
// 3. お金が足りない場合
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
// 成功時の処理
coinsDeposited -= item.price
print("\(name)をどうぞ。")
}
}

エラーをスローし得る関数・メソッドは、tryを付けて呼び出します。

let vendingMachine = VendingMachine()
do {
vendingMachine.coinsDeposited = 80
try vendingMachine.vend(itemNamed: "キャンデー")
} catch VendingMachineError.invalidSelection {
print("商品はありません。")
} catch VendingMachineError.insufficientFunds(let coinNeeded) {
print("コインが\(coinNeeded)足りません。")
} catch VendingMachineError.outOfStock {
print("在庫がありません。")
} catch {
// `error`というローカル定数にエラーがバインドされる
print("予期しないエラーが発生しました: \(error)")
}

try?は、エラーからオプショナル値へ変換します。

try!は、予めエラーをスローしないと分かっている場合にエラーの伝搬を抑えます。

enum ValidationError : Error {
case outOfRange
}
func validate(_ n: Int) throws(ValidationError) -> Int {
if n < 1 || 100 < n {
throw .outOfRange
}
return n
}
let n1: Int? = try? validate(100)
let n2: Int? = try? validate(123)
let n3: Int? = try? validate(-100)
let n4: Int = try! validate(100)
let n5: Int = try! validate(123) // 実行時エラーが発生する

deferによる遅延アクションの指定

Section titled “deferによる遅延アクションの指定”

エラーのスロー、returnbreakなどに関わらず現在のコードブロックから離れるときのクリーンアップ処理はdefer文で定義でき、下から順に実行されます。

// エラー定義
enum FileError: Error {
case fileNotFound
case ioError
}
// ファイルのopen・read・closeを模倣するクラス
class FileSimulation {
var fileName: String
init(fileName: String) throws(FileError) {
print("\(fileName): FileSimulation.init")
if !["good.txt", "bad.txt"].contains(fileName) {throw .fileNotFound}
self.fileName = fileName
}
func read() throws(FileError) -> String {
print("FileSimulation.read")
if fileName == "bad.txt" {throw .ioError}
return "\(fileName): Hello, World!"
}
func close() {
print("\(fileName): FileSimulation.close")
}
}
// ファイルを2つ開いて読み込みを行う関数
func processFile() throws {
let file1 = try FileSimulation(fileName: "good.txt")
defer {
file1.close() // 2番目に実行される
}
let file2 = try FileSimulation(fileName: "bad.txt")
defer {
file2.close() // 1番目に実行される
}
let content1 = try file1.read()
print("読み込みデータは \(content1) です。")
let content2 = try file2.read()
print("読み込みデータは \(content2) です。")
}
do {
try processFile()
} catch {
print("エラー: \(error)")
}
// 実行結果:
// good.txt: FileSimulation.init
// bad.txt: FileSimulation.init
// FileSimulation.read
// 読み込みデータは good.txt: Hello, World! です。
// FileSimulation.read
// bad.txt: FileSimulation.close
// good.txt: FileSimulation.close
// エラー: ioError

値がない状態(nil)を型レベルで管理します。

// オプショナルな変数の定義(?)
var optionalString: String? = "Hello" // 初期値を省略するとnilになる
optionalString = nil // OK
assert(optionalString == nil) // 比較のときは == nil が使える
// 1. オプショナルバインディング(安全な取り出し)
if let optionalString { // `let safeString = optionalString`でもよい
print(optionalString)
} else {
print("値はnilです。")
}
// 条件式に複数のオプショナルバインディングとブール値をカンマ区切りで書ける
if let min = Int("123"), let max = Int("abc"), min < max {
print("最小値 \(min) と 最大値 \(max) は適切です。")
} else {
print("最小値・最大値は適切ではありません。")
}
// 2. nil結合演算子(??) (フォールバック値の提供)
let message = optionalString ?? "Default"
// 3. 強制アンラップ(!)
let possibleNumber = "12345"
let convertedNumber = Int(possibleNumber)
if convertedNumber != nil {
let number = convertedNumber! // nilの場合はランタイムエラーが発生する
print(number)
}
// 4. 暗黙アンラップオプショナル値
var assumedString: String! // `assumedString`の型は`String?`
assumedString = "文字列" // オプショナルに一度値が設定された後は必ず値が存在するという場合を想定
let forcedString: String = assumedString // 強制アンラップ(`!`)が不要

プロパティ・メソッド・サブスクリプトのオプショナル値がnilの場合、プロパティ・メソッド・サブスクリプトの呼び出しはnilを返します。Objective-Cのnilにメッセージを送信したときの挙動に似ていますが、Swiftでは参照型だけでなく値型のデータにも使えます。

// プロトコルの定義
protocol NodeRepresentable {
var title: String { get set }
var body: String { get set }
var parent: (any NodeRepresentable)? { get set }
var children: [any NodeRepresentable]? { get set }
var description: String { get }
subscript(index: Int) -> (any NodeRepresentable)? { get set }
func getChild(_ index: Int) -> (any NodeRepresentable)?
}
// ノードの定義
// NOTE: structでのオプショナルチェーンを試したかったので、
// Boxプロトコル型で無理やり実装している。
// classでNodeを定義すればプロトコルは不要になる。
struct Node: NodeRepresentable {
var title: String
var body: String
var parent: (any NodeRepresentable)?
var children: [any NodeRepresentable]?
init(title: String, body: String, children: [Node]? = nil) {
self.title = title
self.body = body
if var children {
for i in children.indices {
children[i].parent = self
}
self.children = children
} else {
self.children = nil
}
}
var description: String {
"[\(title)] \(body)"
}
subscript(index: Int) -> (any NodeRepresentable)? {
get {
// オプショナルチェーンでcontainsメソッドの戻り値(Bool)のオプショナル値を取得している
(children?.indices.contains(index) ?? false) ? children?[index] : nil
}
set {
if (children?.indices.contains(index) ?? false), let newValue {
children?[index] = newValue
}
}
}
func getChild(_ index: Int) -> (any NodeRepresentable)? {
self[index]
}
}
// 現在日時を取得する関数の定義
import Foundation
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
formatter.timeZone = TimeZone.current
func currentDateTime(_ number: Int) -> String {
let now = formatter.string(from: Date())
print("No.\(number): 現在時刻を取得しました: " + now)
return now
}
// nil確認関数
func check(_ number: Int, _ value: String?) {
print("No.\(number): \(value ?? "nil")")
}
// ツリーデータの作成
var root: Node =
Node(title: "ルート", body: "これはルートです。", children: [
Node(title: "子ノード0", body: "子ノード0です。"),
Node(title: "子ノード1", body: "子ノード1です。", children: [
Node(title: "孫ノード1-1", body: "孫ノード1-1です。"),
Node(title: "孫ノード1-2", body: "孫ノード1-2です。"),
]),
]
)
// テスト
check(10000, root[0]?.description)
check(10001, root.children?[1].description)
check(10011, root.getChild(1)?.description)
check(10002, root[2]?.description) // root[2]は存在しない
// オプショナルチェーンが失敗すると、`=`演算子の右辺式は評価されない
root[0]?.body = currentDateTime(20000)
root.children?[1].body = currentDateTime(20001)
root[2]?.body = currentDateTime(20002) // 右辺式の関数は呼び出されない
// オプショナルチェーンが複数階層になっても、descriptionの戻り値はString?として返される
check(30000, root[1]?[0]?.description)
check(30001, root[1]?.children?[1].description)
check(30011, root.getChild(1)?.getChild(1)?.description)
check(30002, root[1]?[2]?.description) // root[1][2]は存在しない
check(30003, root[2]?.children?[0].description) // root[2]は存在しない

算術演算子や比較演算子、文字列の加算演算子などは、概ねCやJava、JavaScriptなどの演算子と同じですが、算術演算ではオーバーフローできなくなっていたり、代入演算子(=)は値を返さないなど、異なっている部分があります。

var a: Int, b: Int
a = b = 1 // エラー: b = 1は値を返さない

変数が参照しているクラスの同じインスタンスが同一か(===)同一ではないか(!==)を確認します。

class X {
var value: Int = 0
}
let a = X()
let b = a
let c = X()
a === b
a !== c

Swiftの算術演算子はオーバーフローを発生しないようにしているため、オーバーフローするための演算子(&+, &-, &*)が用意されています。

var n = UInt8.max
n = n + 1 // エラー: オーバーフロー発生
n = n &+ 1 // オーバーフロー加算後、`0`になる

閉範囲演算子(a...b)、半開範囲演算子(a..<b)、片側範囲演算子(a..., ...a, ..<a)があります。

let a = Array(1...5) // [1, 2, 3, 4, 5]
let b = Array(0..<5) // [0, 1, 2, 3, 4]
let a1 = a[...2] // [1, 2, 3]
let a2 = a[2...] // [3, 4, 5]
let a3 = a[..<2] // [1, 2]
let range = ...5
assert(range.contains(0)) // 0は含まれる
assert(range.contains(-1)) // -1も含まれる
assert(!range.contains(7)) // 7は含まれない

Pythonと異なり、_ は変数名ではなく**「値を捨てる(Discard)ためのトークン」**です。読み取りはできません。

  1. 引数ラベルの省略: func f(_ x: Int)
  2. 戻り値の無視: _ = function()
  3. ループ変数の無視: for _ in 0..<3
  4. タプル分解時の無視: let (data, _) = result

どちらも条件がfalseの場合に実行時エラーとなりますが、 コンパイルオプション(-Onone, -O, -Ounchecked)に応じて動作が異なります。 -Ouncheckedでコンパイルすると、どちらも条件をチェックしなくなります。

let age = -3
assert(age >= 0, "年齢は0以上です。") // デバッグビルド(-Onone)の場合に条件をチェックする
precondition(age >= 0, "年齢は0以上です。") // デバッグビルドとリリースビルド(-O)の場合に条件をチェックする

既存の型(ソースコードを持たないライブラリの型含む)にメソッドやプロパティを追加できます。

extension Double {
var km: Double { return self * 1000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1000.0 }
}
let oneInch = 25.4.mm // ドット構文(`.`)で計算プロパティを使用できる

アクターは、並行コード間でデータを安全に共有することを可能にする参照型のデータ型です。

クラスと異なる点は、アクターの可変状態(mutable state)にアクセスできるのは一度に1つのタスクだけです。複数タスクが同一アクターとやり取りする場合でも、安全にアクセスできるようにします。

メインアクターは、UIで使用されるデータを保護し、画面表示やイベント処理などのUIに関連するコードを順番に実行します。 並行処理を使い始める前は、全てのコードはメインアクター上で実行されます。

関数やクロージャ、および、クラス・構造体・列挙型・プロトコルのメソッド・プロパティが常にメインアクター上でのみ実行されることを要求するには、@MainActor属性を記述します。

// メインアクターで実行される関数の定義
@MainActor func show(_ text: String) {
/* UI処理 */
}
// メインアクターで実行されるクロージャの定義 (クロージャの先頭に記述する)
let task = Task { @MainActor /* [キャプチャリスト] */ /* (引数) */ in
/* UI処理 */
}
// 構造体の全メソッド・全プロパティをメインアクター上で実行させる (クラス・列挙型・プロトコルも同様)
@MainActor
struct A {
/* UI関連のプロパティとメソッドを定義 */
}
// 構造体の特定メソッド・特定プロパティをメインアクター上で実行させる (クラス・列挙型・プロトコルも同様)
struct B {
@MainActor var values: [Int] = []
let constantText = "Hello world!"
@MainActor func show() {
/* UI処理 */
}
}

独自のアクターはactorで定義します。アクターはクラス同様に参照型です。

SafeCounter.swift
// 安全なカウンターのアクター
actor SafeCounter {
private var _count: Int
init(from value: Int) {
_count = value
}
var count: Int {
_count
}
func countUp() {
_count += 1
}
}
// 安全なカウンターのテスト
let safeCounter = SafeCounter(from: 0)
let workers = 1000, loop = 1000
await withTaskGroup(of: Void.self) { group in
for _ in 1...workers {
group.addTask {
for _ in 1...loop {
// アクターの外からメソッドやプロパティにアクセスするときは`await`を使用する
await safeCounter.countUp()
}
}
}
await group.waitForAll()
}
let result = await safeCounter.count
print("SafeCounter: \(workers) * \(loop) -> \(result)")

クラスで実装した場合、resultworkers * loopにならない場合があります。

UnsafeCounter.swift
// 安全ではないカウンターのクラス
class UnsafeCounter {
private var _count: Int
init(from value: Int) {
_count = value
}
var count: Int {
_count
}
func countUp() {
_count += 1
}
}
// 安全ではないカウンターのテスト
let unsafeCounter = UnsafeCounter(from: 0)
let workers = 1000, loop = 1000
await withTaskGroup(of: Void.self) { group in
for _ in 1...workers {
group.addTask {
for _ in 1...loop {
unsafeCounter.countUp()
}
}
}
await group.waitForAll()
}
let result = unsafeCounter.count
print("UnsafeCounter: \(workers) * \(loop) -> \(result)")

@globalActorSendableについては未稿です。


#から始まる自立型マクロと、@から始まる付属型マクロがあります。コンパイル時にマクロを展開してコードを追加します。

macro.swift
// ファイル名を展開するマクロ
print(#file)
// ファイルIDを展開するマクロ
print(#fileID)
// ファイルパス名を展開するマクロ
print(#filePath)
// 行番号・桁番号を展開するマクロ
print(#line, #column)
// 関数名を展開するマクロ
func testFunc() {
print(#function)
}
testFunc()
// コンパイル時の警告マクロ、エラーマクロ
#warning("コンパイル警告です")
#error("コンパイルエラーにします")

マクロの定義・実装については未稿です。