技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
ios【数据与研究】Use this skill when the user asks about iOS development, Swift, SwiftUI, UIKit, iOS architecture, MVVM, Coordinator, Core Data, SwiftData, or iOS testing.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Build iOS native applications using Swift, SwiftUI/UIKit, MVVM+Coordinator architecture, Combine/async-await, Core Data, and XCTest.
User request includes: ios, swift, swiftui, uikit, ios architecture, ios testing, ios native.
A markdown document containing:
Produce the artifact directly. No preamble, no postamble, no explanations. No filler, no hedging, no transitions. Strip articles a/an/the where unambiguous. Compress output — why use many token when few do trick.
4096 tokens
Organize code with feature-based folders: App, Features, Core (Network, Persistence, DI), and Common components.
Create ViewModels with ObservableObject, Coordinators for navigation logic, and dependency injection container.
Use SwiftUI with @StateObject/@Published for reactive UI, or UIKit with programmatic view controllers and delegates.
Configure Core Data or SwiftData with NSPersistentContainer, context management, and save flows.
Cover ViewModels with XCTest unit tests using mock services and UI flows with XCUITest.
Deployment target?
├── iOS 16+ → SwiftUI (native NavigationStack, SwiftData, Observable macros)
├── iOS 14-15 → SwiftUI with UIKit fallbacks for complex interactions
└── iOS 13- → UIKit (SwiftUI limited by deployment target requirement)
Team expertise?
├── New Swift team → SwiftUI (faster to build, less code)
├── Experienced UIKit team → UIKit for complex features, SwiftUI for simpler screens
└── Enterprise app → UIKit (proven, better accessibility, broader testing tools)
App complexity + team size?
├── Simple (<10 screens, CRUD) → MVVM
│ ViewModel @Observable, lightweight, quick to implement
├── Complex state management → TCA (The Composable Architecture)
│ Reducer pattern, testable, unidirectional, own your dependencies
├── Legacy codebase → VIPER
│ Strong separation, best for huge teams, more boilerplate
└── Multi-platform Swift → Clean Swift (VIP)
Testable, clear boundaries, used with Swift on server too
Data complexity?
├── Simple objects, light queries → SwiftData (iOS 17+)
│ Macro-based, Swift-native, auto-save, iCloud sync
├── Relational, existing Core Data → Core Data
│ Mature, complex queries, migration, iCloud, multi-threaded
├── Ephemeral, in-memory → UserDefaults + Codable
└── Full-text search, large datasets → GRDB (SQLite)
Raw SQL control, migrations, combine publishers
Sync needed?
├── Yes → CloudKit + Core Data / SwiftData
├── No → Local persistence only
└── Real-time → Firebase Firestore
Task type?
├── One-shot network call → async/await (structured concurrency)
├── Stream of values → AsyncSequence (AsyncStream, AsyncAlgorithms)
├── Reactive chain → Combine (pre iOS 13-16, being superseded by Swift concurrency)
└── Long-running observation → AsyncStream + @Observable (iOS 17+)
App/
├── Sources/
│ ├── App/
│ │ └── App.swift
│ ├── Features/
│ │ ├── Orders/
│ │ │ ├── OrderListView.swift
│ │ │ ├── OrderDetailView.swift
│ │ │ ├── OrderViewModel.swift
│ │ │ └── OrderCoordinator.swift
│ │ └── ...
│ ├── Core/
│ │ ├── Network/
│ │ ├── Persistence/
│ │ └── DI/
│ └── Common/
│ ├── Extensions/
│ └── Components/
└── Tests/
├── UnitTests/
└── UITests/
// OrderCoordinator.swift
final class OrderCoordinator: Coordinator {
var navigationController: UINavigationController
private let di: DIContainer
init(navigationController: UINavigationController, di: DIContainer) {
self.navigationController = navigationController
self.di = di
}
func start() {
let vm = OrderViewModel(service: di.orderService)
vm.onSelectOrder = { [weak self] order in
self?.showDetail(order)
}
let vc = OrderListViewController(viewModel: vm)
navigationController.pushViewController(vc, animated: false)
}
private func showDetail(_ order: Order) {
let vm = OrderDetailViewModel(order: order)
let vc = OrderDetailViewController(viewModel: vm)
navigationController.pushViewController(vc, animated: true)
}
}
// OrderViewModel.swift
@MainActor
@Observable
final class OrderViewModel {
var orders: [Order] = []
var isLoading = false
var errorMessage: String?
private let service: OrderService
init(service: OrderService) {
self.service = service
}
func loadOrders() async {
isLoading = true
errorMessage = nil
do {
orders = try await service.fetchOrders()
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
// OrderListView.swift
struct OrderListView: View {
@State private var viewModel: OrderViewModel
var body: some View {
Group {
if viewModel.isLoading {
ProgressView()
} else if let error = viewModel.errorMessage {
ContentUnavailableView("Error", systemImage: "exclamationmark.triangle",
description: Text(error))
} else {
List(viewModel.orders) { order in
Text(order.customerName)
}
}
}
.task { await viewModel.loadOrders() }
}
}
final class OrderListViewController: UIViewController {
private let viewModel: OrderViewModel
private let tableView = UITableView()
private var dataSource: UITableViewDiffableDataSource<String, Order>!
init(viewModel: OrderViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
bindViewModel()
}
private func setupTableView() {
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
dataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, order in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = order.customerName
return cell
}
}
private func bindViewModel() {
Task { @MainActor in
for await orders in viewModel.$orders.values {
var snapshot = NSDiffableDataSourceSnapshot<String, Order>()
snapshot.appendSections(["main"])
snapshot.appendItems(orders)
await dataSource.apply(snapshot)
}
}
}
}
// PersistenceController.swift
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "AppModel")
if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { _, error in
if let error = error { fatalError("\(error)") }
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
var context: NSManagedObjectContext { container.viewContext }
func save() {
if context.hasChanges {
try? context.save()
}
}
}
// Background context for heavy operations
func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
let context = container.newBackgroundContext()
context.perform {
block(context)
try? context.save()
}
}
import XCTest
@testable import App
final class OrderViewModelTests: XCTestCase {
var sut: OrderViewModel!
var mockService: MockOrderService!
override func setUp() {
mockService = MockOrderService()
sut = OrderViewModel(service: mockService)
}
override func tearDown() {
sut = nil
mockService = nil
}
func testLoadOrders() async {
mockService.stubOrders = [Order(id: "1", customerName: "Test")]
await sut.loadOrders()
XCTAssertEqual(sut.orders.count, 1)
XCTAssertFalse(sut.isLoading)
}
func testLoadOrdersFailure() async {
mockService.shouldFail = true
await sut.loadOrders()
XCTAssertTrue(sut.orders.isEmpty)
XCTAssertNotNil(sut.errorMessage)
}
}
Instruments Allocations and Leaks instrument each sprintPreserve Vector Data for PDF assetsOSLog for structured logging — never print() in productionMetricKit for production performance monitoring (hang rate, launch time, power)BackgroundTasks framework for deferrable background work (BGAppRefreshTask, BGProcessingTask)equatable() modifier for view equality checksLazyVStack inside ScrollView for large lists (not VStack)@ViewBuilder for conditional content instead of AnyViewPreferenceKey for child-to-parent communication.drawingGroup() for Metal-backed offscreen rendering (complex shapes)id(_:) modifier for stable identity in animations and transitions@ObservedObject vs @StateObject — parent creates, child observesUITableViewDiffableDataSource for declarative, animated updatesUICollectionViewCompositionalLayout for complex, responsive layoutsestimatedRowHeight and UITableView.automaticDimensionprefetchDataSource for loading data before cells appearcellReuseIdentifier + prepareForReuse to reset cell state@MainActor on all ViewModels and UI controllersTask { @MainActor in ... } for dispatching back to main threadAsyncStream for bridging callback-based APIs to async sequenceswithThrowingTaskGroup for parallel async operations (multiple API calls)Task lifetime tied to view lifecycle with .task modifierTask.isCancelled in long-running loopsawait Task.yield() for cooperative cancellation points@Observable classes, not in View bodyguard let or optional chaining — never ! outside tests[weak self] in escaping closures; use WeakReference<T> wrapper for Combine cancellables@Observable macro (iOS 17+) — fewer view updatesTask.detached or DispatchQueue.global()Hand off to mobile/universal/deployment/SKILL.md for deployment.
` interface EventObserver { onEvent(event: T): Promise; }
class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% over 5min | Critical | Page on-call |
| p99 latency | > 2s over 5min | Warning | Investigate |
| Throughput drop | > 50% over 1min | Critical | Check upstream |
| Queue depth | > 1000 over 1min | Warning | Scale consumers |
| Disk usage | > 85% | Warning | Clean or expand |
| Memory usage | > 90% heap | Critical | Restart or scale |
| Anti-Pattern | Symptom | Root Cause | Solution |
|---|---|---|---|
| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |
| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |
| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |
| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).