モバイル開発におけるSwiftの特徴:概要、主要機能と仕組み

著者: IT Sectr 公開日: 2026-06-25 読了時間: 17 分

SwiftはAppleのプログラミング言語で、Objective-Cのパワーと最新言語の安全性を兼ね備えています。他の多くの言語とは異なり、Swiftは独自の構造を提供します:自動コピー付きの値型、コンパイラレベルの分離を備えたActor、プロパティロジックを再利用するためのProperty Wrapper、そして古典的な継承に代わるプロトコル指向プログラミング。Apple Swiftドキュメントによると、これらの機能によりコードの安全性とパフォーマンスが向上します。

重要なポイント

  • Struct — 値型。代入時にコピーされ、継承をサポートしません。Class — 参照型。参照で渡され、継承とdeinitをサポートします。
  • Actor — コンパイラレベルの分離を備えたスレッドセーフな参照型。コンパイラは異なるタスクからのミュータブルな状態への同時アクセスを禁止します。
  • Property Wrappers — @Published、@State、@BindingがSwiftUIとCombineでプロパティロジックをカプセル化し、ボイラープレートコードを排除します。
  • Genericsを使用すると、特定の型に縛られることなく型安全な関数や構造体を記述できます。プロトコル指向プログラミングは、クラス階層の代わりにextension + protocolを使用します。
  • Enumと関連値 — 状態をモデル化するための強力なツール。各ケースは異なる型の任意のデータセットを格納できます。

SwiftのStruct vs Class:値型と参照型

Swiftでは、structは値型、classは参照型です。この基本的な違いが、代入、関数への受け渡し、メモリ管理の際の動作を決定します。structは代入のたびに完全にコピーされ、classは参照で渡されます—ヒープオブジェクトへのポインタのみがコピーされます。

Structの値セマンティクス

Structは自動的に値セマンティクスを実装します—各インスタンスは独立しています。Structプロパティの変更はvarを介してのみ可能で、プロパティを変更するメソッドはmutatingとマークする必要があります。Structは継承をサポートしませんが、プロトコルに準拠できます。Swiftの標準ライブラリはString、Array、Dictionary、Int、Boolにstructを使用します—すべての基本型は値型です。

swift
struct User {
    let id: Int
    var name: String

    mutating func updateName(_ newName: String) {
        name = newName
    }
}

var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// 完全なコピー — 独立したインスタンス

Classの参照セマンティクス

Classはヒープに格納され、参照の等価性をサポートします。複数の変数が同じオブジェクトを参照できます。ある参照を介した変更は他のすべての参照からも見えます。Classは継承をサポートし、メソッドのオーバーライド(override)を許可し、リソース解放のためのdeinitを持ちます。Swiftでは、structよりもclassの使用頻度は低くなっています—Appleはstructから始め、継承や参照セマンティクスが必要な場合にのみclassに切り替えることを推奨しています。

swift
class Car {
    let model: String
    var speed: Int

    init(model: String, speed: Int) {
        self.model = model
        self.speed = speed
    }

    func accelerate(_ amount: Int) {
        speed += amount
    }
}

let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// 同じ参照 — 変更はどこでも見える

structとclassの選択はデータのセマンティクスに依存します。設定、データモデル、状態にはstructを使用してください—コピーの方が安全で予測可能です。アイデンティティを持つオブジェクト(ユーザーセッション、データベース接続)や継承が必要な場合はclassを使用してください。

Actor — Swiftのスレッドセーフティ

Actorは、ミュータブルな状態を同時アクセスから保護する参照型です。Swiftコンパイラは、actor変数に同じactorコンテキストからのみアクセスできることを保証します—外部からのアクセスはコンパイルエラーになります。これにより、手動ロックなしで言語レベルでのデータ競合を防止します。

Actorの分離

すべてのactorプロパティとメソッドはデフォルトで分離されています。外部からactorメソッドを呼び出すにはawaitが必要です—Swiftは実行をactorに切り替えて戻します。Actorはアクセスを順次同期します:異なるタスクからのリクエストはキューに入れられます。これにより、actor内部で一貫した状態が保証されます。非分離メソッド(nonisolated)はawaitなしで呼び出せますが、ミュータブルなプロパティにはアクセスできません。

swift
actor BankAccount {
    private var balance: Double

    init(initialBalance: Double) {
        balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) throws {
        guard balance >= amount else {
            throw BankError.insufficientFunds
        }
        balance -= amount
    }

    nonisolated func accountInfo() -> String {
        return "銀行口座"
    }
}

let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — アトミック、競合状態なし

Actorは、複数のタスクが同時にデータを読み書きする状態マネージャー、キャッシュ、ネットワークサービスに便利です。NSLockやDispatchQueueを使った手動同期とは異なり、actorはコンパイル時にチェックされるため、間違いを犯すことは不可能です。分離エラーは実行時ではなくコンパイル時に発生します。

