Leveraging Swift Package Manager to Maintain Clean Architecture Layers in iOS Development — Part 2
DomainLayer Implementation
Lets build our DomainLayer from Dependency Diagram above.
First lets define DomainLayer Contract with Protocol, we need 2 protocol that is CharacterRepository and CharacterListUsecase.
public protocol CharacterRepository {
func getCharacters() async throws -> ListModel<CharacterModel>
}
public protocol CharacterListUsecase {
func execute() async throws -> ListModel<CharacterModel>
}
DomainLayer
│
├── Repositories
│ ├── CharacterRepository
│
├── Usecases
│ ├── CharacterListUsecase
Then we need to define Models that will consume by PresentationLayer and built by DataLayer. (We use Rick and Morty API)
public struct ListModel<T: Sendable>: Sendable {
public let results: [T]
public init(results: [T]) {
self.results = results
}
}
public struct CharacterModel: Sendable {
public let id: Int
public let name: String
public let status: String
public let species: String
public let gender: String
public let image: String
public init(id: Int, name: String, status: String, species: String, gender: String, image: String) {
self.id = id
self.name = name
self.status = status
self.species = species
self.gender = gender
self.image = image
}
}
Because we will consume it to ViewModel that implement MainActor we cant just use Array CharacterModel, we need to wrap it to sendable object that is why we create ListModel here.
DomainLayer
│
├── Models
│ ├── ListModel
│ ├── CharacterModel
│
├── Repositories
│ ├── CharacterRepository
│
├── Usecases
│ ├── CharacterListUsecase
After that we can implement Usecases or concrete type of CharacterListUsecase.
public final class CharacterListUsecaseImpl: CharacterListUsecase {
private let characterRepository: CharacterRepository
public init(characterRepository: CharacterRepository) {
self.characterRepository = characterRepository
}
public func execute() async throws -> ListModel<CharacterModel> {
try await characterRepository.getCharacters()
}
}
We can add Mock also, for #Preview and Test later.
public final class CharacterListUsecaseMock: CharacterListUsecase {
public init() {}
public func execute() async throws -> ListModel<CharacterModel> {
return ListModel(results: [
CharacterModel(id: 1, name: "Rick Sanchez", status: "Alive", species: "Human", gender: "Male", image: "https://rickandmortyapi.com/api/character/avatar/1.jpeg"),
CharacterModel(id: 2, name: "Morty Smith", status: "Alive", species: "Human", gender: "Male", image: "https://rickandmortyapi.com/api/character/avatar/2.jpeg")
])
}
}
Final structure will be like this
DomainLayer
│
├── Models
│ ├── ListModel
│ ├── CharacterModel
│
├── Repositories
│ ├── CharacterRepository
│
├── Usecases
│ ├── CharacterListUsecase
│ ├── CharacterListUsecaseImpl
│ ├── CharacterListUsecaseMock