Swift

目次

主要項目のみを表示しています。詳細な小見出しは本文内で確認できます。

概要

まず、この章の中心構造を図で確認します。細部に入る前に、どの概念がどこへつながるかをつかむための地図です。

flowchart LR A["Swift"] --> B["Optional"] A --> C["値型"] A --> D["protocol"] A --> E["async / await"] B --> F["安全性"] C --> G["予測しやすい状態"] D --> H["protocol-oriented design"] E --> I["並行処理"] F --> J["Appleプラットフォーム開発"] G --> J H --> J I --> J
コード例の読み方

コード例は、そのまま写すためだけのものではありません。直前の本文で「何を確かめる例か」を押さえ、直後の説明で「どの性質が見えるか」を確認してください。実務では、ここに入力の境界、失敗時の挙動、依存する実行環境を足して読むと判断しやすくなります。

要点

SwiftはAppleが開発した安全・高速・モダンな静的型付け言語で、iOS / macOS / watchOS / tvOS / visionOSの開発をはじめ、サーバサイドやLinuxでも動作します。Optional、protocol-oriented programming、value types、ARC、async/await、actorを中核に持ちます。

このページでは、Optional、value/reference型、protocol、ジェネリクス、ARC、Concurrency、SwiftUIを、Swiftらしい設計思想とともに整理します。


1. Swiftとは何か・なぜ生まれたか

このセクションでは「Swiftがなぜ生まれたのか」「Objective-Cとの関係はどうなっているのか」「Apple以外でも使えるのか」を整理します。

Swiftは 「安全・高速・モダンを掲げた静的型付け汎用言語」。2014年にAppleが発表し、2015年にオープンソース化されました。

Swift = Objective-Cの代替 + モダンな型システム + 関数型エッセンス + 安全性

1-1. Objective-CとAppleの事情

Appleのプラットフォーム(iOS / macOS)の開発言語は、長らく Objective-C でした。Objective-Cは1980年代にNeXT社(Steve Jobsが設立)が採用し、1990年代後半にAppleへNeXTSTEPとともに引き継がれた言語です。

Objective-Cの特徴と問題:

  • Cの上にSmalltalk風メッセージング [obj method:arg]
  • 動的型付け(実行時メソッド解決)
  • 構文が独特で学習コストが高い
  • nilチェックが手作業
  • 型システムが弱い(実行時クラッシュが多い)
  • 並行処理がGCD(Grand Central Dispatch)/ blocks中心

新人エンジニアにObjective-Cを教えるコスト」「型安全性の不足」「クラッシュ率の高さ」が長年の悩みでした。


1-2. Swiftの誕生(2010〜2014)

Appleのエンジニア Chris Lattner(LLVM / Clangの作者)が2010年から個人プロジェクトとして開発開始。2013年にApple社内プロジェクトに昇格し、2014年6月WWDC で発表されました。

2010  Chris Lattnerが個人プロジェクトとして開始
2013  Apple社内で本格開発
2014  Swift 1.0(WWDC 2014で発表)
2015  Swift 2 / オープンソース化(Apache 2.0)/ Linux移植
2016  Swift 3(API大幅見直し)
2017  Swift 4(Codable等)
2019  Swift 5(ABI安定化)
2021  Swift 5.5(async/await・actor)
2022  Swift 5.7(regex DSL、existential改善)
2023  Swift 5.9(macros、ownership)
2024  Swift 6.0(厳格な並行性チェック)

1-3. Swiftの設計目標

Swiftの公式な目標:

  1. Safe: 未定義動作なし、null安全(Optional)、メモリ安全
  2. Fast: C / C++ と同等の性能を目指す
  3. Expressive: モダンな構文(型推論、closures、generics、pattern matching)
  4. Interoperable: Objective-Cとの完全な相互運用

安全性を最優先しつつ、性能を犠牲にしない」というのがSwiftの哲学です。


1-4. Swiftが動く場所

Appleプラットフォーム:
  iOS / iPadOS / macOS / watchOS / tvOS / visionOS

その他:
  Linux (Ubuntu、CentOS等)
  Windows
  Server-side(Vapor、Hummingbird)
  WebAssembly(SwiftWasm)
  Embedded Swift(マイコン)

SwiftはApple専用言語ではなく、オープンソースのクロスプラットフォーム言語です。


1-5. このセクションのまとめ

- 2014年Appleが発表、Chris Lattner(LLVM作者)が設計
- 2015年オープンソース化、Linux対応
- 設計目標: Safe / Fast / Expressive / Interoperable
- Objective-Cと完全相互運用
- Apple専用ではない、Server-side / Linux / WASMでも使える

2. 環境構築とツールチェイン

2-1. インストール方法

Appleプラットフォーム

# Xcode(macOS、App Storeまたはdeveloper.apple.comから)
xcode-select --install

XcodeにSwiftコンパイラ・標準ライブラリ・iOS SDKが同梱。

Linux / 他

# Swift.orgからダウンロード
curl -O https://download.swift.org/swift-5.9.2-release/ubuntu2204/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04.tar.gz

# Swiftly(バージョン管理ツール)
curl -L https://swift-server.github.io/swiftly/swiftly-install.sh | bash
swiftly install latest
swiftly use latest

2-2. 主要コマンド

swift --version                  # バージョン確認
swift                             # REPL
swift run                         # SPMプロジェクト実行
swift build                       # ビルド
swift test                        # テスト
swift package init                # 新規パッケージ
swift package init --type executable
swift package update              # 依存更新
swift package resolve

# 単独ファイル
swiftc main.swift -o app          # コンパイル
swift main.swift                   # スクリプト実行

2-3. プロジェクト構成(Swift Package Manager)

MyApp/
├── Package.swift              # マニフェスト
├── Sources/
│   └── MyApp/
│       ├── main.swift
│       └── helper.swift
├── Tests/
│   └── MyAppTests/
│       └── MyAppTests.swift
└── .build/                     # 生成物

Package.swiftの例

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [.macOS(.v13)],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"),
    ],
    targets: [
        .executableTarget(
            name: "MyApp",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
            ]
        ),
        .testTarget(
            name: "MyAppTests",
            dependencies: ["MyApp"]
        ),
    ]
)

2-4. このセクションのまとめ

- Apple: Xcode一択
- Linux/Windows: Swift.orgの公式バイナリ
- Swift Package Manager (SPM) が標準のパッケージ管理
- swift run / build / test / package

3. 基本構文と型システム

3-1. Hello World

print("Hello, World!")

Swiftはトップレベルでコードが書ける(main 関数不要)。スクリプト感覚で動かせます。


3-2. 変数:letとvar

let x = 10           // 不変(再代入不可)、推奨
var y = 20           // 可変
y = 30               // OK

let z: Int = 100     // 型を明示
let pi: Double = 3.14

x = 100              // エラー!letは再代入不可

letデフォルトで使うのがSwiftの文化(Kotlinの val と同じ哲学)。


3-3. 基本型

説明
Int 符号付き整数(プラットフォーム依存、通常64bit)
Int8 Int16 Int32 Int64 サイズ固定
UInt 符号なし
Float Double 浮動小数点
Bool true / false
Character 1文字(Unicodeのgrapheme cluster)
String 文字列(値型)
Array<T> [T] 配列
Set<T> 集合
Dictionary<K, V> [K: V] 辞書
Tuple (Int, String)
Optional<T> T? 値があるかも

3-4. 型推論

let n = 42              // Int
let pi = 3.14           // Double
let s = "hello"         // String
let arr = [1, 2, 3]     // [Int]

Swiftの型推論は強力多くの場面で型注釈は省略可能


3-5. 暗黙の型変換は禁止

let n: Int = 10
let d: Double = 3.14
let sum = n + d     // エラー!

let sum = Double(n) + d     // OK

Kotlinと同じく 明示変換のみ


3-6. 文字列補間

let name = "Alice"
let age = 30

let msg = "Hello, \(name)! You are \(age)."

// 複数行
let text = """
    Hello,
    \(name)!
    """

\(expression) で補間。複数行は """..."""


3-7. このセクションのまとめ

- let(不変)/ var(可変)、letをデフォルトに
- 強力な型推論、明示変換が必須
- "\(expr)" で文字列補間
- """..."""で複数行

4. Optionalとエラーハンドリング

4-1. Optionalの基本

var name: String = "Alice"
name = nil            // エラー!Stringはnilを許さない

var nullable: String? = "Alice"
nullable = nil        // OK

Kotlinと同じく 型レベルでnullを区別String? は実質 Optional<String> というジェネリックenum。

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

4-2. unwrapの方法

let s: String? = "hello"

// 1. if let(推奨)
if let s = s {
    print(s.count)        // sはString型
}

// Swift 5.7+ の短縮形
if let s {
    print(s.count)
}

// 2. guard let(早期リターン)
guard let s = s else {
    return
}
print(s.count)             // ここでsはString

// 3. nil合体演算子
let length = s?.count ?? 0

// 4. forced unwrap(危険)
let length = s!.count       // sがnilならcrash

// 5. optional chaining
let upper = s?.uppercased()

4-3. Implicit unwrap

let s: String! = "hello"   // 内部はOptionalだが ! なしでも使える
print(s.count)              // OKだがnilならcrash

Storyboardから接続する @IBOutlet などで使われる。普通は使わない


4-4. このセクションのまとめ

- T? がOptional<T> の糖衣
- if let / guard letで安全にunwrap
- ?. でチェイン、?? でデフォルト値
- ! は強制unwrap(危険)

5. structとclass(値型と参照型)

5-1. struct(値型)

struct Point {
    var x: Double
    var y: Double
}

var p = Point(x: 1, y: 2)
var p2 = p           // コピー!
p2.x = 100
print(p.x)           // 1(変わらない)

struct値型。代入や関数引数でコピーされます。


5-2. class(参照型)

class Person {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let p = Person(name: "Alice", age: 30)
let p2 = p           // 参照を共有
p2.name = "Bob"
print(p.name)        // "Bob"(同じインスタンス)

class参照型。Java / C# のクラスと同じ。


5-3. struct vs classの選び方

structを選ぶ:
  - データを表現する(座標、色、設定)
  - 値の同一性が重要
  - 不変性を保ちたい
  - スレッド安全性

classを選ぶ:
  - アイデンティティが重要(シングルトン、UI Component)
  - 共有状態が必要
  - 継承が必要
  - Objective-C相互運用

Appleの公式ガイドラインは「まずstruct」。「Value Types First」がSwiftの哲学。


5-4. structとmutating

struct Counter {
    var count = 0
    
