swift-macos について
swift-macosは、Swift 6.3、SwiftUI、SwiftDataを使ったmacOSアプリ開発向けのリファレンススキルです。UI、データ、並行処理、テスト、配布に加え、Foundation Modelsや画面キャプチャ、AppKit連携も扱います。
主な機能
- 01SwiftUIのシーン、ウィンドウ、ネイティブUI
- 02SwiftDataのクエリ、リレーション、マイグレーション
- 03Swiftの並行処理とSendable型
- 04Foundation Models連携のリファレンス
- 05テスト、ビルド、アプリ配布
- 06ScreenCaptureKitとAppKitの連携
活用シーン
- 01ネイティブmacOSアプリのUI構築
- 02SwiftDataモデルとマイグレーションの設計
- 03非同期処理と並行安全性の実装
- 04macOSアプリのテストと配布
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tenequm/ skills --skill swift-macosインストール後、Claude Code で/swift-macosを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdLICENSE.txtreferences/actors-isolation.mdapp-lifecycle.mdappkit-interop.mdapproachable-concurrency.mdarchitecture.mdasync-patterns.mdcloudkit-sync.mdcontainer-context.mdcore-audio-tap.mddistribution.mdfall-2026-releases.mdfoundation-models.mdmigration-guide.mdmigrations.mdmodels-schema.mdrelationships-predicates.mdscreen-capture-audio.mdsendable-safety.mdspm-build.mdstructured-concurrency.mdswiftui-macos.mdsystem-integration.mdtesting.md詳細
macOS アプリ開発 - Swift 6.3
Swift 6.3(最新:6.3.3、Xcode 26.6 に同梱、2026 年 6 月)、SwiftUI、SwiftData、macOS 26 Tahoe(現行バージョン 26.6.2)を使って、ネイティブ macOS アプリを開発します。SwiftData/@Observable には macOS 14 以降、最新の SwiftUI には macOS 15 以降、Liquid Glass と Foundation Models には macOS 26 が必要です。Xcode 26.6 に同梱されている macOS SDK は 26.5 のため、26.6 のみで利用できる API はまだビルドできません。WWDC 2026 のベータスタック(macOS 27、Xcode 27、Swift 6.4、2026 年秋リリース)については、references/fall-2026-releases.md を参照してください。
クイックスタート
import SwiftUI
import SwiftData
@Model
final class Project {
var name: String
var createdAt: Date
// Named ProjectTask, not Task - `Task` would shadow `Swift.Task`
@Relationship(deleteRule: .cascade) var tasks: [ProjectTask] = []
init(name: String) {
self.name = name
self.createdAt = .now
}
}
@Model
final class ProjectTask {
var title: String
var isComplete: Bool
var project: Project?
init(title: String) {
self.title = title
self.isComplete = false
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup("Projects") {
ContentView()
}
.modelContainer(for: [Project.self, ProjectTask.self])
.defaultSize(width: 900, height: 600)
#if os(macOS)
Settings { SettingsView() }
MenuBarExtra("Status", systemImage: "circle.fill") {
MenuBarView()
}
.menuBarExtraStyle(.window)
#endif
}
}
struct ContentView: View {
@Query(sort: \Project.createdAt, order: .reverse)
private var projects: [Project]
@Environment(\.modelContext) private var context
@State private var selected: Project?
var body: some View {
NavigationSplitView {
List(projects, selection: $selected) { project in
NavigationLink(value: project) {
Text(project.name)
}
}
.navigationSplitViewColumnWidth(min: 200, ideal: 250)
} detail: {
if let selected {
DetailView(project: selected)
} else {
ContentUnavailableView("Select a Project",
systemImage: "sidebar.left")
}
}
}
}
シーンとウィンドウ
| シーン | 用途 |
|---|---|
WindowGroup | サイズ変更可能なウィンドウ(複数のインスタンス) |
Window | 単一インスタンスのユーティリティウィンドウ |
Settings | 環境設定(Cmd+,) |
MenuBarExtra | .menu または .window スタイルのメニューバー |
DocumentGroup | ドキュメントベースのアプリ |
ウィンドウを開く:@Environment(\.openWindow) var openWindow; openWindow(id: "about")
シーンのライフサイクル全般については、references/app-lifecycle.md を参照してください。
メニューとコマンド
.commands {
CommandGroup(replacing: .newItem) {
Button("New Project") { /* ... */ }
.keyboardShortcut("n", modifiers: .command)
}
CommandMenu("Tools") {
Button("Run Analysis") { /* ... */ }
.keyboardShortcut("r", modifiers: [.command, .shift])
}
}
テーブル(macOS ネイティブ)
Table(items, selection: $selectedIDs, sortOrder: $sortOrder) {
TableColumn("Name", value: \.name)
TableColumn("Date") { Text($0.date, format: .dateTime) }
.width(min: 100, ideal: 150)
}
.contextMenu(forSelectionType: Item.ID.self) { ids in
Button("Delete", role: .destructive) { delete(ids) }
}
フォーム、ポップオーバー、シート、インスペクター、macOS モディファイアについては、references/swiftui-macos.md を参照してください。
@Observable
@Observable
final class AppState {
var projects: [Project] = []
var isLoading = false
func load() async throws {
isLoading = true
defer { isLoading = false }
projects = try await ProjectService.fetchAll()
}
}
// Use: @State var state = AppState() (owner)
// Pass: .environment(state) (inject)
// Read: @Environment(AppState.self) var state (child)
SwiftData
@Query と #Predicate
@Query(filter: #Predicate<Project> { !$0.isArchived }, sort: \Project.name)
private var active: [Project]
// Dynamic predicate
func search(_ term: String) -> Predicate<Project> {
#Predicate { $0.name.localizedStandardContains(term) }
}
// FetchDescriptor (outside views)
var desc = FetchDescriptor<Project>(predicate: #Predicate { $0.isArchived })
desc.fetchLimit = 50
let results = try context.fetch(desc)
let count = try context.fetchCount(desc)
リレーションシップ
@Model final class Author {
var name: String
@Relationship(deleteRule: .cascade, inverse: \Book.author)
var books: [Book] = []
init(name: String) { self.name = name }
}
@Model final class Book {
var title: String
var author: Author?
@Relationship var tags: [Tag] = [] // many-to-many
init(title: String) { self.title = title }
}
削除ルール:.cascade、.nullify(デフォルト)、.deny、.noAction。
すべての @Model クラスには明示的なイニシャライザーが必要です。マクロによって自動生成されないため、省略すると @Model requires an initializer be provided for '<Type>' エラーが発生します。
スキーマの移行
enum SchemaV1: VersionedSchema { /* ... */ }
enum SchemaV2: VersionedSchema { /* ... */ }
enum MigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
static var stages: [MigrationStage] {
[.lightweight(fromVersion: SchemaV1.self, toVersion: SchemaV2.self)]
}
}
// Apply: .modelContainer(for: Model.self, migrationPlan: MigrationPlan.self)
CloudKit 同期
iCloud の機能を有効にすると、.modelContainer(for: Model.self) によって自動的に同期されます。制約:すべてのプロパティにデフォルト値またはオプショナル値が必要で、一意制約は使用できず、リレーションシップはオプショナルである必要があります。
モデル属性、バックグラウンドコンテキスト、バッチ処理、取り消し/やり直し、テストについては、以下の SwiftData 関連資料を参照してください。
並行処理(Swift 6.2 以降)
デフォルトの MainActor 分離
モジュール全体をメインアクターに切り替えると、すべてのコードがデフォルトでメインアクター上で実行されます。
// Package.swift
.executableTarget(name: "MyApp", swiftSettings: [
.defaultIsolation(MainActor.self),
])
または Xcode で、Build Settings > Swift Compiler > Default Isolation > MainActor を選択します。
@concurrent
バックグラウンド実行する関数に指定します。
@concurrent
func processFile(_ url: URL) async throws -> Data {
let data = try Data(contentsOf: url)
return try compress(data) // runs off main actor
}
// After await, automatically back on main actor
let result = try await processFile(fileURL)
CPU 負荷の高い処理、I/O、UI に触れない処理に使用します。
アクター
actor DocumentStore {
private var docs: [UUID: Document] = [:]
func add(_ doc: Document) { docs[doc.id] = doc }
func get(_ id: UUID) -> Document? { docs[id] }
nonisolated let name: String
}
// Requires await: let doc = await store.get(id)
構造化並行処理
// Parallel with async let
func loadDashboard() async throws -> Dashboard {
async let profile = fetchProfile()
async let stats = fetchStats()
return try await Dashboard(profile: profile, stats: stats)
}
// Dynamic with TaskGroup
func processImages(_ urls: [URL]) async throws -> [NSImage] {
try await withThrowingTaskGroup(of: (Int, NSImage).self) { group in
for (i, url) in urls.enumerated() {
group.addTask { (i, try await loadImage(url)) }
}
var results = [(Int, NSImage)]()
for try await r in group { results.append(r) }
return results.sorted { $0.0 < $1.0 }.map(\.1)
}
}
Sendable
struct Point: Sendable { var x, y: Double } // value types: implicit
final class Config: Sendable { let apiURL: URL } // final + immutable
actor SharedState { var count = 0 } // mutable: use actors
// Enable strict mode: .swiftLanguageMode(.v6) in Package.swift
AsyncSequence と Observations
// Stream @Observable changes (macOS 26+ / iOS 26+, SE-0475)
// Observations uses a closure init, not Observations(of:).
let progresses = Observations { manager.progress }
for await p in progresses { print(p) }
// Typed NotificationCenter (macOS 26+)
struct DocSaved: NotificationCenter.MainActorMessage {
typealias Subject = Document
static var name: Notification.Name { .init("DocSaved") }
let id: UUID
}
NotificationCenter.default.post(DocSaved(id: document.id), subject: document)
let token = NotificationCenter.default.addObserver(of: document, for: DocSaved.self) { msg in
refresh(msg.id)
}
並行処理の詳細については、以下の並行処理関連資料を参照してください。
Foundation Models(macOS 26 以降)
デバイス上で動作する約 3B の LLM。無料、オフライン、プライベートです。
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "Summarize: \(text)")
print(response.content) // respond() returns Response<Content>, not Content
// Structured output
@Generable struct Summary { var title: String; var points: [String] }
let result = try await session.respond(to: prompt, generating: Summary.self)
let summary: Summary = result.content
ツール呼び出し、ストリーミング、セッションについては、references/foundation-models.md を参照してください。
テスト
import Testing
@Suite("Project Tests")
struct ProjectTests {
@Test("creates with defaults")
func create() {
let p = Project(name: "Test")
#expect(p.name == "Test")
}
@Test("formats sizes", arguments: [(1024, "1 KB"), (0, "0 KB")])
func format(bytes: Int, expected: String) {
#expect(formatSize(bytes) == expected)
}
}
// SwiftData testing
let container = try ModelContainer(
for: Project.self,
configurations: ModelConfiguration(isStoredInMemoryOnly: true)
)
let ctx = ModelContext(container)
ctx.insert(Project(name: "Test"))
try ctx.save()
終了テスト、添付ファイル、UI テストについては、references/testing.md を参照してください。
配布
| 方法 | サンドボックス | 公証 | 審査 |
|---|---|---|---|
| App Store | 必須 | 自動 | あり |
| Developer ID | 推奨 | 必須 | なし |
| Ad-Hoc | なし | なし | ローカルのみ |
xcodebuild archive -scheme MyApp -archivePath MyApp.xcarchive
xcodebuild -exportArchive -archivePath MyApp.xcarchive \
-exportPath ./export -exportOptionsPlist ExportOptions.plist
xcrun notarytool submit ./export/MyApp.dmg \
--apple-id [email protected] --team-id TEAM_ID \
--password @keychain:AC_PASSWORD --wait
xcrun stapler staple ./export/MyApp.dmg
配布ガイド全般については、references/distribution.md を参照してください。
SPM
// swift-tools-version: 6.3
let package = Package(
name: "MyApp",
platforms: [.macOS(.v14)],
targets: [
.executableTarget(name: "MyApp", swiftSettings: [
.swiftLanguageMode(.v6),
.defaultIsolation(MainActor.self),
]),
.testTarget(name: "MyAppTests", dependencies: ["MyApp"]),
]
)
ビルドプラグイン、マクロ、Swift Build については、references/spm-build.md を参照してください。
Liquid Glass(macOS 26)
Xcode 26 SDK で再ビルドしたアプリには、Liquid Glass のスタイルが自動的に適用されます。独自のガラスサーフェスには .glassEffect() を、独自の階層には GlassEffectContainer を使用します。オプトアウトする場合(Xcode 26 のみ):Info.plist で UIDesignRequiresCompatibility = YES を設定すると、従来のビジュアルスタイルが維持されます。これは一時的な移行支援機能です。Xcode 27(ベータ版)で再ビルドしたアプリはオプトアウトできません。このキーは無視され、Liquid Glass が必須になります(references/fall-2026-releases.md を参照)。
ScreenCaptureKit
画面コンテンツ、アプリのオーディオ、マイクをキャプチャします(macOS 12.3 以降):
import ScreenCaptureKit
let content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true)
guard let display = content.displays.first else { return }
// Filter: specific apps only
let filter = SCContentFilter(display: display, including: [targetApp], exceptingWindows: [])
// Configure
let config = SCStreamConfiguration()
config.capturesAudio = true
config.sampleRate = 48000
config.channelCount = 2
config.excludesCurrentProcessAudio = true
// Audio-only: throttle the video pipeline (it always runs). minimumFrameInterval is a
// MINIMUM gap between frames - 1/Int32.max is ~0s, i.e. native refresh rate (60+ fps
// of discarded frames and a full-screen recomposite each). Use 1 fps.
config.width = 2; config.height = 2
config.minimumFrameInterval = CMTime(value: 1, timescale: 1)
let stream = SCStream(filter: filter, configuration: config, delegate: self)
try stream.addStreamOutput(self, type: .screen, sampleHandlerQueue: nil)
try stream.addStreamOutput(self, type: .audio, sampleHandlerQueue: audioQueue)
try await stream.startCapture()
macOS 15 以降:簡単にファイルを録画できる SCRecordingOutput、マイクをキャプチャする config.captureMicrophone。macOS 14 以降:システムのピッカー UI を表示する SCContentSharingPicker、単一フレームをキャプチャする SCScreenshotManager。
完全な API リファレンス、オーディオの書き込み(AVAssetWriter/AVAudioFile)、権限、例については、references/screen-capture-audio.md を参照してください。
AppKit Interop
struct WebViewWrapper: NSViewRepresentable {
let url: URL
func makeNSView(context: Context) -> WKWebView { WKWebView() }
func updateNSView(_ v: WKWebView, context: Context) {
v.load(URLRequest(url: url))
}
}
AppKit で SwiftUI をホストする方法や高度なブリッジについては、references/appkit-interop.md を参照してください。
Architecture
| パターン | 最適な用途 | 複雑さ |
|---|---|---|
| SwiftUI + @Observable | 小規模〜中規模、個人開発 | 低 |
| MVVM + @Observable | 中規模、チーム開発 | 中 |
| TCA | 大規模、厳格なテスト | 高 |
すべてのパターンと例については、references/architecture.md を参照してください。
参考資料
| ファイル | 読むタイミング |
|---|---|
references/fall-2026-releases.md | WWDC 2026 のベータ版スタック:macOS 27、Xcode 27、Swift 6.4、次世代 Foundation Models、Core AI、Spatial Preview、必須の Liquid Glass |
| SwiftUI と macOS | |
references/app-lifecycle.md | ウインドウ管理、シーン、DocumentGroup、MenuBarExtra の注意点、非同期終了処理(およびメインキューから終了するとハングする問題)、LSUIElement の問題 |
references/swiftui-macos.md | サイドバー、インスペクター、テーブル、フォーム、ポップオーバー、シート、検索 |
references/appkit-interop.md | NSViewRepresentable、ホスティングコントローラー、NSHostingSceneRepresentation、サイズ調整/シーンブリッジのオプション、AppKit の Liquid Glass(NSGlassEffectView)、ペーストボードのプライバシー、NSPanel/フローティング HUD |
references/screen-capture-audio.md | ScreenCaptureKit、SCStream の注意点、SCStream のティアダウンに関する危険性、AVAudioEngine のデュアルパイプライン、AVAssetWriter のクラッシュセーフティ、非インターリーブステレオの落とし穴、TCC の注意点、再インストール後の CDHash 劣化状態、SpeechAnalyzer による文字起こし |
references/core-audio-tap.md | プロセスごとのオーディオ向け CATap:タップのみのアグリゲート(HFP 対応)、ドリフト補正、レート変更のアンチパターン、インターリーブステレオのフレーム数の落とし穴、IO プロセスの分離 |
references/system-integration.md | キーボードショートカット、ドラッグ&ドロップ、ファイルアクセス、App Intents(エンティティ、Spotlight、スニペット)、ウィジェットとコントロールセンター、プロセス監視、AXUIElement、プロセスごとの CoreAudio API、ログイン項目、XPC、LSUIElement、os.Logger とサインポスト、プライバシー利用目的の説明 |
references/foundation-models.md | オンデバイス AI:ガイド付き生成、ツール呼び出し、ストリーミング |
references/architecture.md | MVVM、TCA、依存性注入、プロジェクト構成 |
references/testing.md | Swift Testing、終了テスト、添付ファイル、UI テスト、XCTest からの移行 |
references/distribution.md | App Store、Developer ID、公証の注意点、ネストされたバンドルの署名、xcodebuild の署名の落とし穴(警告なしのアドホックビルド)、単独の CLI バイナリ向けの安定した署名識別子、サンドボックス化、ユニバーサルバイナリ |
references/spm-build.md | Package.swift、Swift Build、プラグイン、マクロ、プラグイン/マクロの検証ゲート、Metal ツールチェーンのダウンロード、.app バンドルの手動構築、ObjC 混在ターゲット、CLT のテスト |
| 並行処理 | |
references/approachable-concurrency.md | デフォルトの MainActor 分離、@concurrent、nonisolated async、実行時の注意点 |
references/actors-isolation.md | アクターモデル、グローバルアクター、カスタムエグゼキューター、再入 |
references/structured-concurrency.md | Task、TaskGroup、async let、キャンセル、優先度、名前付きタスク |
references/sendable-safety.md | Sendable プロトコル、データ競合の安全性、@unchecked Sendable とシリアルキュー、@preconcurrency import |
references/async-patterns.md | AsyncSequence、AsyncStream、Observations、継続、Clock |
references/migration-guide.md | GCD から async/await への移行、Combine から AsyncSequence への移行、Swift 6 への移行 |
| SwiftData | |
references/models-schema.md | @Model、@Attribute のオプション、Codable、変換可能な型、外部ストレージ |
references/relationships-predicates.md | 高度なリレーションシップ、逆方向のルール、複合述語 |
references/container-context.md | ModelContainer、ModelContext、バックグラウンドコンテキスト、取り消し/やり直し、一括操作 |
references/cloudkit-sync.md | CloudKit のセットアップ、競合の解決、共有、同期のデバッグ |
references/migrations.md | VersionedSchema、軽量/カスタム移行、Core Data の移行 |