Swift
目次
主要項目のみを表示しています。詳細な小見出しは本文内で確認できます。
- 概要
- 1. Swiftとは何か・なぜ生まれたか
- 2. 環境構築とツールチェイン
- 3. 基本構文と型システム
- 4. Optionalとエラーハンドリング
- 5. structとclass(値型と参照型)
- 6. enumとassociated value
- 7. プロトコル指向プログラミング
- 8. ジェネリクス
- 9. クロージャ
- 10. プロパティとproperty wrapper
- 11. extensionとprotocol extension
- 12. 制御フローとパターンマッチング
- 13. コレクションとSequence
- 14. 文字列とUnicode
- 15. メモリ管理(ARC)
- 16. 並行処理(async/await・actor・Task)
- 17. Resultとthrows
- 18. SwiftUI
- 19. Foundationと標準ライブラリ
- 20. Swift Package Manager
- 21. Objective-Cとの相互運用
- 22. テスト戦略(XCTest / Swift Testing)
- 23. Swift on Server / Linux
- 24. Macro(Swift 5.9+)
- 25. パフォーマンスチューニング
- 26. Swift 5.x〜6.0の進化
- 27. よくある落とし穴FAQ
- 28. 学習ロードマップ(30日)
- 29. 用語集
- 発展: Swiftらしい設計
- 実践: UIとアプリ開発
- 応用: 型とツール
- 実例: ネットワークとエラー処理
- 発展: Appleプラットフォーム連携
- 応用: 現場パターン
- Swiftエコシステムの補足
- まとめ
- 参考文献
概要
まず、この章の中心構造を図で確認します。細部に入る前に、どの概念がどこへつながるかをつかむための地図です。
コード例は、そのまま写すためだけのものではありません。直前の本文で「何を確かめる例か」を押さえ、直後の説明で「どの性質が見えるか」を確認してください。実務では、ここに入力の境界、失敗時の挙動、依存する実行環境を足して読むと判断しやすくなります。
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の公式な目標:
- Safe: 未定義動作なし、null安全(Optional)、メモリ安全
- Fast: C / C++ と同等の性能を目指す
- Expressive: モダンな構文(型推論、closures、generics、pattern matching)
- 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)"
}
}
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を見て、原因を切り分ける。
値型は強力だが、常に安いわけではない。Array や String は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. let と var、どっち?
let がデフォルト。変更が必要なときだけ var。
Q2. class と struct、どっち?
「Value Types First」。アイデンティティが必要 / 継承が必要 / 共有状態がある場合のみ class。
Q3. weak と unowned、どっち?
nilになりうるなら weak、絶対に存在する想定なら unowned(高速だがcrashリスク)。
Q4. try? と try!、どっち?
エラーを無視したいなら try?、絶対成功と確信できるなら try!(テスト・プロトタイプのみ)。
Q5. [weak self] 必要?
クロージャがselfを保持し、selfもクロージャを保持するなら必要です。保持関係を確認したうえで付けると判断しやすくなります。
Q6. some View と any 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: 実践
- iOSアプリを1つ完成
- Swift Package Managerで公開
- XCTestでテスト
- App Storeにリリース(任意)
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)
}
}
}
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)
$name で wrapper自身にアクセス。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解除済み
}
}
}
「クロージャがインスタンスを強参照しない」ためのキャプチャリスト。weak か unowned を選ぶ。
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
- swift.org / docs.swift.org
- developer.apple.com/swift
- hackingwithswift.com
- swiftbysundell.com(John Sundell)
- pointfree.co(高度な内容)
コミュニティ
- /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)
- async / await
- Task / TaskGroup
- actor
- @MainActor
- Swift Testing
54. Swift用語集(追加)
あ行
- アクター(actor): データ競合を防ぐ型
- オプショナル(Optional): T? = nil可能型
か行
- コンスタント(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 + Decodable。Swift 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に紐づく」前提で unowned。weak より速いが、解放後アクセスでクラッシュ。
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を一気に消す必要はない。既存の Publisher を AsyncSequence に寄せる、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の使い方だけでなく、状態管理、非同期処理、エラー処理を言語機能と結びつけて理解することが重要です。