    mutating func increment() {     // mutating必須
        count += 1
    }
}

var c = Counter()
c.increment()
print(c.count)        // 1

let c2 = Counter()
c2.increment()        // エラー!letは変更不可

structのメソッドが自分自身を変更するなら mutating キーワード必須。これにより「変更しないメソッド」がデフォルトに。


5-5. classの継承

class Animal {
    var name: String
    init(name: String) { self.name = name }
    func speak() -> String { "..." }
}

class Dog: Animal {
    override func speak() -> String { "Woof!" }
}

let d = Dog(name: "Rex")
print(d.speak())     // "Woof!"

class Dog: Animal { ... } で継承。override キーワード必須。


5-6. final

final class Sealed {
    // これ以上継承できない
}

final で継承禁止。性能最適化にも効果(virtual dispatchを避ける)。


5-7. このセクションのまとめ

- struct: 値型、コピー、デフォルト不変
- class: 参照型、共有、継承可能
- structを優先(Value Types First)
- mutatingで値型のメソッドが変更可能
- overrideで多態性、finalで継承禁止

6. enumとassociated value

Swiftのenumは タグ付き共用体(ADT) の機能を持ち、極めて強力です。

6-1. 基本

enum Direction {
    case north
    case south
    case east
    case west
}

let d: Direction = .north

switch d {
case .north: print("N")
case .south: print("S")
case .east, .west: print("E or W")
}

switch網羅性チェック が言語レベル。


6-2. raw value

enum HTTPStatus: Int {
    case ok = 200
    case notFound = 404
    case serverError = 500
}

HTTPStatus.ok.rawValue        // 200
HTTPStatus(rawValue: 200)     // Optional(.ok)

6-3. associated value

enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)
}

let r: Result<Int, Error> = .success(42)

switch r {
case .success(let value): print("got \(value)")
case .failure(let error): print("error: \(error)")
}

各ケースが 異なる値を持てる。これがRust / OCaml / Haskellの代数的データ型に相当。


6-4. enum + メソッド

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case triangle(base: Double, height: Double)
    
    var area: Double {
        switch self {
        case .circle(let r): return .pi * r * r
        case .rectangle(let w, let h): return w * h
        case .triangle(let b, let h): return 0.5 * b * h
        }
    }
}

let s: Shape = .circle(radius: 5)
print(s.area)

6-5. このセクションのまとめ

- enumで網羅性チェック付き分岐
- raw valueでInt/Stringと紐付け
- associated valueで各ケースが値を持つ
- メソッドや計算プロパティを持てる
- Optional / Resultも実はenum

7. プロトコル指向プログラミング

Swiftの中核哲学「Protocol-Oriented Programming(POP)」。Apple WWDC 2015のキーノートでCrustyが説いた思想。

7-1. protocolの基本

protocol Animal {
    var name: String { get }
    func speak() -> String
}

struct Dog: Animal {
    let name: String
    func speak() -> String { "Woof!" }
}

let d: Animal = Dog(name: "Rex")
print(d.speak())

Java / Kotlinの interface 相当ですが、classに限らずstruct / enumにも適用できます。


7-2. protocol extension(デフォルト実装)

protocol Greetable {
    var name: String { get }
}

extension Greetable {
    func greet() -> String {
        return "Hi, \(name)!"
    }
}

struct Person: Greetable {
    let name: String
}

Person(name: "Alice").greet()    // "Hi, Alice!"

プロトコルにデフォルト実装を追加」できる。これがPOPの中核。


7-3. protocol composition

protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }

func describe(_ entity: Named & Aged) {
    print("\(entity.name) (\(entity.age))")
}

A & B で複数プロトコルを満たす型を表現。


7-4. associatedtype

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

ジェネリックなプロトコル。Javaの interface Foo<T> に近い。


7-5. some / any(Swift 5.7+)

// some: 単一の具体型を表す(コンパイル時決定)
func makeAnimal() -> some Animal { Dog(name: "Rex") }

// any: ボックス化されたexistential(実行時決定)
let animals: [any Animal] = [Dog(name: "Rex"), Cat(name: "Mike")]

some は性能が良い(型消去なし)、any は柔軟(同じ配列に異なる実装を入れられる)。


7-6. このセクションのまとめ

- protocolで契約を定義(class/struct/enumすべてに適用)
- protocol extensionでデフォルト実装
- associatedtypeでジェネリック契約
- some / any(Swift 5.7+)でexistential型を明示
- 「Protocol-Oriented Programming」がSwift文化

8. ジェネリクス

8-1. ジェネリック関数

func swap<T>(_ a: inout T, _ b: inout T) {
    let tmp = a
    a = b
    b = tmp
}

var x = 1, y = 2
swap(&x, &y)

8-2. ジェネリック型

struct Stack<T> {
    private var items: [T] = []
    
    mutating func push(_ item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T? {
        return items.popLast()
    }
}

var s = Stack<Int>()
s.push(1)
s.push(2)
print(s.pop())       // Optional(2)

8-3. 制約

func max<T: Comparable>(_ a: T, _ b: T) -> T {
    return a > b ? a : b
}

func process<T>(_ value: T) where T: Numeric, T: Hashable { ... }

T: Comparable で制約、where 句で複雑な制約。


8-4. opaque return type(some)

func makeCollection() -> some Collection {
    return [1, 2, 3]
}

何らかのCollectionを返す」と表明し、具体型は隠す。SwiftUIで多用。


8-5. このセクションのまとめ

- func / struct / enumで <T> ジェネリクス
- T: Protocol / where句で制約
- some Typeで具体型を隠す(コンパイル時単一)
- any Typeでexistential(実行時可変)

9. クロージャ

9-1. クロージャの基本

let add = { (a: Int, b: Int) -> Int in
    return a + b
}
add(1, 2)     // 3

// 型推論
let add: (Int, Int) -> Int = { a, b in a + b }

// $0, $1で引数を参照
let add: (Int, Int) -> Int = { $0 + $1 }

9-2. trailing closure

最後の引数がクロージャの場合、括弧の外に出せる。

[1, 2, 3].map { $0 * 2 }      // [2, 4, 6]
[1, 2, 3].filter { $0 > 1 }   // [2, 3]

// 複数trailing closure(Swift 5.3+)
view.animate {
    self.opacity = 0
} completion: { _ in
    self.removeFromSuperview()
}

9-3. キャプチャ

func makeCounter() -> () -> Int {
    var count = 0
    return {
        count += 1
        return count
    }
}

let counter = makeCounter()
counter()    // 1
counter()    // 2

クロージャは 外側の変数をキャプチャ


9-4. キャプチャリスト

class ViewController {
    var name = "default"
    
    func setup() {
        button.action = { [weak self] in
            self?.handle()
        }
        
        button.action = { [unowned self] in
            self.handle()
        }
    }
}

[weak self] / [unowned self]強参照を避ける(メモリリーク対策)。


9-5. @escapingと @autoclosure

func runLater(_ work: @escaping () -> Void) {
    DispatchQueue.global().async {
        work()       // 関数を抜けてから実行されるので @escaping
    }
}

func assert(_ condition: @autoclosure () -> Bool) {
    if !condition() { fatalError() }
}

assert(x > 0)    // 自動的に { x > 0 } のクロージャに

@escaping: 関数のスコープを超えて生きるクロージャに必要。
@autoclosure: 引数を自動的にクロージャでラップ。


9-6. このセクションのまとめ

- { (args) -> Type in ... } または { args in ... }
- $0, $1で引数省略
- trailing closureで外に出せる
- [weak self] / [unowned self] でキャプチャ管理
- @escaping / @autoclosure

10. プロパティとproperty wrapper

10-1. stored / computedプロパティ

struct Rectangle {
    var width: Double
    var height: Double
    
    var area: Double {           // 計算プロパティ
        return width * height
    }
    
    var perimeter: Double {
        get { 2 * (width + height) }
        set { width = newValue / 4; height = newValue / 4 }
    }
}

10-2. willSet / didSet(observer)

class Counter {
    var count: Int = 0 {
        willSet { print("will change to \(newValue)") }
        didSet { print("changed from \(oldValue)") }
    }
}

10-3. lazy

class Heavy {
    lazy var data: [Int] = {
        // 初回アクセス時に計算
        return loadFromDatabase()
    }()
}

最初に使うときに初期化」する遅延プロパティ。


10-4. property wrapper

@propertyWrapper
struct Clamped<Value: Comparable> {
    private var value: Value
    let range: ClosedRange<Value>
    
    init(wrappedValue: Value, _ range: ClosedRange<Value>) {
        self.value = wrappedValue
        self.range = range
    }
    
