Leveraging Swift Package Manager to Maintain Clean Architecture Layers in iOS Development — Part 2

DomainLayer Implementation

Muhammad Alfiansyah
3 min readNov 26, 2024

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

Dependency Diagram

--

--

No responses yet