🌟

SwiftUIチュートリアルまとめ:データロードからViewModifierまで

に公開

この記事では、これまでのチュートリアルの流れをまとめ、以下のトピックを解説します。

  • Decodableジェネリクス関数でのJSON読み込み
  • Bundleからのリソース取得とguard文
  • JSONDecoderによるデコード処理
  • SwiftUIでのView初期化エラーと対策
  • OptionalとOptionalチェーン
  • ListとIdentifiableプロトコルによるリスト描画
  • ViewModifierによるスタイル共通化

1. Decodableジェネリクスでのデータロード

まず、任意のDecodableに準拠した型を読み込む汎用関数loadを定義します。

func load<T: Decodable>(_ filename: String) -> T {
    // JSONファイルをバンドルから取得
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else {
        fatalError("File \(filename) not found in main bundle.")
    }
    // Data読み込み
    let data: Data
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename):\n\(error)")
    }
    // デコード
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

解説

  1. T: Decodable

    • ジェネリクス+プロトコル制約で任意の型を扱える
  2. Bundle.main.url

    • リソース取得用メソッド
  3. guard let

    • 存在チェック→失敗時は早期終了
  4. JSONDecoder

    • T.selfからデコード

2. SwiftUIの初期化エラー:Missing argument

LandmarkRow(landmark: Landmark) のように、必須プロパティに初期値がない場合は初期化時に必ず引数を渡す必要があります。たとえば:

struct LandmarkRow: View {
    var landmark: Landmark  // stored property, no default
    var body: some View { /* ... */ }
}

#Preview {
    LandmarkRow(landmark: ModelData().landmarks[0])
}

要点

  • var landmark: Landmark は「箱の宣言」であって代入ではない
  • 初期化時に landmark を指定しないとエラーになる
  • プレビューでは適切なインスタンスを渡す

3. OptionalとOptional chaining

Swiftの?はOptional型とOptionalチェーンを表します。

  • 型宣言:var name: String?
  • チェーン:name?.count

4. ListとIdentifiable

List(landmarks, id: \.id) { landmark in
    LandmarkRow(landmark: landmark)
}
  • id: \.id

    • KeyPathでIDを指定
    • 差分検出とアニメーションの基盤
  • Identifiable準拠でid: \.id省略可


5. ViewModifierによるスタイル共通化

struct MainButtonModifier: ViewModifier {
    let backgroundColor: Color
    func body(content: Content) -> some View {
        content
            .padding()
            .background(backgroundColor)
    }
}

extension View {
    func mainButtonStyle(bg: Color) -> some View {
        self.modifier(MainButtonModifier(backgroundColor: bg))
    }
}
  • ViewModifierで装飾を切り出し
  • .modifierかextensionで適用

以上が今回のまとめです!次回はさらに詳細なカスタムモディファイア実装パターンを紹介予定😉

Discussion