    var wrappedValue: Value {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Player {
    @Clamped(0...100) var health = 100
}

var p = Player()
p.health = 200       // 100にクランプ
print(p.health)       // 100

プロパティのアクセスをカスタマイズ」する仕組み。SwiftUIの @State@Binding@Published などはすべてproperty wrapper。


10-5. このセクションのまとめ

- stored(普通の格納)/ computed(計算)プロパティ
- willSet/didSetで変更前後のフック
- lazyで遅延初期化
- @propertyWrapperでアクセスをカスタマイズ
- SwiftUIの @Stateなどの基盤

11. extensionとprotocol extension

11-1. extension(既存型の拡張)

extension Int {
    var isEven: Bool { self % 2 == 0 }
    func squared() -> Int { self * self }
}

5.squared()    // 25
4.isEven       // true

Swiftでは 既存の型に後付けでメソッドを追加できます。Kotlinの拡張関数より深く、プロパティやprotocol適合も追加可能


11-2. extensionでprotocol適合

protocol JSONConvertible {
    func toJSON() -> String
}

extension Int: JSONConvertible {
    func toJSON() -> String { String(self) }
}

extension String: JSONConvertible {
    func toJSON() -> String { "\"\(self)\"" }
}

既存の型に後からprotocolを実装」できる。Rustのtrait implやKotlinの拡張関数より柔軟。


11-3. protocol extension(デフォルト実装)

protocol Identifiable {
    var id: String { get }
}

extension Identifiable {
    var description: String {
        return "ID: \(id)"
    }
}

protocolにもデフォルト実装追加可能。


11-4. このセクションのまとめ

- extensionで既存型に後付け追加
- メソッド・プロパティ・protocol適合・initを追加可能
- protocol extensionでデフォルト実装
- POPの中核機能

12. 制御フローとパターンマッチング

12-1. if / guard

if x > 0 {
    print("positive")
} else if x < 0 {
    print("negative")
} else {
    print("zero")
}

// guard(早期return)
func process(value: Int?) {
    guard let v = value, v > 0 else {
        return
    }
    // ここでvは安全に使える
}

guard は「前提条件を確認、満たさなければ抜ける」専用構文。ネストを浅く保つのに有効。


12-2. switch(強力)

switch value {
case 0:
    print("zero")
case 1...10:                  // 範囲
    print("small")
case let x where x > 100:     // ガード
    print("big: \(x)")
case (1, _):                   // タプル + ワイルドカード
    print("first is 1")
default:
    print("other")
}

Javaのswitchとは別物。範囲、タプル、where句、enumのassociated value分解などが書けます。


12-3. switchでenumを分解

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}

func describe(_ shape: Shape) -> String {
    switch shape {
    case .circle(let r):
        return "Circle r=\(r)"
    case .rectangle(let w, let h) where w == h:
        return "Square \(w)"
    case .rectangle(let w, let h):
        return "Rectangle \(w)x\(h)"
    }
}

12-4. for / while / repeat

for i in 1...10 { print(i) }              // 1〜10 inclusive
for i in 1..<10 { print(i) }              // 1〜9
for (index, value) in arr.enumerated() { print(index, value) }
for (key, value) in dict { print(key, value) }

while cond { ... }
repeat { ... } while cond                  // do-while相当

12-5. このセクションのまとめ

- if / guard / switch
- guardで前提チェック + 早期リターン
- switchは範囲・タプル・enum分解・where句で強力
- for ... in 1...10 / 1..<10 / arr / dict

13. コレクションとSequence

13-1. Array

var arr = [1, 2, 3]
arr.append(4)
arr += [5, 6]
arr[0]                        // 1
arr.count                     // 6
arr.isEmpty                    // false
arr.first                     // Optional(1)
arr.last                      // Optional(6)

// 高階関数
arr.map { $0 * 2 }            // [2, 4, ...]
arr.filter { $0 > 2 }
arr.reduce(0, +)              // 21
arr.contains(3)
arr.sorted()
arr.sorted { $0 > $1 }        // 降順

13-2. Dictionary

var d: [String: Int] = ["a": 1, "b": 2]
d["c"] = 3
d["a"]                        // Optional(1)
d["x", default: 0]            // 0(デフォルト)
d.count

for (key, value) in d {
    print("\(key)=\(value)")
}

13-3. Set

var s: Set<Int> = [1, 2, 3]
s.insert(4)
s.contains(2)
let union = s.union([3, 4, 5])
let intersection = s.intersection([2, 3])

13-4. SequenceとLazySequence

let result = (1...100)
    .lazy                      // 遅延評価
    .filter { $0 % 2 == 0 }
    .map { $0 * $0 }
    .prefix(5)

.lazy必要分だけ計算


13-5. このセクションのまとめ

- [T] / [K: V] / Set<T>
- map / filter / reduce / sortedなどの高階関数
- .lazyで遅延評価
- Sequenceプロトコルで自作も可能

14. 文字列とUnicode

Swiftの String「Unicodeに厳密」な設計。

let s = "Hello, 世界!"
s.count                        // 9(grapheme cluster)

for char in s {
    print(char)                // Character(grapheme cluster)
}

// インデックス
let i = s.index(s.startIndex, offsetBy: 7)
s[i]                           // "世"

// 異なるUnicode表現
let cafe1 = "café"             // U+00E9
let cafe2 = "cafe\u{0301}"     // e + combining acute
cafe1 == cafe2                 // true(正規化)

人間が見て同じ文字なら同じ」を保証するため、Stringインデックスは整数ではなく String.Index 型です。これは性能の対価ですが、絵文字や合成文字を正しく扱える堅牢さを得ています。


15. メモリ管理(ARC)

Swiftは ARC(Automatic Reference Counting) でメモリを管理します。GCではなく 参照カウント方式。

15-1. 参照カウント

class Person {
    let name: String
    init(name: String) { self.name = name; print("init") }
    deinit { print("deinit") }
}

var p1: Person? = Person(name: "Alice")    // count = 1
var p2 = p1                                  // count = 2
p1 = nil                                     // count = 1
p2 = nil                                     // count = 0 → deinit

参照が0になった時点で deterministicに解放


15-2. 強参照サイクル

class A { var b: B? }
class B { var a: A? }

var a: A? = A()
var b: B? = B()
a?.b = b
b?.a = a
a = nil           // Aはまだbから参照されている
b = nil           // どちらも解放されない!リーク

互いに強参照すると 解放されない


15-3. weakとunowned

class Parent {
    var child: Child?
}

class Child {
    weak var parent: Parent?    // 弱参照(カウントを増やさない)
}

class Item {
    unowned let owner: Owner    // 必ず存在する想定の弱参照
}

weak: nilになりうる弱参照(Optional)
unowned: nilにならない想定の弱参照(Optionalではない、nilならcrash)

親→子は強参照、子→親はweak」が定石。


15-4. クロージャでのキャプチャ

class ViewController {
    var name = "default"
    
    func setup() {
        button.action = { [weak self] in
            self?.handle()        // weak self
        }
    }
}

クロージャが self を強参照すると循環。[weak self] で回避。


15-5. このセクションのまとめ

- ARCで参照カウント方式
- deterministicな解放
- 強参照サイクルに注意
- weak: Optionalな弱参照、unowned: 必ず存在する弱参照
- クロージャは [weak self] / [unowned self]

16. 並行処理(async/await・actor・Task)

Swift 5.5(2021年)で導入された structured concurrency

16-1. async / await

func fetchUser(id: Int) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

Task {
    do {
        let user = try await fetchUser(id: 1)
        print(user)
    } catch {
        print(error)
    }
}

C# / TypeScriptのasync/awaitに近いが、throwsと組み合わせたtry/await


16-2. async let(並行実行)

func fetchAll() async throws -> (User, [Order]) {
    async let user = fetchUser(id: 1)
    async let orders = fetchOrders(userId: 1)
    return try await (user, orders)              // 両方並行に取得
}

16-3. TaskGroup(動的並行)

func fetchMany(ids: [Int]) async throws -> [User] {
    return try await withThrowingTaskGroup(of: User.self) { group in
        for id in ids {
            group.addTask { try await fetchUser(id: id) }
        }
        var users: [User] = []
        for try await user in group {
            users.append(user)
        }
        return users
    }
}

16-4. actor(データ競合を防ぐ)

actor BankAccount {
    private var balance: Decimal = 0
    
    func deposit(_ amount: Decimal) {
        balance += amount
    }
    
    func withdraw(_ amount: Decimal) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
}

let account = BankAccount()
Task {
    await account.deposit(100)        // awaitが必要
    let ok = await account.withdraw(50)
}

actor「内部状態を1つのタスクからしか操作できない」型。データ競合をコンパイル時に防ぐ。


16-5. Sendable

struct UserID: Sendable {
    let value: Int
}

actor Cache {
    func store(_ id: UserID) { ... }    // Sendableな値だけ受け渡せる
}

Sendableスレッド安全な型のマーカー。Swift 6ではより厳格に検査。


16-6. MainActor

@MainActor
class ViewModel: ObservableObject {
    @Published var users: [User] = []
    
    func load() async {
        users = try await fetchAll()    // 自動的にmain threadで実行
    }
}

UI更新をmainスレッドに固定。


16-7. このセクションのまとめ

- async / await + throws
- async letで並行取得
- TaskGroupで動的並行
- actorでデータ競合防止
- Sendableでスレッド安全性
- @MainActorでUIスレッド

17. Resultとthrows

17-1. throws / try / catch

enum FileError: Error {
    case notFound
    case permission
}

func read(path: String) throws -> String {
    if !exists(path) { throw FileError.notFound }
    return contents
}

do {
    let s = try read(path: "data.txt")
} catch FileError.notFound {
    print("not found")
} catch {
    print("other: \(error)")
}

Javaのchecked exceptionに近いが、throws 注釈は強制ではない(エラー型の型システムには入っていない、エラーは Error プロトコルなら何でも)。


17-2. try? / try!

let s: String? = try? read(path: "data.txt")    // エラー時nil
let s: String = try! read(path: "data.txt")     // エラー時crash

17-3. Result型

let result: Result<String, FileError> = .success("hello")

switch result {
case .success(let value): print(value)
case .failure(let error): print(error)
}

// クロージャの戻り値として
func fetch(completion: (Result<User, Error>) -> Void) { ... }

非同期コールバック(async/await以前)で頻出。


17-4. このセクションのまとめ

- throws / try / catch
- try? でOptionalに、try! で強制実行
- Result<Success, Failure> で値として扱う
- async/awaitでもtry awaitを使う

18. SwiftUI

宣言的UIフレームワーク。ReactやComposeに近い。

18-1. 基本

import SwiftUI

struct ContentView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
                .font(.largeTitle)
            Button("Increment") {
                count += 1
            }
        }
        .padding()
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

@State は状態、var body: some View でビュー定義。some View のopaque typeがSwiftUIの核心。


18-2. 主要property wrapper

@Stateローカル状態(View内)
@Binding親から借用した状態
@StateObject     ObservableObjectの所有
@ObservedObject外部ObservableObject参照
@EnvironmentObject環境注入
@Environment     SwiftUI環境値(カラースキーム等)
@Published       ObservableObject内のプロパティ

18-3. ObservableObject + @Published

class CounterViewModel: ObservableObject {
    @Published var count = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var vm = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("\(vm.count)")
            Button("+") { vm.increment() }
        }
    }
}

18-4. レイアウト

VStack { /* 縦 */ }
HStack { /* 横 */ }
ZStack { /* 重ね */ }
LazyVStack { /* 必要に応じてレンダー */ }
ScrollView { ... }
List(items) { item in ... }
NavigationStack { ... }
TabView { ... }

18-5. このセクションのまとめ

- 宣言的UI(React / Composeに近い)
- @State / @Binding / @StateObject / @ObservedObject
- VStack / HStack / ZStack / List / NavigationStack
- iOS / macOS / watchOS / tvOS / visionOSで統一

19. Foundationと標準ライブラリ

19-1. 主要型

import Foundation

// 日付
let now = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.string(from: now)

// URL
let url = URL(string: "https://example.com")!

// Data(バイト列)
let data = "hello".data(using: .utf8)!

// JSON
let user = User(name: "Alice")
let json = try JSONEncoder().encode(user)
let decoded = try JSONDecoder().decode(User.self, from: json)

// FileManager
let fm = FileManager.default
let docs = fm.urls(for: .documentDirectory, in: .userDomainMask)[0]

19-2. Codable

struct User: Codable {
    let name: String
    let age: Int
}

let user = User(name: "Alice", age: 30)
let json = try JSONEncoder().encode(user)
let str = String(data: json, encoding: .utf8)!
print(str)        // {"name":"Alice","age":30}

let decoded = try JSONDecoder().decode(User.self, from: json)

Codable 適合だけでJSON / plist / 任意のシリアライザに対応。Swift 4で導入された強力な機能。


19-3. このセクションのまとめ

- Foundation: Date / URL / Data / FileManager等
- CodableでJSON等の双方向シリアライズ
- 日付・国際化・正規表現が標準

20. Swift Package Manager

20-1. パッケージ追加

// Package.swift
dependencies: [
    .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"),
    .package(url: "https://github.com/vapor/vapor", from: "4.0.0"),
],
swift package update
swift package resolve

20-2. 自作パッケージ

swift package init --type library
swift package init --type executable
// Package.swift
let package = Package(
    name: "MyLib",
    products: [
        .library(name: "MyLib", targets: ["MyLib"]),
    ],
    targets: [
        .target(name: "MyLib"),
        .testTarget(name: "MyLibTests", dependencies: ["MyLib"]),
    ]
)

20-3. このセクションのまとめ

- Package.swiftで依存・ターゲット定義
- swift package initで雛形
- swift build / test / run
- GitHub URLで他者のパッケージを利用

21. Objective-Cとの相互運用

21-1. SwiftからObj-Cを呼ぶ

Bridging-Header.h:

#import "MyObjCClass.h"

Swift側:

let obj = MyObjCClass()
obj.doSomething()

21-2. Obj-CからSwiftを呼ぶ

@objc public class MyClass: NSObject {
    @objc public func greet() -> String { "hi" }
}

@objc を付けるとObjective-Cから見える。


21-3. このセクションのまとめ

- 双方向の相互運用が可能
- @objc / NSObject継承でObj-Cに公開
- Bridging-HeaderでObj-Cヘッダ取り込み
- iOSの旧コードとの共存

22. テスト戦略(XCTest / Swift Testing)

22-1. XCTest

import XCTest
@testable import MyApp

class CalculatorTests: XCTestCase {
    var calc: Calculator!
    
