ワンクリックで
swift-mvvm
Swift/iOS MVVM 패턴 안내. Model·View·ViewModel 구조, 바인딩(Combine/@Published), 서비스/저장소 연동 시 사용.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Swift/iOS MVVM 패턴 안내. Model·View·ViewModel 구조, 바인딩(Combine/@Published), 서비스/저장소 연동 시 사용.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
NAVER API HUB 검색 스킬. 네이버 클라우드 NAVER API HUB로 블로그·뉴스·지역·웹·이미지 등 검색 및 검색어 트렌드 조회. "네이버 API HUB", "네이버 검색 API", "블로그 검색", "검색어 트렌드" 요청에 활성화.
Git Flow 브랜치·커밋 규칙(변경 요약은 한국어 필수). 커밋, 브랜치 생성, Git Flow, 커밋 메시지 작성 요청 시 사용.
UIKit MVVM에서 Combine 바인딩, @Published, PassthroughSubject, ObservableObject 역할, sink/cancellables 패턴. UI 상태는 @Published, 화면 이동/토스트/Alert 같은 일회성 이벤트는 PassthroughSubject. SwiftUI vs UIKit ObservableObject 차이, Combine 바인딩 설계 시 사용.
Swift Concurrency 경계 설정, @MainActor, Task 배치, UIKit MVVM async API 설계. ViewModel, @MainActor, Task, async await, Combine 바인딩, isLoading 처리 시 사용.
SwiftUI MVVM에서 @Observable/ObservableObject 선택, @StateObject/@ObservedObject/@EnvironmentObject 사용 기준, .task/.refreshable와 취소 처리, @MainActor 상태 갱신 규칙 설계 시 사용.
웹 검색 기반 조사형 AI 에이전트의 환각을 줄이는 검증 중심 워크플로우. "검색해줘", "조사해줘", "최신 정보 찾아줘", "근거 있는 요약", "사실 확인" 같은 요청에서 사용.
| name | swift-mvvm |
| description | Swift/iOS MVVM 패턴 안내. Model·View·ViewModel 구조, 바인딩(Combine/@Published), 서비스/저장소 연동 시 사용. |
FeatureName/
├── Model/ # 도메인 모델, DTO
├── View/ # SwiftUI View 또는 UIKit ViewController
├── ViewModel/ # 뷰 상태·로직, 입력 처리
└── Service/ # (선택) API, DB, 비즈니스 로직
순수 데이터. Codable로 직렬화 시 DTO와 동일하게 두거나, 화면용 모델만 둘 수 있음.
struct User: Equatable {
let id: String
let name: String
}
// API 응답이 다르면 DTO로 변환
struct UserDTO: Codable {
let id: String
let name: String
var toModel: User {
User(id: id, name: name)
}
}
뷰 상태와 사용자 액션 처리. ObservableObject + @Published 또는 @Observable(iOS 17+) 사용.
@MainActor
final class UserViewModel: ObservableObject {
@Published var user: User?
@Published var error: Error?
@Published var isLoading = false
private let userService: UserServiceProtocol
init(userService: UserServiceProtocol) {
self.userService = userService
}
func loadUser(id: String) async {
isLoading = true
defer { isLoading = false }
do {
user = try await userService.fetchUser(id: id)
} catch {
self.error = error
}
}
}
@Observable
@MainActor
final class UserViewModel {
var user: User?
var error: Error?
var isLoading = false
private let userService: UserServiceProtocol
init(userService: UserServiceProtocol) {
self.userService = userService
}
func loadUser(id: String) async {
isLoading = true
defer { isLoading = false }
do {
user = try await userService.fetchUser(id: id)
} catch {
self.error = error
}
}
}
ViewModel만 주입하고, 상태는 바인딩으로 반영.
struct UserView: View {
@StateObject private var viewModel: UserViewModel
var body: some View {
Group {
if viewModel.isLoading { ProgressView() }
else if let user = viewModel.user { Text(user.name) }
else if let error = viewModel.error { Text(error.localizedDescription) }
}
.task { await viewModel.loadUser(id: userId) }
}
}
struct UserView: View {
@State private var viewModel: UserViewModel
let userId: String
var body: some View {
// @Observable이면 @State만으로 자동 구독
Group {
if viewModel.isLoading { ProgressView() }
else if let user = viewModel.user { Text(user.name) }
else if let error = viewModel.error { Text(error.localizedDescription) }
}
.task { await viewModel.loadUser(id: userId) }
}
}
final class UserViewController: UIViewController {
private let viewModel: UserViewModel
private let userId: String
init(viewModel: UserViewModel, userId: String) {
self.viewModel = viewModel
self.userId = userId
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
Task { await viewModel.loadUser(id: userId) }
}
private func bindViewModel() {
viewModel.$user
.receive(on: DispatchQueue.main)
.sink { [weak self] user in
self?.updateUI(user: user)
}
.store(in: &cancellables)
}
}
API·DB 접근. ViewModel은 이 레이어만 의존하도록 하면 테스트·교체가 쉬움.
protocol UserServiceProtocol {
func fetchUser(id: String) async throws -> User
}
final class UserService: UserServiceProtocol {
private let apiClient: APIClient
func fetchUser(id: String) async throws -> User {
let dto: UserDTO = try await apiClient.request(.user(id))
return dto.toModel
}
}
final class DIContainer {
lazy var userService: UserServiceProtocol = UserService(apiClient: apiClient)
func makeUserViewModel() -> UserViewModel {
UserViewModel(userService: userService)
}
}
PascalCase.swift (타입명과 동일)XxxViewModelXxxServiceProtocol (프로토콜), XxxService (구현체)XxxView (SwiftUI), XxxViewController (UIKit)swift-combine-uikit-mvvmswift-concurrency-mvvmswiftui-mvvmswift-mvvm은 공통 골격(계층/DI/네이밍) 중심으로 사용하고,
플랫폼별 구현 디테일은 위 전문 스킬로 분기한다.