| name | repository |
| description | Cria um novo Repository SwiftUI seguindo o padrao MVVM do projeto (iOS 15+). Use quando o usuario pedir para criar um Repository, camada de dados, ou fonte de dados para um ViewModel. |
Crie um novo Repository SwiftUI seguindo o padrao MVVM do projeto. Target minimo: iOS 15.
Argumentos
Nome em PascalCase (sem o sufixo Repository): $ARGUMENTS
Se $ARGUMENTS estiver vazio, pergunte: "Qual o nome do Repository? (ex: User, Product)"
A partir do nome PascalCase, derive:
<Name>RepositoryProtocol para o protocolo
<Name>Repository para a implementacao concreta
- Arquivo:
Repositories/<Name>Repository.swift
Perguntas a fazer (se nao informadas nos argumentos)
- Model relacionada: qual Model esse Repository gerencia? (ex:
User, Product) — normalmente tem o mesmo nome base
- Operacoes necessarias: quais operacoes o Repository precisa expor? (padrao:
fetchAll, fetchById, create, update, delete) — liste apenas as necessarias
- Fonte de dados: vai consumir
APIClient? Banco local? Ambos? (padrao: APIClient)
- Mock para testes: precisa de
Mock<Name>Repository para testes e previews? (padrao: sim)
O que criar
Repositories/<Name>Repository.swift
import Foundation
protocol <Name>RepositoryProtocol: Sendable {
func fetchAll() async throws -> [<Name>]
func fetch(id: String) async throws -> <Name>
func create(_ item: <Name>) async throws -> <Name>
func update(_ item: <Name>) async throws -> <Name>
func delete(_ item: <Name>) async throws
}
final class <Name>Repository: <Name>RepositoryProtocol {
private let apiClient: APIClientProtocol
init(apiClient: APIClientProtocol = APIClient()) {
self.apiClient = apiClient
}
func fetchAll() async throws -> [<Name>] {
try await apiClient.request(
endpoint: .<name>List,
responseType: [<Name>].self
)
}
func fetch(id: String) async throws -> <Name> {
try await apiClient.request(
endpoint: .<name>(id),
responseType: <Name>.self
)
}
func create(_ item: <Name>) async throws -> <Name> {
try await apiClient.request(
endpoint: .create<Name>(item),
responseType: <Name>.self
)
}
func update(_ item: <Name>) async throws -> <Name> {
try await apiClient.request(
endpoint: .update<Name>(item),
responseType: <Name>.self
)
}
func delete(_ item: <Name>) async throws {
try await apiClient.request(
endpoint: .delete<Name>(item.id),
responseType: EmptyResponse.self
)
}
}
Inclua apenas as funcoes solicitadas pelo usuario. Nao crie operacoes que nao serao usadas.
Mock (se solicitado)
Crie o mock no arquivo Preview Content/Mock<Name>Repository.swift para uso em testes e previews:
import Foundation
final class Mock<Name>Repository: <Name>RepositoryProtocol {
var stubbedItems: [<Name>] = <Name>.mockList
var stubbedItem: <Name> = <Name>.mock
var shouldFail: Bool = false
var error: Error = NetworkError.invalidResponse
func fetchAll() async throws -> [<Name>] {
if shouldFail { throw error }
return stubbedItems
}
func fetch(id: String) async throws -> <Name> {
if shouldFail { throw error }
return stubbedItem
}
func create(_ item: <Name>) async throws -> <Name> {
if shouldFail { throw error }
stubbedItems.append(item)
return item
}
func update(_ item: <Name>) async throws -> <Name> {
if shouldFail { throw error }
if let index = stubbedItems.firstIndex(where: { $0.id == item.id }) {
stubbedItems[index] = item
}
return item
}
func delete(_ item: <Name>) async throws {
if shouldFail { throw error }
stubbedItems.removeAll { $0.id == item.id }
}
}
O mock implementa todas as funcoes do protocolo para garantir que compila mesmo que o ViewModel use apenas algumas delas.
Endpoints necessarios
Lembre o usuario de adicionar os endpoints correspondentes ao enum Endpoint em Services/Networking/Endpoint.swift:
case <name>List
case <name>(String)
case create<Name>(<Name>)
case update<Name>(<Name>)
case delete<Name>(String)
E os respectivos path e method:
case .<name>List: return "/<names>"
case .<name>(let id): return "/<names>/\(id)"
case .create<Name>: return "/<names>"
case .update<Name>(let item): return "/<names>/\(item.id)"
case .delete<Name>(let id): return "/<names>/\(id)"
case .<name>List: return .get
case .<name>: return .get
case .create<Name>: return .post
case .update<Name>: return .put
case .delete<Name>: return .delete
Apos criar os arquivos
Informe o usuario que ainda precisa:
- Adicionar os endpoints ao enum
Endpoint em Services/Networking/Endpoint.swift
- Injetar o repository no
<Name>ViewModel via init
- Garantir que
<Name>.mock e <Name>.mockList existem em Preview Content/PreviewData+<Name>.swift — o mock depende deles
- Verificar se
APIClient.swift e NetworkError.swift existem em Services/Networking/
Regras
- Sempre
Protocol + implementacao concreta — nunca expor a classe diretamente ao ViewModel
- Protocolo marcado como
Sendable para compatibilidade com async/await
- Sem
import SwiftUI — apenas import Foundation
- Sem logica de negocio — apenas mapeamento entre camada de rede e camada de dominio
- Sem tratamento de erro no Repository — propague
throws para o ViewModel tratar
- Dependencias injetadas via
init com valor padrao concreto (para testabilidade)
- Mock sempre em
Preview Content/ — nunca no target principal
- Inclua apenas as operacoes que o ViewModel realmente usa
APIs proibidas (acima do iOS 15)
| NAO usar | Requer | Usar no lugar |
|---|
@Observable | iOS 17 | Nao aplicavel em Repository |
| Actors Swift | iOS 17 | Sendable + async/await |
Checklist de Revisao