    override func setUp() {
        calc = Calculator()
    }
    
    func testAdd() {
        XCTAssertEqual(calc.add(1, 2), 3)
    }
    
    func testThrows() {
        XCTAssertThrowsError(try calc.divide(1, by: 0))
    }
    
    func testAsync() async throws {
        let result = try await fetchData()
        XCTAssertNotNil(result)
    }
}

22-2. Swift Testing(新標準、Swift 6+)

import Testing

@Test func add() {
    #expect(1 + 2 == 3)
}

@Test("Division by zero")
func divisionByZero() throws {
    #expect(throws: DivideError.self) {
        try divide(1, by: 0)
    }
}

@Test(arguments: [(1, 1, 2), (2, 3, 5)])
func parameterized(a: Int, b: Int, expected: Int) {
    #expect(a + b == expected)
}

XCTestより モダンで簡潔。Swift 6で標準化される予定。


22-3. このセクションのまとめ

- XCTest(伝統)とSwift Testing(新)
- @Test / #expect / argumentsパラメータ化
- asyncテストもサポート

23. Swift on Server / Linux

23-1. Vapor

import Vapor

let app = Application(.development)
defer { app.shutdown() }

app.get("hello") { req in
    "Hello, World!"
}

app.get("users", ":id") { req -> User in
    let id = try req.parameters.require("id", as: Int.self)
    return User(id: id, name: "Alice")
}

try app.run()

Express.js / Flask風のWebフレームワーク。サーバサイドSwiftの事実上の標準


23-2. Hummingbird

軽量なSwift Webフレームワーク。Vaporの代替。


23-3. SwiftNIO

非同期I/Oライブラリ。Vapor / Hummingbirdの基盤。Apple自身が開発。


23-4. このセクションのまとめ

- SwiftはLinuxでも動く本格言語
- Vapor / HummingbirdでWeb
- SwiftNIOで低レベル非同期I/O
- AWS Lambda Custom Runtime対応

24. Macro(Swift 5.9+)

Swift 5.9で導入された コンパイル時メタプログラミング

@freestanding(expression)
macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(...)

let result = #stringify(42)    // (42, "42")
@attached(member, names: named(name))
macro Named() = #externalMacro(...)

@Named
struct Person {
    // マクロがnameプロパティを生成
}

@Observable@Codable、SwiftUIの #Preview などはマクロで実装されています。


25. パフォーマンスチューニング

- 値型を使う(コピーは速い、ARCオーバーヘッドなし)
- final classでvirtual dispatchを避ける
- @inlinable / @inline(__always)
- 不必要なOptional展開を避ける
- Arrayは連続メモリ
- 計測: Instruments / signpost / @MainActorの最適化

Swiftの性能改善は、まず計測から始める。体感で final@inline(__always) を足しても、実際の遅さがレイアウト、I/O、メインスレッドの待ち、画像処理、JSONデコード、ARCの解放タイミングにあることは多い。InstrumentsでTime Profiler、Allocations、Leaks、Main Thread Checkerを見て、原因を切り分ける。

値型強力だが、常に安いわけではない。ArrayString はCopy-on-Writeで効率化されているが、大きな値を頻繁に変形するとコピーや再確保が目立つことがある。大きな構造体をUI状態にそのまま流す、深いネストの差分を毎フレーム作る、巨大なJSONを同期的にデコードする、といった設計は避けたい。

Swift Concurrencyでは、性能と正しさがつながる。重い処理を @MainActor に置きすぎるとUIが詰まり、逆にactorを細かく分けすぎるとawait境界が増える。UI更新、状態保護、CPU処理、I/O処理の置き場所を分け、計測で確認しながら粒度を調整する。


26. Swift 5.x〜6.0の進化

5.0 (2019)  ABI安定、Result型、Raw String
5.1 (2019)  property wrapper、SwiftUI登場
5.5 (2021)  async/await、actor、structured concurrency
5.7 (2022)  some/any改善、regex DSL
5.9 (2023)  macro、ownership(borrowing/consuming)
5.10 (2024) data race safety
6.0 (2024)  厳格な並行性、typed throws、生ABI拡張

27. よくある落とし穴FAQ

Q1. letvar、どっち?

let がデフォルト。変更が必要なときだけ var

Q2. classstruct、どっち?

「Value Types First」。アイデンティティが必要 / 継承が必要 / 共有状態がある場合のみ class

Q3. weakunowned、どっち?

nilになりうるなら weak、絶対に存在する想定なら unowned(高速だがcrashリスク)。

Q4. try?try!、どっち?

エラーを無視したいなら try?、絶対成功と確信できるなら try!(テスト・プロトタイプのみ)。

Q5. [weak self] 必要?

クロージャがselfを保持し、selfもクロージャを保持するなら必要です。保持関係を確認したうえで付けると判断しやすくなります。

Q6. some Viewany View

戻り値型some View(コンパイル時単一)。配列に異なるViewを入れたいなら any View

Q7. protocol extension でメソッドが上書きされない

protocol extensionのメソッドは 静的ディスパッチ。クラスのメソッドのみオーバーライド可能。

Q8. async関数の中でsync関数を呼べる?

OK。逆は不可(Task { ... } で囲む必要あり)。

Q9. @MainActor をどこに付ける?

UI更新する関数やViewModelクラス全体に。

Q10. iOS 16未満をサポート

@available(iOS 17.0, *) で機能制限。古いiOSには別実装を提供。


28. 学習ロードマップ(30日)

Week 1: 基礎

  • Xcodeセットアップ
  • let/var/Optional/関数
  • struct/class/enum
  • Array/Dictionary

Week 2: OOP/POP

  • protocol/extension
  • ジェネリクス
  • クロージャ
  • ARC/weak/unowned

Week 3: SwiftUI/Concurrency

  • SwiftUI入門
  • @State/@Binding/@StateObject
  • async/await
  • actor

Week 4: 実践


29. 用語集

あ行

  • ARC: Automatic Reference Counting、参照カウントメモリ管理
  • アクター: actorキーワード、データ競合を防ぐ
  • オプショナル(Optional): nilを許容する型、T?

か行

  • 構造化並行性: structured concurrency、Swift 5.5+

さ行

  • 整数リテラル: 10、0xff、0b1010、0o17
  • storage type: stored property
  • Sendable: スレッド安全な型のマーカー

た行

  • 抽象クラス: Swiftには正式にはなし(protocolを使う)
  • トレイリングクロージャ: trailing closure

な行

は行

  • プロパティラッパー: @propertyWrapper

ま行

  • マクロ(Macro): Swift 5.9+、コンパイル時メタプログラミング

A〜Z

  • ABI: Application Binary Interface(Swift 5.0で安定化)
  • POP: Protocol-Oriented Programming
  • Sendable: スレッド安全マーカー
  • SwiftUI: 宣言的UI
  • SwiftNIO: 非同期I/Oフレームワーク
  • SPM: Swift Package Manager

発展: Swiftらしい設計

ここからはSwiftの各機能を 実例とともに深掘り。Optional、Concurrency、Property Wrapper、Macro、SwiftUI、性能チューニングまで詳細に。


31. プロトコル指向プログラミング深掘り

31-1. POPの哲学

継承(is-a)よりプロトコル準拠(does)」がSwift文化。classの継承を控えめに、protocolで振る舞いを契約する。

// 古典的なOOP
class Animal { ... }
class Dog: Animal { ... }       // is-a

// POP
protocol Speaks { func speak() -> String }
struct Dog: Speaks { ... }
struct Robot: Speaks { ... }    // is-a関係なし、振る舞いだけ共有

WWDC 2015のCrusty講演がPOPを世界に広めた。


31-2. プロトコルの型として使う

// existential
let speakers: [any Speaks] = [Dog(), Robot()]
speakers.forEach { print($0.speak()) }

// generic(より高速、型消去なし)
func describe<T: Speaks>(_ s: T) {
    print(s.speak())
}

// some(戻り値のopaque type)
func makeSpeaker() -> some Speaks {
    return Dog()
}

any動的ディスパッチsome静的単一型


31-3. PAT(Protocol with Associated Types)

protocol Container {
    associatedtype Item
    var count: Int { get }
    mutating func append(_ item: Item)
    subscript(i: Int) -> Item { get }
}

struct IntStack: Container {
    typealias Item = Int     // 明示も推論も可
    var items = [Int]()
    var count: Int { items.count }
    mutating func append(_ item: Int) { items.append(item) }
    subscript(i: Int) -> Int { items[i] }
}

プロトコルに型パラメータ」を持たせる仕組み。Javaの interface Container<T> に近い。


31-4. プロトコル拡張で振る舞いを追加

protocol Identifiable {
    var id: String { get }
}

extension Identifiable {
    var description: String { "ID: \(id)" }
}

struct User: Identifiable {
    let id: String
}

User(id: "123").description    // "ID: 123"

プロトコルにデフォルト実装を加える」。Kotlinのinterface default、Rustのdefault impl相当。


31-5. 条件付き準拠

extension Array: Equatable where Element: Equatable {
    static func == (lhs: [Element], rhs: [Element]) -> Bool { ... }
}

要素がEquatableのときだけArrayもEquatable」のような条件付き準拠。


31-6. このセクションのまとめ

- POP: 「継承より準拠」
- existential(any)vs generic vs some
- PATでassociatedtype
- protocol extensionでデフォルト実装
- 条件付き準拠(where句)

32. Concurrency深掘り

32-1. async/awaitの内部

func fetchUser() async throws -> User {
    let data = try await URLSession.shared.data(from: url).0
    return try JSONDecoder().decode(User.self, from: data)
}

async 関数はコンパイラによって CPS(Continuation Passing Style) に変換され、suspend pointで待機できる構造になる。


32-2. TaskとTaskGroup

// 単一タスク
let task = Task {
    return await fetchUser()
}
let user = try await task.value

// 並行タスク
let users = try await withThrowingTaskGroup(of: User.self) { group in
    for id in ids {
        group.addTask { try await fetchUser(id: id) }
    }
    var results: [User] = []
    for try await user in group {
        results.append(user)
    }
    return results
}

32-3. async let(軽量並行)

async let user = fetchUser()
async let orders = fetchOrders()
async let activities = fetchActivities()

let result = try await (user, orders, activities)

3つを並行に開始、最後にまとめてawait」。


32-4. actorの真の意味

actor Counter {
    private var count = 0
    
    func increment() {
        count += 1
    }
    
    func value() -> Int {
        return count
    }
}

let counter = Counter()
Task {
    await counter.increment()    // awaitが必要(actor内部へ入るため)
    let v = await counter.value()
    print(v)
}

actorは「内部状態を1つのタスクからしか操作できない」型。データ競合を コンパイル時に防止。Erlangのアクターモデルにinspired。


32-5. Sendableとdata race safety

struct UserID: Sendable {           // 値型 + immutableで自動的にSendable
    let value: Int
}

class MutableState {                 // classはデフォルトでnon-Sendable
    var count: Int = 0
}

actor Cache {
    func store(_ id: UserID) { ... }    // Sendableな値だけ受け取れる
    // func store(_ s: MutableState) { ... }  // エラー!
}

Swift 6では strict concurrency checking がデフォルトに。データ競合がコンパイルエラー。


32-6. @MainActor