Property Wrappers: @Published, @State, @Binding

Property Wrapperは、プロパティの宣言を変更せずに動作を追加するアノテーションです。SwiftUIとCombineは、ビューの状態管理のための組み込みラッパーを提供します。コンパイラはプロパティアクセスをラップするコードを生成し、監視、同期、変換のロジックを追加します。

@Publishedと@State

@Publishedは、ObservableObjectに準拠するクラスで変更を自動公開するために使用されます。Combineのサブスクライバーは更新のたびに通知を受け取ります。@StateはSwiftUIビューのローカル状態です。@Stateが変更されると、SwiftUIは自動的にビューを再描画します。@Bindingは、データを所有せずに親ビューと子ビューの間に双方向の接続を作成します。

swift
import SwiftUI
import Combine

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

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @State private var greeting: String = "こんにちは"
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            CounterDisplay(count: $viewModel.count)
            Text(greeting)
            Button("増加", action: viewModel.increment)
        }
    }
}

struct CounterDisplay: View {
    @Binding var count: Int

    var body: some View {
        Text("カウント: \(count)")
    }
}

カスタムProperty Wrapperは、@propertyWrapperとwrappedValueフィールドを持つ構造体を使用して作成します。これにより、検証、正規化、UserDefaults保存などのロジックを再利用できます。Property Wrapperはボイラープレートコードを大幅に削減し、アノテーションを通じて開発者の意図を明確にします。

swift
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Settings {
    @Clamped(range: 0...100) var volume: Int = 50
}

var s = Settings()
s.volume = 150
// s.volume === 100 — 値が自動的に制限される

Genericsとプロトコル指向プログラミング

Genericsは、型安全性を失わずに柔軟なコードを記述するための強力なSwiftのメカニズムです。ジェネリック関数と型は、制約を満たす任意の型で動作します。プロトコル指向プログラミング(POP)は、protocol + extensionが古典的なクラス継承を置き換え、振る舞いの合成を提供するSwiftのパラダイムです。

ジェネリック制約と関連型

Swiftは、プロトコル(where句)でジェネリックパラメータを制約できます。関連型を持つプロトコルは、プロトコルに対するジェネリックの同等物です:具体的な型は準拠時に決定されます。これは型安全なコレクションとアルゴリズムの基礎です。Swiftの標準ライブラリは積極的にGenericsを使用しています:Array、Dictionary、Optional — すべてジェネリック型です。

swift
protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    mutating func set(_ value: Value, for key: Key)
}

struct MemoryCache<K: Hashable, V>: Cacheable {
    private var storage: [K: V] = [:]

    typealias Key = K
    typealias Value = V

    func get(_ key: K) -> V? {
        return storage[key]
    }

    mutating func set(_ value: V, for key: K) {
        storage[key] = value
    }
}

func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
    for (index, item) in array.enumerated() {
        if item == target {
            return index
        }
    }
    return nil
}

let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — すべてのEquatable型に対する1つのジェネリックメソッド

プロトコルExtensionとデフォルト実装

プロトコルextensionはPOPの重要なメカニズムです。extensionではプロトコルメソッドのデフォルト実装を提供できます。プロトコルに準拠する型はこの実装を無料で取得し、必要に応じてオーバーライドできます。これにより、クラス継承なしで振る舞いの階層を構築できます—Swiftにはない多重継承の代替手段です。

swift
protocol Drivable {
    var speed: Double { get set }
    func drive()
}

extension Drivable {
    func drive() {
        if speed > 0 {
            print("時速\(speed)kmで走行中")
        }
    }
}

struct Bicycle: Drivable {
    var speed: Double = 15
}

struct Plane: Drivable {
    var speed: Double = 900

    func drive() {
        print("時速\(speed)kmで飛行中")
    }
}

let bike = Bicycle()
bike.drive() // "時速15.0kmで走行中" — デフォルト

let plane = Plane()
plane.drive() // "時速900.0kmで飛行中" — オーバーライド

extensionを使用したPOPは、複数のプロトコルを通じた合成を可能にします—型は複数のプロトコルに準拠し、その実装を取得できます。これは階層全体が固定された単一クラス継承よりも柔軟です。Swiftの標準ライブラリはPOP上に構築されています:CustomStringConvertible、Equatable、Hashable、Codable — すべてextensionを持つプロトコルです。

関連値とExtensionを持つEnum

EnumはSwiftでは他の言語よりもはるかに強力な構造です。各ケースは関連値を持つことができます—任意の型のデータセット。extensionとパターンマッチングを組み合わせることで、enumは状態、操作結果、有限状態マシンのモデリングの基盤となります。

関連値とパターンマッチング

関連値により、各ケースが固有のデータを格納できます。たとえば、loadingケースはデータを含まず、successは結果を含み、failureはエラーを含みます。switchによるパターンマッチングは関連値を抽出し、各ケースを処理します。コンパイラは網羅性をチェックします(exhaustive switch)—これにより、忘れられた状態が排除されます。

