基礎
変数と定数
変更されない値にはデフォルトで let を使用し、変更が必要な場合のみ var に切り替えてください。Swift はコンパイル時に型を推論しますが、複雑な型や曖昧な型には明示的なアノテーションが可読性を向上させます。
var name = "Alice" // mutable
let age = 30 // immutable constant
let pi: Double = 3.14159 // explicit type
var count: Int = 0
count = 10
print(type(of: age)) // Intオプショナル
オプショナルは値の不在を表します。安全なアンラップに if-let を、デフォルト値に ?? を使用し、! は値が確実に存在すると確信できる場合のみ使用してください(リスクあり—クラッシュする可能性)。
var nickname: String? = nil
nickname = "Al"
if let n = nickname {
print("Hi, \(n)")
}
print(nickname ?? "Anonymous") // nil-coalescing
let force: String = nickname! // force unwrap (crashes if nil)タプル
タプルは複数の値を1つの複合値にグループ化します。関数から複数の値を返すのに有用です。名前付き要素は可読性を向上させます。
let person = (name: "Alice", age: 30)
print(person.name) // Alice
print(person.0) // Alice
let (n, a) = person
print("\(n), \(a)") // Alice, 30型変換
Swift は型間の暗黙的な変換を行いません。曖昧さを避け、微妙なバグを防ぐため、常に明示的な変換(例:Double(intVal))を使用してください。
let intVal = 42
let doubleVal = Double(intVal)
let strVal = String(intVal)
let fromStr = Int("100")! // 100
let invalid = Int("abc") // nil
print(doubleVal, strVal)アサーションと事前条件
デバッグチェックには assert を(リリースビルドで削除)、すべてのビルドでチェックされる重要な不変条件には precondition を使用してください。どちらも論理エラーの早期発見に役立ちます。
let age = -5
assert(age >= 0, "Age cannot be negative")
precondition(age >= 0, "Age must be non-negative")
// In debug builds, assert crashes if false
// precondition checks in release builds too文字列と文字
文字列補間
\(expr) を使った文字列補間は任意の式を文字列に埋め込みます。タイプセーフでコンパイル時に評価されるため、フォーマット文字列より安全です。
let name = "Alice"
let age = 30
let msg = "Name: \(name), Age: \(age)"
let calc = "5 + 3 = \(5 + 3)"
print(msg) // Name: Alice, Age: 30
print(calc) // 5 + 3 = 8一般的な文字列メソッド
Swift の文字列は Unicode 正しいです。つまり count はバイト数ではなく実際の文字(書記素クラスタ)を反映します。安全のため手動のインデックス作成の代わりにこれらのメソッドを使用してください。
let s = "Hello, World"
print(s.count) // 12
print(s.uppercased()) // HELLO, WORLD
print(s.lowercased()) // hello, world
print(s.hasPrefix("Hello")) // true
print(s.contains("World")) // true
print(s.reversed()) // "dlroW ,olleH"部分文字列とインデックス
文字列のインデックスは整数ではありません。文字のバイトサイズが異なる可能性があるためです。ナビゲーションには index(_:offsetBy:) を使用してください。部分文字列は元の文字列とメモリを共有します—長期保存には String に変換してください。
let s = "Hello, World"
let idx = s.index(s.startIndex, offsetBy: 7)
print(s[idx]) // W
let sub = s[idx...] // "World"
let newStr = String(sub) // convert to String
print(s.prefix(5)) // "Hello"複数行文字列
三重引用符文字列は改行とインデントを保持します。閉じる """ がベースラインインデントを決定します。HTML、JSON、長いテキストブロックに最適です。
let poem = """
Roses are red,
Violets are blue,
Swift is great,
And so are you.
"""
print(poem)
// Use \\(expr) for interpolation in multiline文字列の構築と分割
区切り文字で連結するには joined(separator:) を、トークン化するには split(separator:) を使用してください。これらは + 連結の手動ループより効率的です。
let parts = ["apple", "banana", "cherry"]
let joined = parts.joined(separator: ", ")
print(joined) // apple, banana, cherry
let csv = "a,b,c"
let fields = csv.split(separator: ",")
print(fields) // ["a", "b", "c"]
let reversed = String(s.reversed())データ構造
配列
配列は順序付きのゼロインデックスコレクションです。追加には append/insert を、変換には filter/map/reduce を使用します。スレッド安全性のため値型(Array は構造体)を優先してください。
var nums = [1, 2, 3]
nums.append(4)
nums.insert(0, at: 0)
nums[1] = 20
print(nums.count) // 5
print(nums.contains(20)) // true
let evens = nums.filter { $0 % 2 == 0 }
print(nums.first, nums.last)ディクショナリ
ディクショナリはキーと値のペアを O(1) の平均ルックアップで格納します。存在しないキーへのアクセスで nil を避けるため default: を使用してください。キーは Hashable である必要があります。
var ages: [String: Int] = ["Alice": 30]
ages["Bob"] = 25
ages["Alice"] = 31
print(ages["Alice", default: 0]) // 31
for (name, age) in ages {
print("\(name): \(age)")
}
print(ages.keys.sorted())セット
セットは O(1) のメンバーシップテストでユニークな値を格納します。重複排除とセット操作(和集合、積集合、差集合)に最適です。要素は Hashable である必要があります。
var a: Set<Int> = [1, 2, 3]
var b: Set<Int> = [3, 4, 5]
print(a.union(b)) // {1,2,3,4,5}
print(a.intersection(b)) // {3}
print(a.subtracting(b)) // {1,2}
print(a.isSubset(of: b)) // false
a.insert(6)範囲
..< は半開範囲(上限を含まない)、... は閉範囲(両方を含む)です。範囲はループ、スライシング、~= 演算子でのパターンマッチングに有用です。
for i in 0..<5 { print(i) } // 0,1,2,3,4
for i in 0...5 { print(i) } // 0,1,2,3,4,5
let nums = Array(1...5) // [1,2,3,4,5]
let prefix = nums.prefix(3) // [1,2,3]
if 5...10 ~= 7 { print("in range") }高階関数
map は各要素を変換、filter はマッチする要素を選択、reduce はすべてを1つに結合します。これらは Swift の関数型プログラミングの基盤であり、簡潔で読みやすいデータパイプラインを可能にします。
let nums = [1, 2, 3, 4, 5]
let doubled = nums.map { $0 * 2 } // [2,4,6,8,10]
let evens = nums.filter { $0 % 2 == 0 } // [2,4]
let sum = nums.reduce(0, +) // 15
let strs = nums.map { String($0) } // ["1","2",...]
print(doubled, evens, sum)制御フロー
If / Else If / Else
標準的な条件分岐です。条件はブール(Bool 型)である必要があります。Swift は Apple の goto fail 脆弱性のようなバグを防ぐため、単一文でも中括弧を要求します。
let score = 85
if score >= 90 {
print("A")
} else if score >= 80 {
print("B")
} else if score >= 70 {
print("C")
} else {
print("F")
}Switch(パターンマッチング)
Swift の switch は強力です:値バインディング、タプル、where ガード、範囲をサポートします。網羅的である必要があり(default が残りのケースをカバー)、デフォルトでフォールスルーしません。
let point = (2, 0)
switch point {
case (0, 0):
print("origin")
case (_, 0):
print("on x-axis")
case (0, _):
print("on y-axis")
case let (x, y) where x == y:
print("on diagonal")
default:
print("elsewhere: \(point.0),\(point.1)")
}For-In ループ
for-in は範囲、配列、ディクショナリ、任意の Sequence を反復します。インデックスが必要な場合は enumerated() を使用してください。使用しないループ変数は _ で無視できます。
for i in 0..<5 { print(i) }
let fruits = ["apple", "banana"]
for (i, fruit) in fruits.enumerated() {
print("\(i): \(fruit)")
}
let dict = ["a": 1, "b": 2]
for (k, v) in dict { print("\(k)=\(v)") }
for _ in 0..<3 { print("tick") }While と Repeat-While
while は各反復前に条件をチェックし、repeat-while は後にチェックします(C の do-while のようなもの)。本体を少なくとも1回実行する必要がある場合は repeat-while を使用してください。
var n = 5
while n > 0 {
print(n)
n -= 1
}
var x = 0
repeat {
x += 1
} while x < 3
print(x) // 3Guard(早期退出)
guard は前提条件が満たされない場合の早期退出を提供します。アンラップされたオプショナルはスコープの残りで利用可能で、ネストを減らします。深い if-let ネストより guard を優先してください。
func greet(_ name: String?) {
guard let n = name, !n.isEmpty else {
print("No name provided")
return
}
print("Hello, \(n)")
// n is unwrapped and available here
}
greet("Alice")
greet(nil)関数とクロージャ
関数定義
可読性のために引数ラベルを省略するには _ を使用します。デフォルトパラメータ値でパラメータをオプションにします。単一式の関数は暗黙的な return を持ちます(Swift 5.9 以降)。
func greet(_ name: String, greeting: String = "Hello") -> String {
return "\(greeting), \(name)!"
}
print(greet("Alice")) // Hello, Alice!
print(greet("Bob", greeting: "Hi")) // Hi, Bob!
func add(_ a: Int, _ b: Int) -> Int { a + b }
print(add(3, 4)) // 7複数の戻り値
タプルで複数の名前付き値を返せます。オプショナルタプルを返すことで失敗の可能性を示せます。r.min または r.0 でアクセス—名前付き要素の方が明確です。
func minMax(_ nums: [Int]) -> (min: Int, max: Int)? {
guard let first = nums.first else { return nil }
var mn = first, mx = first
for n in nums { mn = min(mn, n); mx = max(mx, n) }
return (mn, mx)
}
if let r = minMax([3, 1, 4, 1, 5]) {
print("min=\(r.min), max=\(r.max)")
}クロージャ
クロージャは自己完結した機能ブロックです。$0、$1 を省略引数名として使用します。後置クロージャ構文(最後の引数ラベルを省略)は map/filter/reduce で慣用的です。
let square: (Int) -> Int = { x in x * x }
print(square(5)) // 25
let add: (Int, Int) -> Int = { $0 + $1 }
print(add(3, 4)) // 7
let nums = [1, 2, 3]
let doubled = nums.map { $0 * 2 }
print(doubled) // [2, 4, 6]Escaping と Auto-Closure
@escaping は関数が戻った後に保存または呼び出されるクロージャをマークします(非同期コールバックに必要)。@autoclosure は式をクロージャでラップし、評価を遅延させます(assert で使用)。
var handlers: [() -> Void] = []
func saveHandler(_ fn: @escaping () -> Void) {
handlers.append(fn)
}
saveHandler { print("done") }
handlers.first?()
// @autoclosure delays evaluation
func logIfTrue(_ cond: @autoclosure () -> Bool) {
if cond() { print("true") }
}
logIfTrue(2 > 1)inout パラメータ
inout パラメータは関数が呼び出し側の変数を変更できるようにします(参照渡し)。明確さのため新しい値を返すことを優先し、控えめに使用してください。& プレフィックスが変更箇所を示します。
func swap(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 10, y = 20
swap(&x, &y)
print(x, y) // 20 10クラスと構造体
構造体(値型)
構造体は値型です—代入時にコピーされます。シンプルなデータコンテナに使用します。計算プロパティ(distance)はアクセス時に計算します。mutating メソッドが self を変更します。
struct Point {
var x: Double
var y: Double
var distance: Double {
(x * x + y * y).squareRoot()
}
mutating func moveBy(dx: Double, dy: Double) {
x += dx; y += dy
}
}
var p = Point(x: 3, y: 4)
print(p.distance) // 5.0クラス(参照型)
クラスは参照型です—参照経由で共有、継承と deinit をサポートします。アイデンティティ、共有された可変状態、または Objective-C 相互運用性が必要な場合に使用します。それ以外は構造体を優先してください。
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
deinit { print("\(name) deallocated") }
}
let p = Person(name: "Alice", age: 30)
print(p.name) // Alice継承とオーバーライド
スーパークラスのメソッドを再定義するには override を使用します。Swift は動的ディスパッチを使用するためサブクラスのバージョンが呼ばれます。パフォーマンスのためメソッドに final を付けてさらなるオーバーライドを防いでください。
class Animal {
func speak() { print("...") }
}
class Dog: Animal {
override func speak() { print("Woof!") }
}
class Cat: Animal {
override func speak() { print("Meow!") }
}
let pets: [Animal] = [Dog(), Cat()]
for pet in pets { pet.speak() }プロパティ(計算と遅延)
計算プロパティは get/set ブロックを持ち、set はデフォルトで newValue を使用します。lazy は初回アクセスまで初期化を遅延します—高コストや稀に必要なプロパティに有用です。var である必要があります。
class Circle {
var radius: Double
var area: Double {
get { .pi * radius * radius }
set { radius = (newValue / .pi).squareRoot() }
}
lazy var heavy = loadExpensiveData()
init(radius: Double) { self.radius = radius }
}
func loadExpensiveData() -> String { "data" }プロパティオブザーバー
willSet/didSet がプロパティの変更を監視します。didSet で値の検証や変更への反応(例:値のクランプ、UI 更新のトリガー)を行ってください。オブザーバーは init 中は発火しません。
class Counter {
var count: Int = 0 {
willSet { print("about to set \(newValue)") }
didSet {
print("was \(oldValue), now \(count)")
if count > 10 { count = 10 }
}
}
}
let c = Counter()
c.count = 5
c.count = 20 // clamped to 10プロトコルと拡張
プロトコル定義
プロトコルはメソッドとプロパティの設計図を定義します。型はそれらを実装することで準拠します。抽象化、ポリモーフィズム、疎結合に 使用します—Java/C# のインターフェースに似ています。
protocol Greetable {
var name: String { get }
func greet() -> String
}
struct User: Greetable {
let name: String
func greet() -> String { "Hi, \(name)" }
}
let u = User(name: "Alice")
print(u.greet()) // Hi, Aliceプロトコル拡張(デフォルト実装)
プロトコル拡張はデフォルト実装を提供します。これにより遡的モデリングと基底クラスなしのコード再利用が可能になります。無関係な型間で振る舞いを共有する強力な機能です。
protocol Describable {
func describe() -> String
}
extension Describable {
func describe() -> String { "A \(type(of: self))" }
}
struct Box: Describable { }
let b = Box()
print(b.describe()) // A Box拡張
拡張は既存の型(Int のような所有しない型も含む)に機能を追加します。コードの整理、計算プロパティの追加、プロトコルへの準拠に使用します。格納プロパティは追加できません。
extension Int {
var squared: Int { self * self }
func times(_ block: () -> Void) {
for _ in 0..<self { block() }
}
}
print(5.squared) // 25
3.times { print("hi") } // prints hi 3 timesジェネリクス
ジェネリクスは型安全性を保ちながら任意の型で動作する柔軟で再利用可能なコードを記述します。型パラメータに <T> を使用します。制約(where T: Equatable)で許可される型を制限します。
func stackOf<T>(_ items: T...) -> [T] {
var arr: [T] = []
for item in items { arr.append(item) }
return arr
}
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ e: Element) { items.append(e) }
}
let s = stackOf(1, 2, 3) // [Int]関連型を持つプロトコル
関連型によりプロトコルはプレースホルダー型を使用できます。プロトコル用のジェネリクスのようなものです。準拠する型が実際の型を指定します。明示的にするには typealias を使用するか、Swift に推論させます。
protocol Container {
associatedtype Item
var count: Int { get }
mutating func append(_ item: Item)
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
typealias Item = Int
private var items: [Int] = []
var count: Int { items.count }
mutating func append(_ item: Int) { items.append(item) }
subscript(i: Int) -> Int { items[i] }
}エラー処理
エラーの定義とスロー
エラーは Error プロトコルに準拠します(通常は列挙型)。throw がエラーを通知します。関連値(coinsNeeded )がコンテキストを運びます。失敗する可能性のある関数に throws をマークします。
enum VendingError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
func vend(item: String, coins: Int) throws -> String {
guard item == "Candy" else { throw VendingError.invalidSelection }
guard coins >= 2 else { throw VendingError.insufficientFunds(coinsNeeded: 2) }
return "Dispensing \(item)"
}Do-Catch と Try
do-catch がスローされたエラーを処理します。try がスローする呼び出しをマークします。対象を絞った処理のために特定のケースでパターンマッチします。予期しないエラーを処理する catch-all もあります。エラーはコールスタックを伝播します。
do {
let result = try vend(item: "Candy", coins: 1)
print(result)
} catch VendingError.insufficientFunds(let needed) {
print("Need \(needed) more coins")
} catch VendingError.outOfStock {
print("Sold out")
} catch {
print("Other error: \(error)")
}Try? / Try!
try? はエラーを nil に変換します(Optional を返します)。try! は成功を前提としエラー時にクラッシュします—失敗が本当に不可能な場合のみ使用してください。優雅な降格のために try? とオプショナルバインディングを優先してください。