@MainActor
class ViewModel: ObservableObject {
    @Published var users: [User] = []
    
    func load() async {
        let data = try await fetchUsers()
        users = data            // 自動的にmain threadで実行
    }
}

UI更新をmain threadに固定。SwiftでAndroid UIスレッドの罠を回避する仕組み。


32-7. Continuation(コールバック → async化)

func legacyFetch(completion: @escaping (Result<Data, Error>) -> Void) { ... }

func modernFetch() async throws -> Data {
    return try await withCheckedThrowingContinuation { continuation in
        legacyFetch { result in
            continuation.resume(with: result)
        }
    }
}

古いコールバックAPIasyncするブリッジ。


32-8. このセクションのまとめ

- async / await + try / throws
- async let / TaskGroupで並行
- actorでデータ競合防止
- Sendableでスレッド安全性
- @MainActorでUI固定
- Continuationでコールバックをasync化

33. SwiftUI深掘り

33-1. State管理の階層

@State           View内部のローカル
@Binding親から渡された参照
@StateObject     ObservableObjectの所有(Viewが作る)
@ObservedObject外部から注入されたObservableObject
@EnvironmentObject環境注入(深い階層へ)
@Environment     SwiftUI環境値(カラースキーム等)
@AppStorage      UserDefaults
@SceneStorageシーン状態(multi-window)
@FetchRequest   Core Data

33-2. Viewの合成

struct ContentView: View {
    var body: some View {
        VStack(spacing: 16) {
            HeaderView(title: "Hello")
            
            ForEach(items) { item in
                ItemView(item: item)
            }
            
            FooterView()
        }
        .padding()
        .background(Color.gray.opacity(0.1))
    }
}

すべてのViewは 値型struct。レンダリングは差分計算で最適化。Reactに近い思想。


33-3. modifierの連鎖

Text("Hello")
    .font(.largeTitle)
    .foregroundColor(.blue)
    .padding()
    .background(Color.yellow)
    .cornerRadius(10)
    .shadow(radius: 5)

各modifierは 新しいViewを返す。チェーン順序で挙動が変わる(padding().background()background().padding())。


33-4. 動的レイアウト

GeometryReader { geometry in
    HStack {
        Text("Left")
            .frame(width: geometry.size.width * 0.3)
        Text("Right")
            .frame(width: geometry.size.width * 0.7)
    }
}

ScrollView {
    LazyVStack(spacing: 8) {
        ForEach(0..<1000) { i in
            Text("Item \(i)")
        }
    }
}

GeometryReader でサイズ取得、Lazy* で遅延描画。


33-5. Animation

@State private var isExpanded = false

VStack {
    Text(isExpanded ? "Expanded" : "Collapsed")
    Button("Toggle") {
        withAnimation(.spring()) {
            isExpanded.toggle()
        }
    }
}
.frame(height: isExpanded ? 200 : 100)
.animation(.easeInOut, value: isExpanded)

宣言的アニメーション。state変更に追従して自動アニメ。


33-6. Navigation

NavigationStack {
    List(items) { item in
        NavigationLink(value: item) {
            Text(item.title)
        }
    }
    .navigationTitle("Items")
    .navigationDestination(for: Item.self) { item in
        ItemDetailView(item: item)
    }
}

iOS 16+ の NavigationStack。型安全なナビゲーション。


33-7. このセクションのまとめ

- Viewは値型struct、合成でUI構築
- @State / @Binding / @StateObject / @ObservedObject
- modifier連鎖で見た目調整
- LazyVStack / LazyHGridで大量データ
- withAnimationで宣言的アニメ
- NavigationStackで型安全Nav(iOS 16+)

34. Property Wrapper深掘り

34-1. 自作Property Wrapper

@propertyWrapper
struct Clamped<Value: Comparable> {
    private var value: Value
    let range: ClosedRange<Value>
    
    init(wrappedValue: Value, _ range: ClosedRange<Value>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
    
    var wrappedValue: Value {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Player {
    @Clamped(0...100) var health = 100
    @Clamped(1...10) var level = 1
}

var p = Player()
p.health = 200       // 100にクランプ
p.health = -50       // 0にクランプ

34-2. projectedValue($)

@propertyWrapper
struct Tracked<Value> {
    private var value: Value
    private(set) var changeCount = 0
    
    init(wrappedValue: Value) {
        self.value = wrappedValue
    }
    
    var wrappedValue: Value {
        get { value }
        set { value = newValue; changeCount += 1 }
    }
    
    var projectedValue: Int { changeCount }
}

struct Foo {
    @Tracked var name = ""
}

var f = Foo()
f.name = "Alice"
f.name = "Bob"
print(f.$name)        // 2(changeCount)

$namewrapper自身にアクセス。SwiftUIの @State$state がこの仕組み。


34-3. このセクションのまとめ

- @propertyWrapperでアクセスをカスタマイズ
- wrappedValueが普通のアクセス
- projectedValueは $ で
- SwiftUIの @State / @Binding / @Published等の基盤

35. Result Builder(DSL)

SwiftUI / Regex Builderの基盤。

@resultBuilder
struct StringBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined(separator: "\n")
    }
}

@StringBuilder
func makeText() -> String {
    "Hello"
    "World"
    "Swift"
}

print(makeText())
// Hello
// World
// Swift

SwiftUIの body: some View { ... } の中身は @ViewBuilder というresult builder。


36. Macro(Swift 5.9+)

36-1. Macroの例

@freestanding(expression)
macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(...)

let result = #stringify(42)    // (42, "42")

Swift 5.9で コンパイル時メタプログラミングが導入された。型安全 + 強力なツール


36-2. 主要な組み込みMacro

@Observable               // SwiftUIでView連携
class ViewModel {
    var count = 0
}

#Preview {                // SwiftUI Preview
    ContentView()
}

#expect(value > 0)        // Swift Testing

@Observable はObservableObject + @Publishedの代替(より軽量)。


36-3. このセクションのまとめ

- Swift 5.9+ でmacro導入
- @freestanding / @attachedマクロ
- @Observableでreactive class
- #PreviewでSwiftUI確認
- コード生成 + 静的解析の中間

37. メモリ管理(ARC)詳細

37-1. 強参照サイクルの回避

class Parent {
    var children: [Child] = []
    deinit { print("Parent deinit") }
}

class Child {
    weak var parent: Parent?    // 強参照しない
    deinit { print("Child deinit") }
}

親→子は強参照、子→親はweak」。


37-2. クロージャでの [weak self]

class ViewController {
    var data: [String] = []
    
    func loadData() {
        api.fetch { [weak self] result in
            guard let self = self else { return }
            self.data = result    // selfは安全にOptional解除済み
        }
    }
}

クロージャがインスタンスを強参照しない」ためのキャプチャリスト。weakunowned を選ぶ。


37-3. unowned vs weak

weak:    Optional、解放されたらnil
unowned: 非Optional、解放されたらcrash(速いが危険)

「絶対にselfが先に死ぬ」と確信できるときだけunowned。普通はweak。


37-4. このセクションのまとめ

- ARCで参照カウント
- 強参照サイクルをweak / unownedで回避
- クロージャは [weak self] / [unowned self]
- 親→子は強、子→親はweakが定石

38. Server-Side Swift

38-1. Vapor

import Vapor

let app = try await Application.make(.development)
defer { try? await app.asyncShutdown() }

app.get("hello") { req in
    "Hello, World!"
}

app.get("users", ":id") { req -> User in
    let id = try req.parameters.require("id", as: Int.self)
    return User(id: id, name: "Alice")
}

app.post("users") { req -> User in
    let user = try req.content.decode(User.self)
    // DBに保存
    return user
}

try await app.execute()

Express / Flask風のWebフレームワーク。Server-Side Swiftの事実上の標準


38-2. Hummingbird / SwiftNIO

SwiftNIO はApple自身が作った非同期I/Oライブラリ。Vapor、Hummingbirdの基盤。


38-3. AWS Lambda

import AWSLambdaRuntime

let runtime = LambdaRuntime { (event: APIGatewayV2Request, context: LambdaContext) -> APIGatewayV2Response in
    return APIGatewayV2Response(statusCode: .ok, body: "Hello from Swift Lambda!")
}

try await runtime.run()

Native AOTで 起動瞬時。SwiftでLambda関数を書ける。


38-4. このセクションのまとめ

- VaporがServer-Sideの標準
- SwiftNIOが低レベル基盤
- AWS Lambda対応
- Linuxで実用的に動く

39. Swift拡張FAQ

Q1. let / varはどっち?

let をデフォルト、変更必要な時だけ var。Kotlinのval/var哲学と同じ。

Q2. structとclassの選び方

Value Types First」。アイデンティティが必要、または継承が必要なときだけclass。

Q3. asyncとsyncの混在

Task { ... }asyncコードを起動。同期から非同期はOK、逆は基本不可。

Q4. Optionalの強制unwrap

! は危険。if let / guard let / ?. / ?? を使う。

Q5. weak vs unowned

nilになりうるならweak、絶対nilにならないならunowned(高速)。

Q6. SwiftUIの @Stateと @StateObject

@State:        値型のローカル状態
@StateObject:  参照型(ObservableObject)の所有

参照型なら @StateObject値型なら @State

Q7. someとanyの違い

some:  単一の具体型(コンパイル時決定、ゼロコスト)
any:   ボックス化(実行時決定、柔軟だが遅い)

戻り値型some、配列に異なる型を入れたいなら any

Q8. POP(プロトコル指向)vs OOP

Swiftでは POPが推奨。classの継承は最小限。

Q9. Swift TestingとXCTest

新規は Swift Testing(Swift 6+)が推奨。#expect / @Test でモダン。

Q10. iOSのバージョン制約

@available(iOS 17, *) で機能を限定。古いバージョンは別実装。


40. 実践プロジェクト構成

MyApp/
├── Package.swift
├── App/
│   ├── App.swift
│   └── ContentView.swift
├── Features/
│   ├── User/
│   │   ├── Models/
│   │   ├── Views/
│   │   ├── ViewModels/
│   │   └── Services/
│   └── Order/
├── Core/
│   ├── Networking/
│   ├── Storage/
│   └── Utilities/
├── Resources/
│   └── Assets.xcassets
└── Tests/

Featureごとにフォルダ」がSwiftUI時代の主流。


実践: UIとアプリ開発


42. ジェネリクス深掘り

42-1. 制約とプロトコル

func max<T: Comparable>(_ a: T, _ b: T) -> T {
    return a > b ? a : b
}

func process<T>(_ x: T) where T: Hashable, T: CustomStringConvertible { ... }

extension Array where Element: Numeric {
    func total() -> Element {
        return reduce(0, +)
    }
}

where 句で 特定の制約を満たすときだけ拡張


42-2. PATとexistential type

protocol Container {
    associatedtype Item
    var count: Int { get }
}

// 古いSwiftでは「any Container」が使えなかった
// Swift 5.6+ でanyキーワード必須
let containers: [any Container] = [...]

// generic functionで受ける(推奨)
func describe<C: Container>(_ c: C) {
    print(c.count)
}

42-3. opaque type(some)

func makeIdentifiable() -> some Identifiable {
    User(id: UUID())
}

戻り値の具体型を隠すが、コンパイル時には1つの型」。性能ペナルティなしで型抽象。


