Bỏ qua để đến nội dung

Dependency Injection

// ❌ Without DI - hard to test
class ViewModel {
let service = APIService() // Coupled
}
// ✅ With DI - testable
class ViewModel {
let service: APIServiceProtocol
init(service: APIServiceProtocol) {
self.service = service
}
}
protocol UserService {
func getUsers() async throws -> [User]
}
class RealUserService: UserService {
func getUsers() async throws -> [User] {
// API call
}
}
class MockUserService: UserService {
func getUsers() async throws -> [User] {
[User(id: 1, name: "Test")]
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(UserService())
.environmentObject(AuthService())
}
}
}
struct SomeView: View {
@EnvironmentObject var userService: UserService
}
class DependencyContainer {
static let shared = DependencyContainer()
lazy var apiClient: APIClient = {
APIClient(baseURL: "https://api.example.com")
}()
lazy var userRepository: UserRepository = {
UserRepositoryImpl(apiClient: apiClient)
}()
func makeUserListViewModel() -> UserListViewModel {
UserListViewModel(repository: userRepository)
}
}
// Usage
let viewModel = DependencyContainer.shared.makeUserListViewModel()
class UserViewModelTests: XCTestCase {
func testLoadUsers() async {
let mockService = MockUserService()
let viewModel = UserListViewModel(service: mockService)
await viewModel.loadUsers()
XCTAssertEqual(viewModel.users.count, 1)
}
}

  • Loosely coupled code
  • Easy to test with mocks
  • Flexible configuration
  • Clear dependencies