Skip to content
cccskills
FreePublished on GitHub

swiftui-patterns

SwiftUIアーキテクチャパターン、@Observableによる状態管理、ビュー合成、ナビゲーション、パフォーマンス最適化、モダンなiOS/macOS UIのベストプラクティス。

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md7.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

SwiftUIパターン

Appleプラットフォーム上で宣言的かつ高性能なUIを構築するためのモダンなSwiftUIパターン。Observationフレームワーク、ビュー合成、型安全なナビゲーション、パフォーマンス最適化をカバー。

アクティベーション条件

  • SwiftUIビューの構築と状態管理(@State、@Observable、@Binding)
  • NavigationStackによるナビゲーションフローの設計
  • ビューモデルとデータフローの構造化
  • リストや複雑なレイアウトのレンダリングパフォーマンス最適化
  • SwiftUIにおけるEnvironment値と依存性注入の操作

状態管理

プロパティラッパーの選択

適合する最もシンプルなラッパーを選択:

ラッパーユースケース
@Stateビューローカルの値型(トグル、フォームフィールド、シート表示)
@Binding親の@Stateへの双方向参照
@Observableクラス + @State複数プロパティを持つ所有モデル
@Observableクラス(ラッパーなし)親から渡される読み取り専用参照
@Bindable@Observableプロパティへの双方向バインディング
@Environment.environment()で注入される共有依存関係

@Observable ViewModel

@Observableを使用(ObservableObjectではなく)— プロパティレベルの変更を追跡するため、SwiftUIは変更されたプロパティを読み取るビューのみを再レンダリング:

@Observable
final class ItemListViewModel {
    private(set) var items: [Item] = []
    private(set) var isLoading = false
    var searchText = ""

    private let repository: any ItemRepository

    init(repository: any ItemRepository = DefaultItemRepository()) {
        self.repository = repository
    }

    func load() async {
        isLoading = true
        defer { isLoading = false }
        items = (try? await repository.fetchAll()) ?? []
    }
}

ViewModelを使用するビュー

struct ItemListView: View {
    @State private var viewModel: ItemListViewModel

    init(viewModel: ItemListViewModel = ItemListViewModel()) {
        _viewModel = State(initialValue: viewModel)
    }

    var body: some View {
        List(viewModel.items) { item in
            ItemRow(item: item)
        }
        .searchable(text: $viewModel.searchText)
        .overlay { if viewModel.isLoading { ProgressView() } }
        .task { await viewModel.load() }
    }
}

Environmentインジェクション

@EnvironmentObjectを@Environmentに置き換え:

// 注入
ContentView()
    .environment(authManager)

// 使用
struct ProfileView: View {
    @Environment(AuthManager.self) private var auth

    var body: some View {
        Text(auth.currentUser?.name ?? "Guest")
    }
}

ビュー合成

無効化を制限するためにサブビューを抽出

ビューを小さく焦点を絞った構造体に分割。状態が変更されると、その状態を読み取るサブビューのみが再レンダリング:

struct OrderView: View {
    @State private var viewModel = OrderViewModel()

    var body: some View {
        VStack {
            OrderHeader(title: viewModel.title)
            OrderItemList(items: viewModel.items)
            OrderTotal(total: viewModel.total)
        }
    }
}

再利用可能なスタイリングのためのViewModifier

struct CardModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.regularMaterial)
            .clipShape(RoundedRectangle(cornerRadius: 12))
    }
}

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

ナビゲーション

型安全なNavigationStack

プログラム的で型安全なルーティングのためにNavigationStackとNavigationPathを使用:

@Observable
final class Router {
    var path = NavigationPath()

    func navigate(to destination: Destination) {
        path.append(destination)
    }

    func popToRoot() {
        path = NavigationPath()
    }
}

enum Destination: Hashable {
    case detail(Item.ID)
    case settings
    case profile(User.ID)
}

struct RootView: View {
    @State private var router = Router()