42-4. このセクションのまとめ

- where句で複雑な制約
- PATとassociatedtype
- some(静的)/ any(動的)
- 条件付きextensionで型ごとに振る舞い追加

43. SwiftUIのさらなる深掘り

43-1. Observable(Swift 5.9+)

@Observable
class CounterModel {
    var count = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @State private var model = CounterModel()
    
    var body: some View {
        VStack {
            Text("\(model.count)")
            Button("+") { model.increment() }
        }
    }
}

@Observable macroで ObservableObject + @Publishedより軽量。Swift 5.9で導入。


43-2. ViewBuilderの活用

struct Card<Content: View>: View {
    let title: String
    @ViewBuilder let content: () -> Content
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.headline)
            content()
        }
        .padding()
        .background(Color.gray.opacity(0.1))
        .cornerRadius(8)
    }
}

// 使用
Card(title: "Profile") {
    Text("Name: Alice")
    Text("Age: 30")
}

子Viewを受け取るカスタムView」を作れる。


43-3. PreferenceKey(子から親へ通信)

struct ContentSize: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

// 子側
GeometryReader { proxy in
    Color.clear.preference(key: ContentSize.self, value: proxy.size)
}

// 親側
parentView
    .onPreferenceChange(ContentSize.self) { size in
        // sizeを取得
    }

子の情報を親に伝える仕組み。複雑だが強力


43-4. このセクションのまとめ

- @Observableで軽量モデル
- ViewBuilderでコンテナView
- PreferenceKeyで子→親通信
- アニメーション: withAnimation / .animation()

44. Combine(Appleのリアクティブ)

import Combine

class ViewModel: ObservableObject {
    @Published var search = ""
    @Published var results: [Result] = []
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $search
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .flatMap { query in
                api.search(query: query)
                    .replaceError(with: [])
            }
            .receive(on: RunLoop.main)
            .assign(to: &$results)
    }
}

Apple製のreactiveフレームワーク。RxSwiftの代替。Swift Concurrencyが推奨されるようになり、Combineの役割は縮小中


45. 実践: Photo Galleryアプリ

import SwiftUI

@Observable
class GalleryViewModel {
    var photos: [Photo] = []
    var isLoading = false
    var error: Error?
    
    func loadPhotos() async {
        isLoading = true
        defer { isLoading = false }
        
        do {
            photos = try await api.fetchPhotos()
        } catch {
            self.error = error
        }
    }
}

struct PhotoCell: View {
    let photo: Photo
    
    var body: some View {
        AsyncImage(url: photo.thumbnailURL) { image in
            image.resizable()
                .aspectRatio(contentMode: .fill)
        } placeholder: {
            ProgressView()
        }
        .frame(width: 100, height: 100)
        .clipShape(RoundedRectangle(cornerRadius: 8))
    }
}

struct GalleryView: View {
    @State private var vm = GalleryViewModel()
    
    let columns = [GridItem(.adaptive(minimum: 100), spacing: 8)]
    
    var body: some View {
        NavigationStack {
            Group {
                if vm.isLoading && vm.photos.isEmpty {
                    ProgressView()
                } else if let error = vm.error {
                    ErrorView(error: error) {
                        Task { await vm.loadPhotos() }
                    }
                } else {
                    ScrollView {
                        LazyVGrid(columns: columns, spacing: 8) {
                            ForEach(vm.photos) { photo in
                                NavigationLink(value: photo) {
                                    PhotoCell(photo: photo)
                                }
                            }
                        }
                        .padding()
                    }
                }
            }
            .navigationTitle("Gallery")
            .navigationDestination(for: Photo.self) { photo in
                PhotoDetailView(photo: photo)
            }
            .refreshable {
                await vm.loadPhotos()
            }
            .task {
                await vm.loadPhotos()
            }
        }
    }
}

これだけで:

  • 非同期データ読み込み
  • エラーハンドリング
  • Pull to refresh
  • 自動レイアウト(適応型grid)
  • 詳細画面遷移
  • 型安全なナビゲーション

46. Swift学習リソース

書籍

  • 『The Swift Programming Language』(公式、無料)
  • 『Hacking with Swift』Paul Hudson
  • 『Swift in Depth』Tjeerd in 't Veen

Web

コミュニティ

  • /r/swift(Reddit)
  • Swift Forums(公式)
  • iOS Developers Slack
  • WWDCビデオ

応用: 型とツール


48. Swiftの型システム詳細

48-1. typealias

typealias UserID = Int
typealias Callback<T> = (Result<T, Error>) -> Void
typealias JSONDict = [String: Any]

48-2. enumのさらなる活用

enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)
    
    var value: Success? {
        guard case .success(let v) = self else { return nil }
        return v
    }
    
    func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> Result<NewSuccess, Failure> {
        switch self {
        case .success(let v): return .success(transform(v))
        case .failure(let e): return .failure(e)
        }
    }
}

@frozen将来もcaseが増えないことを保証。


48-3. 動的メンバー検索

@dynamicMemberLookup
struct DynamicProxy {
    var data: [String: Any] = [:]
    
    subscript(dynamicMember key: String) -> Any? {
        get { data[key] }
        set { data[key] = newValue }
    }
}

var p = DynamicProxy()
p.name = "Alice"
p.age = 30
print(p.name)        // Optional("Alice")

JSONやJavaScript bridgeで活用。


48-4. このセクションのまとめ

- typealiasで別名
- enum + associated valueでADT
- @dynamicMemberLookupで動的アクセス
- @frozenで将来のcase追加禁止

49. Swift Package Manager詳細

49-1. Package.swift

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "MyLib",
    platforms: [
        .iOS(.v17),
        .macOS(.v14),
    ],
    products: [
        .library(name: "MyLib", targets: ["MyLib"]),
        .executable(name: "MyTool", targets: ["MyTool"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
        .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "MyLib",
            dependencies: [
                .product(name: "Collections", package: "swift-collections"),
            ]
        ),
        .executableTarget(
            name: "MyTool",
            dependencies: [
                "MyLib",
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
            ]
        ),
        .testTarget(
            name: "MyLibTests",
            dependencies: ["MyLib"]
        ),
    ]
)

49-2. ローカル依存

.package(path: "../OtherLib"),

49-3. binary target(XCFramework)

.binaryTarget(
    name: "PrebuiltLib",
    url: "https://example.com/Lib.xcframework.zip",
    checksum: "..."
)

49-4. このセクションのまとめ

- Package.swiftで依存・ターゲット定義
- swift package init / build / test / run
- Linux / macOSで動く
- XCFrameworkでiOSの事前ビルド配信

50. Swiftプログラマの推奨ツール

50-1. SwiftLint / SwiftFormat

brew install swiftlint
brew install swiftformat

swiftlint
swiftformat .

SwiftLint は規約チェック、SwiftFormat は自動整形。CI必須。


50-2. Xcode

Apple純正IDE。Appleプラットフォーム開発で 唯一の現実的選択

- ストーリーボード / SwiftUI Preview
- インタフェースビルダー
- Instruments(プロファイラ)
- Simulator
- App Store配信

50-3. VSCode + Swift extension

Linux / WindowsでのSwift開発。Server-Side Swiftで活用。


50-4. Swift Playgrounds

iPad / Macで動く対話型Swift環境。学習・実験向け。


51. Swiftパフォーマンスチューニング

51-1. 値型の活用

// 値型: コピーは速い、ARCオーバーヘッドなし
struct Point { var x, y: Double }

// 参照型: 必要な場合だけ
class Node { var children: [Node] = [] }

51-2. finalでvirtual dispatch排除

final class Foo {
    func method() { ... }    // 静的ディスパッチ可能(速い)
}

class Bar {
    func method() { ... }    // virtual dispatch(遅い)
}

継承しないならfinal」。コンパイラが直接呼び出しに最適化。


51-3. @inlinable / @inline

@inlinable public func tinyFunction() -> Int { 42 }
@inline(__always) func reallyTiny() -> Int { 42 }

ホットパスのインライン化指示。


51-4. Instruments

- Time Profiler:    CPUプロファイル
- Allocations:       メモリ割り当て
- Leaks:             メモリリーク
- Network:           ネットI/O
- SwiftUI:           View再描画

Xcodeに統合された強力なプロファイラ。


51-5. このセクションのまとめ

- 値型を優先
- final classでvirtual dispatch排除
- @inlinable / @inlineで最適化
- Instrumentsで計測

52. Swiftの現実的な弱点

弱点:
  - エコシステムがApple中心(Linux/Windowsは限定的)
  - コンパイル時間(特にジェネリクス重用)
  - エラーメッセージが分かりにくいことがある
  - ABI互換性の厳しさ(バージョン依存)
  - Server-sideはまだJava/Goほど成熟していない

将来性:
  - Appleの継続投資
  - Embedded Swift
  - Server-sideの改善
  - WASM対応
  - VisionOSという新領域

Swiftの弱点は、言語仕様そのものよりも「使われる場所の偏り」に現れる。Appleプラットフォームでは第一級の選択肢だが、サーバサイドやCLIではGo、Java、Rust、Pythonほど採用事例や運用ノウハウが多くない。採用判断では、言語の良さだけでなく、チームの経験、ライブラリ、監視、デプロイ、障害対応まで見る必要がある。

コンパイル時間は、大規模アプリで現実的な問題になる。ジェネリクス、SwiftUIの大きなView、複雑な型推論、巨大な単一モジュールはビルドを重くする。モジュール分割、型注釈、Viewの分割、依存関係の整理は、設計改善であると同時に開発体験の改善でもある。

それでもSwiftは、Optional、値型、Protocol、async/await、actor、SwiftUIを一つの言語体験として統合している点で強い。Apple向けアプリを長く保守するなら、Objective-C時代よりも安全で表現力のある土台を提供する。


53. Swift学習ロードマップ(60日)

Phase 1: 基礎(Day 1-15)

  • Xcodeセットアップ
  • 基本構文、let/var、Optional
  • struct / class / enum
  • 関数、closures
  • Array / Dictionary / Set

Phase 2: 中級(Day 16-30)

  • Protocol Oriented Programming
  • ジェネリクス
  • ARC、weak / unowned
  • CodableでJSON
  • エラーハンドリング

Phase 3: SwiftUI(Day 31-45)

  • @State / @Binding / @StateObject
  • View合成とmodifier
  • NavigationStack
  • アニメーション
  • @Observable(Swift 5.9+)

Phase 4: Concurrency(Day 46-60)


54. Swift用語集(追加)

あ行

か行

  • コンスタント(const): let
  • コンテンツ(content): SwiftUIの子View
  • コンディション(throwing): throws

さ行

  • シングルトン: 通常enumで実装
  • 修飾子(modifier): .padding() などSwiftUI

た行

  • タプル: (a, b) の組
  • 動的型: Any、AnyObject

A〜Z

  • ARC: Automatic Reference Counting
  • POP: Protocol-Oriented Programming
  • PAT: Protocol with Associated Types
  • SwiftUI: 宣言的UI
  • Swift Concurrency: async/await/actor
  • WWDC: Worldwide Developers Conference

実例: ネットワークとエラー処理


56. ネットワーキング詳細

// 単純なGET
let url = URL(string: "https://api.example.com/users")!
let (data, _) = try await URLSession.shared.data(from: url)
let users = try JSONDecoder().decode([User].self, from: data)