swift
enum NetworkResult<T> {
    case loading
    case success(T)
    case failure(Error)
}

enum MediaFile {
    case image(url: URL, width: Int, height: Int)
    case video(url: URL, duration: Double)
    case audio(url: URL, bitrate: Int)
}

func handle(_ result: NetworkResult<String>) {
    switch result {
    case .loading:
        print("読み込み中...")
    case .success(let data):
        print("受信: \(data)")
    case .failure(let error):
        print("エラー: \(error.localizedDescription)")
    }
}

let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)

if case .video(let url, let duration) = file {
    print("動画 \(url) 再生時間 \(duration)秒")
}

EnumのExtension

Swiftでは、extensionを使用してenumにメソッド、計算プロパティ、プロトコル準拠を追加できます。元の宣言を変更する必要はありません—コードはクリーンでモジュール化されたままです。extension内のメソッドは関連値を操作し、派生データを計算し、ビジネスロジックを実装できます。

swift
extension MediaFile {
    var fileName: String {
        switch self {
        case .image(let url, _, _),
             .video(let url, _),
             .audio(let url, _):
            return url.lastPathComponent
        }
    }

    var description: String {
        switch self {
        case .image(_, let w, let h):
            return "画像 \(w)x\(h)"
        case .video(_, let d):
            return "動画 \(d)秒"
        case .audio(_, let b):
            return "音声 \(b) kbps"
        }
    }
}

extension MediaFile: Equatable {
    static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
        return lhs.fileName == rhs.fileName
    }
}

let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "動画 120.0s"

関連値 + extensionを持つEnumは、Kotlinのsealedクラスや他の言語のdiscriminated unionの代替です。Swiftはこの組み合わせをResult(標準ライブラリの標準型)、Optional(Optionalは.noneと.someを持つenumです)、およびSwiftUIの状態処理に使用します。パターンマッチングによりすべての状態が処理されることが保証され、実行時エラーは発生しません。

よくある質問

Swiftでstructとclassの違いは何ですか?

Struct — 値型。代入時にコピーされ、継承をサポートしません。Class — 参照型。参照で渡され、継承、deinit、同一性チェック(===)をサポートします。Appleはstructから始め、参照セマンティクスが必要な場合にのみclassに切り替えることを推奨しています。

SwiftのActorとは何ですか?いつ使用しますか?

Actor — スレッドセーフな参照型。コンパイラは異なるコンテキストからのミュータブルな状態への同時アクセスを禁止します。Actorは、複数のタスクからアクセスされる状態マネージャー、キャッシュ、サービスに使用します。Actorメソッドの呼び出しにはawaitが必要です。

SwiftUIで使用されるProperty Wrappersは?

主要なもの:@State — ローカルビュー状態;@Binding — 所有権なしのデータ接続;@Published — ObservableObjectでの変更公開;@StateObject — ObservableObjectの所有;@EnvironmentObject — 環境からの依存関係。カスタムラッパーは@propertyWrapperで作成します。

プロトコル指向プログラミングとは?

POP — クラス継承の代わりにprotocol + extensionが振る舞いを定義するパラダイム。型は複数のプロトコルに準拠し、その実装を無料で取得します。継承よりも合成—より柔軟で安全です。Swiftの標準ライブラリ(Equatable、Codable)はPOP上に構築されています。

Swiftのenumは他の言語のenumとどう違いますか?

Swiftのenumは関連値をサポートします—各ケースは異なる型の任意のデータを格納できます。パターンマッチング(switch)と組み合わせることで、状態、エラー、ドメイン駆動設計のための強力なモデルを提供します。OptionalとResultは関連値を持つ標準的なSwiftのenumです。

まとめ

  • Struct vs Class — 基本的な違い:値型はコピーされ、参照型は参照で渡されます。Structは継承をサポートせず、classはサポートします。Appleはデフォルトでstructを推奨しています。
  • Actor — コンパイラレベルのスレッドセーフティ。分離されたミュータブル状態、awaitによるアクセス、手動ロックなしでのデータ競合からの保護。
  • Property Wrappers — @Published、@State、@Binding、および@propertyWrapperによるカスタム。ボイラープレートコードを削減し、アノテーションで意図を明確にします。
  • Generics — where制約付きの型安全な関数と型。抽象化のためのProtocol + 関連型。Swiftの標準ライブラリは完全にGenerics上に構築されています。
  • プロトコル指向プログラミング — プロトコルextensionによる振る舞いの合成。デフォルト実装により、クラス継承なしでコードを再利用できます。
  • 関連値を持つEnum — 状態のための強力なモデル。各ケースは固有のデータを格納します。パターンマッチングによりすべての状態の処理が保証されます。
  • Extension — enumを含む任意の型にメソッド、計算プロパティ、プロトコル準拠を追加します。元の宣言を変更せずにコードはモジュール化されクリーンなままです。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談