Dependency Injection
1. Tại sao cần DI?
Phần tiêu đề “1. Tại sao cần DI?”// ❌ Without DI - hard to testclass ViewModel { let service = APIService() // Coupled}
// ✅ With DI - testableclass ViewModel { let service: APIServiceProtocol
init(service: APIServiceProtocol) { self.service = service }}2. Protocol-based DI
Phần tiêu đề “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
Phần tiêu đề “3. Environment Object”@mainstruct MyApp: App { var body: some Scene { WindowGroup { ContentView() .environmentObject(UserService()) .environmentObject(AuthService()) } }}
struct SomeView: View { @EnvironmentObject var userService: UserService}4. Dependency Container
Phần tiêu đề “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) }}
// Usagelet viewModel = DependencyContainer.shared.makeUserListViewModel()5. Testing
Phần tiêu đề “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
Phần tiêu đề “📝 Benefits”- Loosely coupled code
- Easy to test with mocks
- Flexible configuration
- Clear dependencies