// POST + JSON
struct CreateUserRequest: Codable {
    let name: String
    let email: String
}

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(CreateUserRequest(name: "Alice", email: "a@b.c"))

let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
    throw NetworkError.badStatus
}
let user = try JSONDecoder().decode(User.self, from: data)

57. エラーハンドリング統一

enum AppError: Error, LocalizedError {
    case network(Error)
    case decoding(Error)
    case unauthorized
    case notFound(resource: String)
    
    var errorDescription: String? {
        switch self {
        case .network(let e): return "Network error: \(e.localizedDescription)"
        case .decoding(let e): return "Parse error: \(e)"
        case .unauthorized: return "Login required"
        case .notFound(let resource): return "\(resource) not found"
        }
    }
}

// 使用
func fetchUser(id: Int) async throws -> User {
    do {
        let data = try await api.fetch(id: id)
        return try JSONDecoder().decode(User.self, from: data)
    } catch let e as URLError {
        throw AppError.network(e)
    } catch let e as DecodingError {
        throw AppError.decoding(e)
    }
}

発展: Appleプラットフォーム連携


59. Codable / JSON詳細

59-1. 基本

struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

let json = #"""
{"id": 1, "name": "Alice", "email": "a@b.c"}
"""#

let user = try JSONDecoder().decode(User.self, from: json.data(using: .utf8)!)
let encoded = try JSONEncoder().encode(user)

Codable = Encodable + DecodableSwift 4の革命的機能


59-2. キーマッピング

struct User: Codable {
    let id: Int
    let firstName: String
    let lastName: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case lastName = "last_name"
    }
}

// またはデコーダの設定
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

59-3. 日付フォーマット

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)

59-4. カスタムエンコード

struct Color: Codable {
    let red: Double
    let green: Double
    let blue: Double
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        red = try container.decode(Double.self, forKey: .red)
        green = try container.decode(Double.self, forKey: .green)
        blue = try container.decode(Double.self, forKey: .blue)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(red, forKey: .red)
        try container.encode(green, forKey: .green)
        try container.encode(blue, forKey: .blue)
    }
}

59-5. このセクションのまとめ

- Codableで双方向シリアライズ
- CodingKeysでkeyマッピング
- DateDecodingStrategy / KeyDecodingStrategy
- カスタムencode(to:) / init(from:)

60. SwiftData(Core Dataの現代版)

import SwiftData

@Model
class User {
    var id: UUID
    var name: String
    var email: String
    var createdAt: Date
    
    init(name: String, email: String) {
        self.id = UUID()
        self.name = name
        self.email = email
        self.createdAt = .now
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: User.self)
    }
}

struct ContentView: View {
    @Environment(\.modelContext) var context
    @Query(sort: \User.name) var users: [User]
    
    var body: some View {
        List(users) { user in
            Text(user.name)
        }
    }
}

iOS 17+ の SwiftData。Core Dataの現代的代替。


61. WidgetKitとApp Intents

// Widget
struct CounterWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "Counter", provider: CounterProvider()) { entry in
            VStack {
                Text("\(entry.count)")
                    .font(.largeTitle)
            }
        }
    }
}

// App Intent (Shortcuts、Siri連携)
struct IncrementCounterIntent: AppIntent {
    static var title: LocalizedStringResource = "Increment Counter"
    
    func perform() async throws -> some IntentResult {
        // カウンタを増やす
        return .result()
    }
}

ホーム画面ウィジェット、Shortcuts、Siri連携。


62. visionOS入門

import SwiftUI
import RealityKit

struct ContentView: View {
    var body: some View {
        VStack {
            RealityView { content in
                let entity = ModelEntity(mesh: .generateBox(size: 0.5))
                content.add(entity)
            }
        }
    }
}

Apple Vision Pro開発。SwiftUI + RealityKit で3D / AR。


63. Linux / Server-Side詳細

# Linuxインストール
curl -O https://download.swift.org/swift-5.9-release/ubuntu2204/swift-5.9-RELEASE/swift-5.9-RELEASE-ubuntu22.04.tar.gz
tar -xzf swift-*.tar.gz
export PATH=/path/to/swift/usr/bin:$PATH

# Vaporアプリ
vapor new HelloWorld
cd HelloWorld
swift run

サーバサイドSwiftでは、Vaporが代表的な選択肢になる。ルーティング、ミドルウェア、ORM、テンプレート、認証などを一通り持ち、Swiftの型安全性をWeb APIへ持ち込める。ただし、エコシステムの厚みはNode.js、Java、Goほどではないため、外部サービス連携や運用ツールは事前に確認したほうがよい。

LinuxでSwiftを使う場合、Foundationの挙動、利用できるAppleフレームワーク、パッケージの対応状況を意識する。iOSで当たり前に使うUIKit、SwiftUI、Combineの一部はサーバサイドでは前提にできない。逆に、CLI、バッチ、軽量API、既存Swiftコードの共有には向いている。

本番運用では、Dockerイメージ、静的リンクの可否、ログ形式、メトリクス、ヘルスチェック、SIGTERM時の終了処理を整える。Swiftは型安全だが、プロセス運用の基本は他のサーバ言語と同じである。


64. Swiftと他言語の連携

Swiftから呼べる:
  - Objective-C(直接)
  - C / C++(@_silgen_name、ヘッダブリッジング)
  - Python(PythonKit)
  - JavaScript(JavaScriptCore)

Swiftを呼べる:
  - Objective-C(@objc)
  - C(@_cdecl)
  - Kotlin(KMP経由)

Swiftの相互運用は、Apple開発では非常に重要である。既存のObjective-C資産を段階的にSwiftへ移行したり、Cライブラリを包んだり、C++実装と接続したりする場面が多い。境界では、所有権、例外、NULL、ポインタ、文字列、スレッド安全性を慎重に扱う。

Objective-Cとの連携では、@objc, NSObject, optional性、ブリッジ可能な型を理解する。SwiftらしいAPIを作るなら、Objective-Cの都合を内部に閉じ込め、外側にはOptional、Result、async/await、値型を使った薄いラッパーを出すとよい。

CやC++との連携では、Swiftの安全性が自動的に境界を守ってくれるわけではない。ポインタ寿命、バッファサイズ、メモリ解放の責任を明確にし、危険なAPIを直接アプリ全体へ広げない。相互運用は移行と再利用のための道具であり、境界を設計することが保守性を決める。


65. Swift CLIツール作成

import ArgumentParser

@main
struct MyTool: ParsableCommand {
    @Argument(help: "Input file")
    var input: String
    
    @Option(name: .shortAndLong, help: "Output file")
    var output: String = "output.txt"
    
    @Flag(name: .shortAndLong, help: "Verbose output")
    var verbose = false
    
    mutating func run() throws {
        if verbose { print("Reading \(input)...") }
        let data = try String(contentsOfFile: input)
        let processed = data.uppercased()
        try processed.write(toFile: output, atomically: true, encoding: .utf8)
        if verbose { print("Written to \(output)") }
    }
}

swift-argument-parser型安全なCLI引数解析


66. Swift実装チェックリスト

新規プロジェクト

☐ Swift 5.9以上
☐ Xcode 15以上
☐ SwiftPMで依存管理
☐ SwiftLint / SwiftFormat
☐ Swift Testing(またはXCTest)
☐ Concurrency Strict Mode検討

コード品質

☐ letを優先
☐ structを優先(Value Types First)
☐ Optionalを ! で強制unwrapしない
☐ weak self / unowned selfを意識
☐ async/awaitを推進、callback廃止
☐ @MainActorでUI隔離

パフォーマンス

☐ final classでvirtual dispatch排除
☐ 値型でコピー(ARCオーバーヘッド減)
☐ Instrumentsで計測
☐ Lazy初期化
☐ ARCリーク監視

67. Swiftの未来予想

近い将来:
  - Swift 6でstrict concurrency
  - Embedded Swift(マイコン)
  - WASM対応強化
  - Server-Sideの成熟
  - LLM統合(Apple Intelligence)

長期:
  - Appleプラットフォームの全面Swift化
  - Cross-platform UI(Swift on Android?)
  - Linux / Windowsでのサポート拡大

応用: 現場パターン


69. ARCとメモリ管理の詳細

69-1. 強参照サイクルの実例

class Person {
    let name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
    }
    
    deinit { print("\(name) deinit") }
}

class Apartment {
    let unit: String
    var tenant: Person?      // 強参照ならサイクル!
    
    init(unit: String) {
        self.unit = unit
    }
    
    deinit { print("Apt \(unit) deinit") }
}

var alice: Person? = Person(name: "Alice")
var apt1: Apartment? = Apartment(unit: "1A")
alice?.apartment = apt1
apt1?.tenant = alice

alice = nil      // Alice deinitが呼ばれない!
apt1 = nil       // 同じく解放されない

解決:weak

class Apartment {
    let unit: String
    weak var tenant: Person?    // 弱参照
}

// これでalice = nil → Alice deinitが呼ばれる

69-2. closureのキャプチャ

class Manager {
    var name = "Manager"
    var observer: (() -> Void)?
    
    func setup() {
        observer = {
            print("Hello, \(self.name)")    // selfを強参照!
        }
    }
}

// 解決
func setup() {
    observer = { [weak self] in
        guard let self = self else { return }
        print("Hello, \(self.name)")
    }
}

69-3. unownedの使いどころ

class Customer {
    let name: String
    var card: CreditCard?
    
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: String
    unowned let customer: Customer    // 必ず存在する
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

Cardは必ずCustomerに紐づく」前提で unownedweak より速いが、解放後アクセスでクラッシュ。


69-4. このセクションのまとめ

- 親→子は強、子→親はweak
- closureは [weak self] でキャプチャ
- 必ず存在する想定ならunowned(速いが危険)
- InstrumentsのLeaksでリーク検出

70. 高度なConcurrencyパターン

70-1. AsyncSequence / AsyncStream

struct CounterSequence: AsyncSequence {
    typealias Element = Int
    let limit: Int
    
    struct AsyncIterator: AsyncIteratorProtocol {
        let limit: Int
        var current = 0
        
        mutating func next() async -> Int? {
            guard current < limit else { return nil }
            try? await Task.sleep(nanoseconds: 1_000_000)
            defer { current += 1 }
            return current
        }
    }
    
    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(limit: limit)
    }
}

for await num in CounterSequence(limit: 5) {
    print(num)
}

AsyncStream(コールバック → AsyncSequence)

func observeNetworkStatus() -> AsyncStream<NetworkStatus> {
    AsyncStream { continuation in
        let observer = NetworkObserver { status in
            continuation.yield(status)
        }
        continuation.onTermination = { _ in
            observer.stop()
        }
        observer.start()
    }
}

for await status in observeNetworkStatus() {
    print(status)
}

70-2. Distributed Actor

import Distributed

distributed actor RemoteCounter {
    var count = 0
    
    distributed func increment() async {
        count += 1
    }
    
    distributed func value() async -> Int {
        return count
    }
}

ネットワーク越しに動くactor」。Swift 5.7+ で導入。


70-3. このセクションのまとめ

- AsyncSequence / AsyncStreamで非同期反復
- AsyncStreamでコールバックを変換
- Distributed actorで分散システム

71. SwiftUI上級パターン

71-1. 自作ViewModifier

struct CardStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.gray.opacity(0.1))
            .cornerRadius(8)
            .shadow(radius: 2)
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardStyle())
    }
}

// 使用
Text("Hello")
    .cardStyle()

71-2. EnvironmentValues拡張

struct ThemeKey: EnvironmentKey {
    static let defaultValue: Theme = .light
}

extension EnvironmentValues {
    var theme: Theme {
        get { self[ThemeKey.self] }
        set { self[ThemeKey.self] = newValue }
    }
}

// 注入
ContentView()
    .environment(\.theme, .dark)

// 使用
struct MyView: View {
    @Environment(\.theme) var theme
    
    var body: some View {
        Text("Hello").foregroundColor(theme.textColor)
    }
}

71-3. matchedGeometryEffect(高度なアニメ)

@Namespace var namespace

if isExpanded {
    ExpandedView()
        .matchedGeometryEffect(id: "card", in: namespace)
} else {
    CardView()
        .matchedGeometryEffect(id: "card", in: namespace)
}

View切替時に位置・サイズを補完アニメ」。Heroアニメーション。


71-4. このセクションのまとめ

- ViewModifierで再利用可能スタイル
- EnvironmentKeyでカスタム環境値
- matchedGeometryEffectでヒーローアニメ
- @StateObject vs @ObservedObjectの使い分け

72. CombineとSwift Concurrencyの使い分け

Combine:
  - リアクティブストリーム
  - debounce / throttle / merge / combineLatest
  - SwiftUIの @Publishedと統合
  - 学習コスト高め

Swift Concurrency:
  - async/awaitで簡潔
  - actorでデータ競合防止
  - 構造化並行性
  - 推奨される現代的選択

新規はSwift Concurrency、Combineは維持・互換用」。

Combineは、時間とともに流れる値を合成するモデルである。検索入力のdebounce、複数Publisherの合成、フォーム状態の監視、古いiOSバージョンとの互換では今も役に立つ。一方で、単発の非同期処理、逐次的なAPI呼び出し、エラー処理を含む読みやすいフローには、async/awaitのほうが自然である。

移行時は、Combineを一気に消す必要はない。既存の PublisherAsyncSequence に寄せる、ViewModelの新規処理だけasync/awaitにする、外部API境界で変換する、という段階的な進め方が現実的である。SwiftUIでは task, Observable, @MainActor と合わせると、状態更新の位置が分かりやすくなる。

判断基準は「値の流れを合成したいか、手続き的に待ちたいか」である。前者はCombine、後者はSwift Concurrencyが読みやすい。どちらを使う場合も、メインスレッド更新、キャンセル、重複リクエスト、エラーの表示場所を設計しておく。


73. 実装チートシート

// 文字列
let s = "Hello, World"
s.count                        // 12
s.uppercased()                 // "HELLO, WORLD"
s.lowercased()
s.trimmingCharacters(in: .whitespaces)
s.replacingOccurrences(of: "l", with: "L")
s.components(separatedBy: ",")
s.contains("World")
s.hasPrefix("Hello")
s.hasSuffix("World")

// 配列
var arr = [1, 2, 3]
arr.append(4)
arr += [5, 6]
arr.removeLast()
arr.first
arr.last
arr.isEmpty
arr.contains(3)
arr.firstIndex(of: 3)

// 高階関数
arr.map { $0 * 2 }
arr.filter { $0 > 2 }
arr.reduce(0, +)
arr.sorted()
arr.sorted { $0 > $1 }

// Dictionary
var d: [String: Int] = ["a": 1, "b": 2]
d["c"] = 3
d["a", default: 0]
d.keys
d.values

// 範囲
for i in 0..<10 { ... }
for i in 0...10 { ... }
let sub = arr[1..<3]
let suffix = arr[2...]

// Optional
let s: String? = "hello"
s?.count
s ?? "default"
if let s = s { ... }
guard let s = s else { return }

74. Swiftの未来とJEP系の議論

進行中のSE(Swift Evolution Proposal):
  - SE-0413: Typed throws(Result風の型付き例外)
  - SE-0432: より良い借用セマンティクス
  - 多数のconcurrency改善

長期方向:
  - LLMコーディング支援
  - サーバサイド成熟
  - Embedded普及
  - WASMターゲット安定

この章でいう「JEP系」はJavaのJEPそのものではなく、Swift Evolution Proposalを通じた言語進化の見方として捉えるとよい。Swiftは、Appleの製品開発に合わせて進むだけでなく、公開提案とレビューを通じて仕様を少しずつ変えてきた。変更は言語機能だけでなく、標準ライブラリ、並行性、所有権、パッケージ管理にも及ぶ。

近年の方向性は、並行性の安全性、所有権による性能改善、組み込み環境、サーバサイド、クロスプラットフォームの拡張である。Swift 6以降は、データ競合をコンパイル時に発見する方向が強まり、既存コードでは警告や修正が増える可能性がある。これは移行負担であると同時に、大規模アプリの安全性を上げる機会でもある。

学習者にとって重要なのは、未来の機能を追いすぎないこと。まずOptional、値型、Protocol、エラー処理、async/await、actorを堅実に使えるようにし、そのうえでMacro、ownership、Embedded Swift、WASMなどを必要に応じて読むと、言語の変化を実務に結びつけやすい。


76. Swift構文の補足

Trailing ClosureとMultiple Trailing Closures

animate(duration: 0.3) {
    self.alpha = 0
} completion: { _ in
    self.removeFromSuperview()
}

Subscript

struct Matrix {
    var data: [[Double]]
    
    subscript(row: Int, col: Int) -> Double {
        get { data[row][col] }
        set { data[row][col] = newValue }
    }
}

var m = Matrix(data: [[1,2],[3,4]])
m[0, 1] = 99

Operator overloading

struct Vec {
    let x, y: Double
    
    static func + (lhs: Vec, rhs: Vec) -> Vec {
        Vec(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    
    static prefix func - (v: Vec) -> Vec {
        Vec(x: -v.x, y: -v.y)
    }
}

Custom operators

infix operator |>: AdditionPrecedence

func |> <T, U>(value: T, transform: (T) -> U) -> U {
    transform(value)
}

let result = 5 |> { $0 * 2 } |> { $0 + 1 }   // 11

Result builders(簡潔形)

@resultBuilder
struct ArrayBuilder<T> {
    static func buildBlock(_ items: T...) -> [T] { items }
}

@ArrayBuilder<Int>
var numbers: [Int] {
    1
    2
    3
}

Swiftエコシステムの補足

Apple公式ドキュメント

Swift言語の公式リファレンス: Apple Developer(https://developer.apple.com/)が提供するSwiftドキュメント群は、言語の全機能、標準ライブラリ、フレームワーク統合について、実装者による正式な解説を含む業界最高権威のリソースです。2026年時点で、Swift 6系の言語機能、Concurrencyモデル(async/await、Sendable)、メモリ安全性(ownership semantics)などについて最新の資料が整備されています。

Swift Language Guide: Apple Developer Documentation内の「Swift Language Guide」セクションは、以下のトピックについて体系的な説明を提供します:

  • Basic Operators and Control Flow
  • Functions and Closures
  • Enumerations and Structures
  • Classes and Object-Oriented Programming
  • Error Handling
  • Concurrency (async/await, Tasks, Actors)
  • Memory Safety and Ownership
  • Generics and Protocols
  • Access Control and Visibility
  • Package Management and Modularity

バージョン固有の差異: Apple Developerドキュメントは、Swiftのメジャーバージョン(5.9, 6.0など)ごとに差異を明記しており、「この機能はSwift 5.5以降で利用可能」「Swift 6から動作が変更」といった情報が集約されています。

Swift.orgと言語設計

オープンソースのSwift仕様: Swift.org(https://swift.org/)は、Appleがオープンソース化したSwift言語の公式サイトであり、言語の設計・進化についての透明性を確保するプラットフォームです。以下のリソースが提供されています:

  • Swift Evolution Process: SEXXXX形式の提案(Proposal)を通じて言語の新機能が議論・採択されるプロセス
  • Source Code: GitHub上に公開されたコンパイラ、標準ライブラリ、ランタイムのソースコード
  • Release Notes: バージョンごとの機能追加・廃止・変更を記載したドキュメント
  • Language Reference: 言語の形式的定義

Protocol Buffersとの連携: Swift言語の進化はSE(Swift Evolution)番号で追跡されており、例えば:

  • SE-0296: Async/await
  • SE-0306: Async Sequences
  • SE-0313: Regex Literals
  • SE-0324: Flexible Result Types in Property Wrappers などが段階的に統合されています。

Swift Server Working Group

サーバーサイドSwiftの標準化: Swift-server.github.io(https://swift-server.github.io/)が管理するSwift Server Working Groupは、以下の領域でSwiftの企業向け使用を推進する資料を提供しています:

  • HTTP Server APIs: Standardized HTTP server implementation
  • Logging: Unified logging framework for server applications
  • Monitoring and Observability: Metrics and tracing standards
  • Security: TLS/SSL configuration, authentication patterns
  • Package Ecosystem: Recommended server-side libraries
  • Performance: Benchmarking and optimization guidelines

サーバーサイドのユースケース: Vapor、Kitura、Perfect などのサーバーフレームワークは、このWGの標準化作業に基づいており、エンタープライズアプリケーション開発での信頼性を確保しています。

ダウンロードとインストール

公式Swift配布: download.swift.org(https://download.swift.org/)は、Swift言語およびSwiftコンパイラツールチェーンの公式配布サイトです。以下のプラットフォーム向けに最新版と過去のリリースが提供されています:

  • macOS (Intel and Apple Silicon)
  • Linux (Ubuntu LTS variants)
  • Windows (experimental)
  • WebAssembly (experimental)
  • ARM (Raspberry Pi等)

バージョン管理: 各ダウンロード版には、「Swift 5.9.2」「Swift 6.0.0」など、厳密なセマンティックバージョニングが付与されており、プロジェクトの互換性要件に合わせた版の選択が可能です。

GitHub連携とコミュニティ

オープンソースプロジェクト: github.comでホストされるSwift関連プロジェクト群には、以下のような重要なリソースが含まれています:

  • apple/swift: コンパイラ、ランタイム、標準ライブラリのソースコード
  • apple/swift-package-manager: パッケージマネージャの実装
  • apple/swift-corelibs-foundation: Foundation フレームワークのSwift実装
  • swiftlang/swift-syntax: AST解析とコード生成ツール
  • apple/swift-testing: 標準テストフレームワーク(XCTestの後継)

コミュニティ提案: Swift Evolution提案の議論は主にGitHubのissueとして行われ、言語設計の透明性と参加可能性が確保されています。新しい機能提案(New Protocol Composition、Generalized type-erased Containers等)は、実装前の段階で広くコミュニティの意見を求める形式が取られています。

まとめ

Swiftは、型安全性、値型、Protocol、Optional、並行処理を軸に、Appleプラットフォームで安全かつ表現力の高いアプリケーションを書くための言語です。UIKitやSwiftUIの使い方だけでなく、状態管理、非同期処理、エラー処理を言語機能と結びつけて理解することが重要です。

参考文献

公式・標準

解説・補助