docs/ja-JP/skills/swiftui-patterns/SKILL.md
@Observableを使用した状態管理、ビュー合成、ナビゲーション、パフォーマンス最適化、モダンなiOS/macOS UIのベストプラクティスを備えたSwiftUIアーキテクチャパターン。
npx skillsauth add affaan-m/everything-claude-code swiftui-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Appleプラットフォーム向けのモダンなSwiftUIパターン。宣言的で高性能なユーザーインターフェースを構築するために使用する。Observationフレームワーク、ビュー合成、型安全なナビゲーション、パフォーマンス最適化をカバーする。
@State、@Observable、@Binding)NavigationStack を使用したナビゲーションフローを設計する場合最も適したシンプルなラッパーを選択する:
| ラッパー | 使用場面 |
|---------|----------|
| @State | ビューローカルな値型(トグル、フォームフィールド、シート表示) |
| @Binding | 親ビューの @State への双方向参照 |
| @Observable クラス + @State | 複数のプロパティを持つ所有モデル |
| @Observable クラス(ラッパーなし) | 親ビューから渡される読み取り専用参照 |
| @Bindable | @Observable プロパティへの双方向バインディング |
| @Environment | .environment() で注入された共有依存関係 |
ObservableObject ではなく @Observable を使用する——プロパティレベルの変更を追跡するため、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()) ?? []
}
}
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() }
}
}
@EnvironmentObject の代わりに @Environment を使用する:
// Inject
ContentView()
.environment(authManager)
// Consume
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)
}
}
}
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 と 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)
}
}
LazyVStack と LazyHStack はビューが表示される時のみ作成する:
ScrollView {
LazyVStack(spacing: 8) {
ForEach(items) { item in
ItemRow(item: item)
}
}
}
ForEach では常に安定した一意のIDを使用する——配列インデックスは避ける:
// Use Identifiable conformance or explicit id
ForEach(items, id: \.stableID) { item in
ItemRow(item: item)
}
body 内でI/O、ネットワーク呼び出し、重い計算を絶対に実行しない.task {} を使用する——ビューが消えると自動的にキャンセルされる.sensoryFeedback() と .geometryGroup() を慎重に使用する.shadow()、.blur()、.mask() の使用を最小化する——画面外レンダリングを引き起こすbodyの計算が高コストなビューには、不要な再レンダリングをスキップするために Equatable に準拠する:
struct ExpensiveChartView: View, Equatable {
let dataPoints: [DataPoint] // DataPoint must conform to Equatable
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.dataPoints == rhs.dataPoints
}
var body: some View {
// Complex chart rendering
}
}
インラインのモックデータで #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 を優先するActorベースの永続化パターンについては、スキル swift-actor-persistence を参照。
プロトコルベースのDIとSwift Testingを使用したテストについては、スキル swift-protocol-di-testing を参照。
development
Share durable, inspectable context and handoffs between Claude, Codex, Hermes, Cursor, OpenCode, and other agents through the local ECC Memory Vault. Use when an agent must save work state, transfer context, resume another agent's task, or search shared project knowledge.
development
Use when multiple consumers and providers must evolve an API or event schema without field drift, integration surprises, or one side silently redefining the interface.
tools
Query live GPU inventory, submit an authenticated Itô fixed-rate RFQ, inspect RFQ or procurement status, and run explicitly gated node qualification through the separately installed canonical CLI. Use when a user asks to find H100/H200 capacity, request a fixed compute rate, check Itô compute status, or validate GPU nodes.
data-ai
Instinct-based learning system that observes sessions via hooks, creates atomic instincts with confidence scoring, and evolves them into skills/commands/agents. v2.1 adds project-scoped instincts to prevent cross-project contamination.