Skip to Content
iOS🏗️ ArchitectureDependency Injection

Dependency Injection

1. Tại sao cần DI?

// ❌ 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 } }

2. Protocol-based DI

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")] } }

3. Environment Object

@main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .environmentObject(UserService()) .environmentObject(AuthService()) } } } struct SomeView: View { @EnvironmentObject var userService: UserService }

4. Dependency Container

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()

5. Testing

class UserViewModelTests: XCTestCase { func testLoadUsers() async { let mockService = MockUserService() let viewModel = UserListViewModel(service: mockService) await viewModel.loadUsers() XCTAssertEqual(viewModel.users.count, 1) } }

📝 Benefits

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