    var body: some View {
        NavigationStack(path: $router.path) {
            HomeView()
                .navigationDestination(for: Destination.self) { dest in
                    switch dest {
                    case .detail(let id): ItemDetailView(itemID: id)
                    case .settings: SettingsView()
                    case .profile(let id): ProfileView(userID: id)
                    }
                }
        }
        .environment(router)
    }
}

パフォーマンス

大規模コレクションにはLazyコンテナを使用

LazyVStackとLazyHStackは表示時にのみビューを作成:

ScrollView {
    LazyVStack(spacing: 8) {
        ForEach(items) { item in
            ItemRow(item: item)
        }
    }
}

安定した識別子

ForEachでは常に安定したユニークなIDを使用 — 配列インデックスの使用を避ける:

// Identifiable適合または明示的なidを使用
ForEach(items, id: \.stableID) { item in
    ItemRow(item: item)
}

body内での高コスト処理を避ける

  • body内でI/O、ネットワーク呼び出し、重い計算を決して行わない
  • 非同期作業には.task {}を使用 — ビューが消えると自動的にキャンセル
  • スクロールビュー内での.sensoryFeedback()や.geometryGroup()の使用は控えめに
  • リスト内の.shadow()、.blur()、.mask()を最小限に — オフスクリーンレンダリングを発生させる

Equatable適合

高コストなbodyを持つビューでは、不要な再レンダリングをスキップするためにEquatableに適合:

struct ExpensiveChartView: View, Equatable {
    let dataPoints: [DataPoint] // DataPointはEquatableに適合必要

    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.dataPoints == rhs.dataPoints
    }

    var body: some View {
        // 複雑なチャートレンダリング
    }
}

プレビュー

高速なイテレーションのためにインラインモックデータと#Previewマクロを使用:

#Preview("Empty state") {
    ItemListView(viewModel: ItemListViewModel(repository: EmptyMockRepository()))
}

#Preview("Loaded") {
    ItemListView(viewModel: ItemListViewModel(repository: PopulatedMockRepository()))
}

避けるべきアンチパターン

  • 新しいコードでObservableObject / @Published / @StateObject / @EnvironmentObjectを使用 — @Observableに移行
  • bodyやinitに直接非同期処理を入れる — .task {}または明示的なロードメソッドを使用
  • データを所有しない子ビュー内で@Stateとしてビューモデルを作成 — 代わりに親から渡す
  • AnyView型消去を使用 — 条件付きビューには@ViewBuilderやGroupを推奨
  • アクターとの間でデータを渡す際のSendable要件を無視

参考

スキルswift-actor-persistenceでアクターベースの永続化パターンを参照。 スキルswift-protocol-di-testingでプロトコルベースのDIとSwift Testingによるテストを参照。

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

コーディングエージェント(Claude Code、Aider、Codexなど)のカスタムタスクによる直接比較。合格率、コスト、時間、一貫性メトリクスを測定

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

AIエージェントのアクションスペース、ツール定義、オブザベーションフォーマットを設計・最適化し、タスク完了率を向上させる

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

評価ファーストの実行、分解、コスト考慮型モデルルーティングによるエージェンティックエンジニアとしての運用

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

AIエージェントが実装出力の大部分を生成するチームのためのエンジニアリング運用モデル

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

AI支援開発のためのリグレッションテスト戦略。データベース依存なしのサンドボックスモードAPIテスト、自動バグチェックワークフロー、同一モデルがコードの作成とレビューを行う際のAIの盲点を検出するパターン。

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

AndroidおよびKotlin Multiplatformプロジェクトのためのクリーンアーキテクチャパターン — モジュール構成、依存関係ルール、UseCase、Repository、データレイヤーパターン

The English summary is being prepared. Showing the original description.

hage-oyaji/everything-claude-code-jp-local3Updated Mar 29, 2026

See all skills by hage-oyaji

Report a problem with this skill