diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 79a58f7..39a08d2 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -104,6 +104,8 @@ 7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */; }; 7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90B24335A6D009DE740 /* FlagLayer.swift */; }; 7A6F096026DBF588003A965D /* VehicleNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F095F26DBF588003A965D /* VehicleNote.swift */; }; + 7A7097C22C9EC139007CFDCA /* ServiceContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */; }; + 7A7097C62C9EC77A007CFDCA /* ServicePropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7097C52C9EC77A007CFDCA /* ServicePropertyWrapper.swift */; }; 7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7157FF2C43EA6900852088 /* OwnersScreen.swift */; }; 7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158032C43EAA200852088 /* OwnersCoordinator.swift */; }; 7A7158072C44085600852088 /* OsagoScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158062C44085600852088 /* OsagoScreen.swift */; }; @@ -361,6 +363,8 @@ 7A6DD90B24335A6D009DE740 /* FlagLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagLayer.swift; sourceTree = ""; }; 7A6DD90D24337930009DE740 /* PlateNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = ""; }; 7A6F095F26DBF588003A965D /* VehicleNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNote.swift; sourceTree = ""; }; + 7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceContainer.swift; sourceTree = ""; }; + 7A7097C52C9EC77A007CFDCA /* ServicePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicePropertyWrapper.swift; sourceTree = ""; }; 7A7157FF2C43EA6900852088 /* OwnersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersScreen.swift; sourceTree = ""; }; 7A7158032C43EAA200852088 /* OwnersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersCoordinator.swift; sourceTree = ""; }; 7A7158062C44085600852088 /* OsagoScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OsagoScreen.swift; sourceTree = ""; }; @@ -836,6 +840,15 @@ path = Fonts; sourceTree = ""; }; + 7A7097C02C9EC113007CFDCA /* DependencyInjection */ = { + isa = PBXGroup; + children = ( + 7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */, + 7A7097C52C9EC77A007CFDCA /* ServicePropertyWrapper.swift */, + ); + path = DependencyInjection; + sourceTree = ""; + }; 7A7157FE2C43EA5200852088 /* OwnersScreen */ = { isa = PBXGroup; children = ( @@ -957,6 +970,7 @@ 7AF6D1F02677C03B0086EA64 /* AutoCatCore */ = { isa = PBXGroup; children = ( + 7A7097C02C9EC113007CFDCA /* DependencyInjection */, 7A45FB362C2706D000618694 /* Services */, 7A761C06267E8E720005F28F /* ThirdParty */, 7AF6D2292677C3950086EA64 /* Extensions */, @@ -1364,6 +1378,8 @@ 7A64A2182C19E64800284124 /* VehicleOwnershipPeriodDto.swift in Sources */, 7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */, 7A64A2162C19E4CF00284124 /* VehiclePhotoDto.swift in Sources */, + 7A7097C22C9EC139007CFDCA /* ServiceContainer.swift in Sources */, + 7A7097C62C9EC77A007CFDCA /* ServicePropertyWrapper.swift in Sources */, 7A5D84BE2C1AE44700C2209B /* VehiclePhoto.swift in Sources */, 7A64A2262C1A32C800284124 /* AudioRecordDto.swift in Sources */, 7A761C09267E8EE40005F28F /* Base64FS.swift in Sources */, diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index a83a6d8..1e00cef 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -33,5 +33,21 @@ landmarkType = "7"> + + + + diff --git a/AutoCat/AppDelegate.swift b/AutoCat/AppDelegate.swift index ca5dcc7..95502f4 100644 --- a/AutoCat/AppDelegate.swift +++ b/AutoCat/AppDelegate.swift @@ -2,6 +2,8 @@ import UIKit import RealmSwift import PKHUD import AutoCatCore +import SwiftLocation +import CoreLocation enum QuickAction { case none @@ -70,10 +72,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate { HUD.dimsBackground = true HUD.allowsInteraction = false + Task { + try! await registerServices() + } + //Logging.URLRequests = { _ in false }; return true } + + func registerServices() async throws { + + let container = ServiceContainer.shared + + container.register(SettingsServiceProtocol.self, instance: SettingsService(defaults: .standard)) + container.register(StorageServiceProtocol.self, instance: try await StorageService()) + container.register(ApiServiceProtocol.self, instance: ApiService()) + container.register(GeocoderProtocol.self, instance: CLGeocoder()) + container.register(SwiftLocationProtocol.self, instance: Location()) + container.register(LocationServiceProtocol.self, instance: LocationService()) + } // MARK: UISceneSession Lifecycle diff --git a/AutoCat/Screens/AdsScreen/AdsScreen.swift b/AutoCat/Screens/AdsScreen/AdsScreen.swift index df5c08a..2e7f347 100644 --- a/AutoCat/Screens/AdsScreen/AdsScreen.swift +++ b/AutoCat/Screens/AdsScreen/AdsScreen.swift @@ -10,7 +10,7 @@ import SwiftUI struct AdsScreen: View { - @StateObject var viewModel: AdsViewModel + @State var viewModel: AdsViewModel @State var galleryModel: ACImageSliderModel? diff --git a/AutoCat/Screens/AdsScreen/AdsViewModel.swift b/AutoCat/Screens/AdsScreen/AdsViewModel.swift index c5c5638..59b7370 100644 --- a/AutoCat/Screens/AdsScreen/AdsViewModel.swift +++ b/AutoCat/Screens/AdsScreen/AdsViewModel.swift @@ -10,7 +10,8 @@ import Foundation import AutoCatCore @MainActor -class AdsViewModel: ObservableObject { +@Observable +class AdsViewModel { let ads: [VehicleAdDto] diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift b/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift index c8c6bbb..0db8607 100644 --- a/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift +++ b/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift @@ -13,7 +13,7 @@ struct LocationEditScreen: View { @Environment(\.dismiss) var dismiss - @StateObject var viewModel: LocationEditViewModel + @State var viewModel: LocationEditViewModel var body: some View { List { diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift b/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift index a2d54c9..29fc8dd 100644 --- a/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift +++ b/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift @@ -10,12 +10,13 @@ import AutoCatCore import SwiftUI @MainActor -final class LocationEditViewModel: ObservableObject { +@Observable +final class LocationEditViewModel { weak var coordinator: LocationEditCoordinator? - @Published var event: VehicleEventDto - @Published var date: Date + var event: VehicleEventDto + var date: Date var result: VehicleEventDto? diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift index 4342692..5f5c4fd 100644 --- a/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift @@ -23,7 +23,7 @@ final class LocationPickerCoordinator: Coordinator { func start() async throws -> VehicleEventDto? { - let viewModel = LocationPickerViewModel(event: event, locationService: LocationService.shared) + let viewModel = LocationPickerViewModel(event: event) let screen = LocationPickerScreen(viewModel: viewModel) let controller = CustomHostingController(rootView: screen) viewController?.pushViewController(controller, animated: true) diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift index afe82d8..e7f8781 100644 --- a/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift @@ -14,7 +14,7 @@ struct LocationPickerScreen: View { @Environment(\.dismiss) var dismiss - @StateObject var viewModel: LocationPickerViewModel + @State var viewModel: LocationPickerViewModel var body: some View { ZStack { @@ -52,8 +52,7 @@ struct LocationPickerScreen: View { event.address = "Ул. Ленина, 123" let locationService = LocationServiceStub(event: event) - let viewModel = LocationPickerViewModel(event: event, - locationService: locationService) + let viewModel = LocationPickerViewModel(event: event) return LocationPickerScreen(viewModel: viewModel) } diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift index 88c2d9a..3cb8f93 100644 --- a/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift @@ -12,18 +12,18 @@ import MapKit import SwiftUI @MainActor -final class LocationPickerViewModel: ObservableObject { +@Observable +final class LocationPickerViewModel { - let locationService: LocationServiceProtocol + @ObservationIgnored @Service var locationService: LocationServiceProtocol - @Published var event: VehicleEventDto - @Published var position: MapCameraPosition + var event: VehicleEventDto + var position: MapCameraPosition var result: VehicleEventDto? - init(event: VehicleEventDto, locationService: LocationServiceProtocol) { + init(event: VehicleEventDto) { self.event = event - self.locationService = locationService if event.latitude == 0 && event.longitude == 0 { self.position = .userLocation(fallback: .automatic) diff --git a/AutoCat/Screens/NotesScreen/NotesCoordinator.swift b/AutoCat/Screens/NotesScreen/NotesCoordinator.swift index 9d7aeec..f036fdf 100644 --- a/AutoCat/Screens/NotesScreen/NotesCoordinator.swift +++ b/AutoCat/Screens/NotesScreen/NotesCoordinator.swift @@ -10,6 +10,7 @@ import Foundation import SwiftUI import AutoCatCore +@MainActor class NotesCoordinator: Coordinator { let viewController: UINavigationController? @@ -23,10 +24,8 @@ class NotesCoordinator: Coordinator { func start() async throws { - let viewModel = await NotesViewModel(vehicle: vehicle, - storageService: try await StorageService.shared, - apiService: ApiService.shared) - let controller = await UIHostingController(rootView: NotesScreen(viewModel: viewModel)) - await viewController?.pushViewController(controller, animated: true) + let viewModel = NotesViewModel(vehicle: vehicle) + let controller = UIHostingController(rootView: NotesScreen(viewModel: viewModel)) + viewController?.pushViewController(controller, animated: true) } } diff --git a/AutoCat/Screens/NotesScreen/NotesScreen.swift b/AutoCat/Screens/NotesScreen/NotesScreen.swift index 1f4ed29..70033d3 100644 --- a/AutoCat/Screens/NotesScreen/NotesScreen.swift +++ b/AutoCat/Screens/NotesScreen/NotesScreen.swift @@ -11,7 +11,7 @@ import AutoCatCore struct NotesScreen: View { - @StateObject var viewModel: NotesViewModel + @State var viewModel: NotesViewModel @State var showNewNoteAlert = false @State var showEditNoteAlert = false @@ -96,9 +96,7 @@ struct NotesScreen: View { .init(text: "zxcv") ] - let vm = NotesViewModel(vehicle: vehicle, - storageService: StorageServiceStub(), - apiService: ApiServiceStub()) + let vm = NotesViewModel(vehicle: vehicle) return NotesScreen(viewModel: vm) } diff --git a/AutoCat/Screens/NotesScreen/NotesViewModel.swift b/AutoCat/Screens/NotesScreen/NotesViewModel.swift index 8aa60d6..f94fcdf 100644 --- a/AutoCat/Screens/NotesScreen/NotesViewModel.swift +++ b/AutoCat/Screens/NotesScreen/NotesViewModel.swift @@ -12,19 +12,18 @@ import UIKit import UniformTypeIdentifiers @MainActor -class NotesViewModel: ObservableObject, ACHudContainer { +@Observable +class NotesViewModel: ACHudContainer { - let storageService: StorageServiceProtocol - let apiService: ApiServiceProtocol + @ObservationIgnored @Service var storageService: StorageServiceProtocol + @ObservationIgnored @Service var apiService: ApiServiceProtocol - @Published var vehicle: VehicleDto - @Published var hud: ACHud? + var vehicle: VehicleDto + var hud: ACHud? - init(vehicle: VehicleDto, storageService: StorageServiceProtocol, apiService: ApiServiceProtocol) { + init(vehicle: VehicleDto) { self.vehicle = vehicle - self.storageService = storageService - self.apiService = apiService } func addNote(text: String) async { diff --git a/AutoCat/Screens/SettingsScreen/SettingsCoordinator.swift b/AutoCat/Screens/SettingsScreen/SettingsCoordinator.swift index 8dbe81a..acb2443 100644 --- a/AutoCat/Screens/SettingsScreen/SettingsCoordinator.swift +++ b/AutoCat/Screens/SettingsScreen/SettingsCoordinator.swift @@ -23,7 +23,7 @@ class SettingsCoordinator: Coordinator { func start() async throws { - let viewModel = SettingsViewModel(settingService: SettingsService.shared) + let viewModel = SettingsViewModel() viewModel.coordinator = self let controller = UIHostingController(rootView: SettingsScreen(viewModel: viewModel)) settingsController = controller diff --git a/AutoCat/Screens/SettingsScreen/SettingsScreen.swift b/AutoCat/Screens/SettingsScreen/SettingsScreen.swift index 31b49a7..9ba7354 100644 --- a/AutoCat/Screens/SettingsScreen/SettingsScreen.swift +++ b/AutoCat/Screens/SettingsScreen/SettingsScreen.swift @@ -83,5 +83,5 @@ struct SettingsScreen: View { } #Preview { - SettingsScreen(viewModel: .init(settingService: SettingsService(defaults: .standard))) + SettingsScreen(viewModel: .init()) } diff --git a/AutoCat/Screens/SettingsScreen/SettingsViewModel.swift b/AutoCat/Screens/SettingsScreen/SettingsViewModel.swift index ac3e799..aff4f19 100644 --- a/AutoCat/Screens/SettingsScreen/SettingsViewModel.swift +++ b/AutoCat/Screens/SettingsScreen/SettingsViewModel.swift @@ -42,8 +42,8 @@ class SettingsViewModel { return jwt.payload.email } - init(settingService: SettingsServiceProtocol) { - self.settingService = settingService + init() { + self.settingService = try! ServiceContainer.shared.resolve(SettingsServiceProtocol.self) } func signOut() { diff --git a/AutoCatCore/DependencyInjection/ServiceContainer.swift b/AutoCatCore/DependencyInjection/ServiceContainer.swift new file mode 100644 index 0000000..73eb7e3 --- /dev/null +++ b/AutoCatCore/DependencyInjection/ServiceContainer.swift @@ -0,0 +1,64 @@ +// +// ServiceContainer.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 21.09.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +public enum DIError: Error { + + case wrongServiceType(String) + case serviceNotFound(String) + + var localizedDescription: String { + switch self { + case .wrongServiceType(let type): "Wrong service type for service: \(type)" + case .serviceNotFound(let type): "Service \(type) not found" + } + } +} + +@MainActor +public class ServiceContainer { + + public static let shared: ServiceContainer = .init() + + private var cache: [String: Any] = [:] + private var factories: [String: () -> Any] = [:] + + public func register(_ service: Service.Type, + factory: @autoclosure @escaping () -> Service) { + + factories[String(describing: service.self)] = factory + } + + public func register(_ service: Service.Type, + instance: Service) { + + cache[String(describing: service.self)] = instance + } + + public func resolve(_ service: Service.Type) throws -> Service { + + let type = String(describing: service.self) + + if let cachedService = cache[type] { + if let service = cachedService as? Service { + return service + } else { + throw DIError.wrongServiceType(type) + } + } + + guard let factory = factories[type] else { + throw DIError.serviceNotFound(type) + } + + if let service = factory() as? Service { + return service + } else { + throw DIError.wrongServiceType(type) + } + } +} diff --git a/AutoCatCore/DependencyInjection/ServicePropertyWrapper.swift b/AutoCatCore/DependencyInjection/ServicePropertyWrapper.swift new file mode 100644 index 0000000..b7ffe26 --- /dev/null +++ b/AutoCatCore/DependencyInjection/ServicePropertyWrapper.swift @@ -0,0 +1,24 @@ +// +// ServicePropertyWrapper.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 21.09.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +@MainActor +@propertyWrapper +public struct Service { + + public var service: Service + + public init() { + + self.service = try! ServiceContainer.shared.resolve(Service.self) + } + + public var wrappedValue: Service { + get { service } + set { service = newValue } + } +} diff --git a/AutoCatCore/Services/ApiService/ApiService.swift b/AutoCatCore/Services/ApiService/ApiService.swift index dc7965c..6a1f2c7 100644 --- a/AutoCatCore/Services/ApiService/ApiService.swift +++ b/AutoCatCore/Services/ApiService/ApiService.swift @@ -2,12 +2,11 @@ import Foundation public actor ApiService: ApiServiceProtocol { - public static let shared = ApiService(settingsService: SettingsService.shared) + public static let shared = ApiService() - let settingsService: SettingsServiceProtocol + @Service var settingsService: SettingsServiceProtocol - init(settingsService: SettingsServiceProtocol) { - self.settingsService = settingsService + public init() { } private let session: URLSession = { @@ -19,9 +18,9 @@ public actor ApiService: ApiServiceProtocol { // MARK: - Private wrappres - private func createRequest(api: String, method: String, body: B? = nil, params: [String:P]? = nil) -> URLRequest? where B: Encodable, P: LosslessStringConvertible { + private func createRequest(api: String, method: String, body: B? = nil, params: [String:P]? = nil) async -> URLRequest? where B: Encodable, P: LosslessStringConvertible { - let baseUrl = settingsService.useTestBackend ? Constants.baseUrlDebug : Constants.baseUrl + let baseUrl = await settingsService.useTestBackend ? Constants.baseUrlDebug : Constants.baseUrl guard var urlComponents = URLComponents(string: baseUrl + api) else { return nil } @@ -51,7 +50,7 @@ public actor ApiService: ApiServiceProtocol { body: B?, params: [String:P]? = nil) async throws -> T where T: Decodable, B: Encodable, P: LosslessStringConvertible { - guard let request = self.createRequest(api: api, method: method, body: body, params: params) else { + guard let request = await self.createRequest(api: api, method: method, body: body, params: params) else { throw ApiError.generic } diff --git a/AutoCatCore/Services/LocationService/LocationService.swift b/AutoCatCore/Services/LocationService/LocationService.swift index d55770d..5f9cc45 100644 --- a/AutoCatCore/Services/LocationService/LocationService.swift +++ b/AutoCatCore/Services/LocationService/LocationService.swift @@ -12,17 +12,13 @@ import SwiftLocation @MainActor public final class LocationService { - public static let shared = LocationService(geocoder: CLGeocoder(), - locationManager: Location()) - - private let geocoder: GeocoderProtocol - private var locationManager: SwiftLocationProtocol + @Service var geocoder: GeocoderProtocol + @Service var locationManager: SwiftLocationProtocol private var eventTask: Task? - public init(geocoder: GeocoderProtocol, locationManager: SwiftLocationProtocol) { - self.geocoder = geocoder - self.locationManager = locationManager + public init() { + } private func checkPermissions() async throws { diff --git a/AutoCatCore/Services/SettingsService/SettingsService.swift b/AutoCatCore/Services/SettingsService/SettingsService.swift index f61da7c..840dd8c 100644 --- a/AutoCatCore/Services/SettingsService/SettingsService.swift +++ b/AutoCatCore/Services/SettingsService/SettingsService.swift @@ -10,8 +10,6 @@ import SwiftUI @Observable public final class SettingsService: SettingsServiceProtocol { - - public static let shared = SettingsService(defaults: .standard) let defaults: UserDefaults var observations: [NSKeyValueObservation] = [] diff --git a/AutoCatCore/Services/StorageService/StorageService.swift b/AutoCatCore/Services/StorageService/StorageService.swift index afd3676..b14430e 100644 --- a/AutoCatCore/Services/StorageService/StorageService.swift +++ b/AutoCatCore/Services/StorageService/StorageService.swift @@ -24,20 +24,6 @@ public enum StorageError: LocalizedError { public actor StorageService: StorageServiceProtocol { - private static var instance: StorageService? - - public static var shared: StorageService { - get async throws { - if let instance { - return instance - } else { - let service = try await StorageService() - instance = service - return service - } - } - } - var realm: Realm! public init(config: Realm.Configuration = .defaultConfiguration) async throws { diff --git a/AutoCatCoreTests/LocationServiceTests.swift b/AutoCatCoreTests/LocationServiceTests.swift index d0e9418..bc766a3 100644 --- a/AutoCatCoreTests/LocationServiceTests.swift +++ b/AutoCatCoreTests/LocationServiceTests.swift @@ -22,8 +22,11 @@ struct LocationServiceTests { let locationService: LocationService init() { - self.locationService = LocationService(geocoder: geocoder, - locationManager: locationManager) + + ServiceContainer.shared.register(GeocoderProtocol.self, instance: geocoder) + ServiceContainer.shared.register(SwiftLocationProtocol.self, instance: locationManager) + + self.locationService = LocationService() } @Test diff --git a/AutoCatCoreTests/SettingsServiceTests.swift b/AutoCatCoreTests/SettingsServiceTests.swift index d7eaf31..ee51f37 100644 --- a/AutoCatCoreTests/SettingsServiceTests.swift +++ b/AutoCatCoreTests/SettingsServiceTests.swift @@ -18,13 +18,14 @@ extension Encodable { } } +@MainActor struct SettingsServiceTests { let testRegion = "xxx" let testEmail = "test@test.test" let testToken = "testToken" let testUser: User - let dbName = "testUserDefaults" + let dbName = UUID().uuidString let defaults: UserDefaults var settingsService: SettingsService @@ -35,9 +36,9 @@ struct SettingsServiceTests { self.defaults.removePersistentDomain(forName: dbName) self.settingsService = SettingsService(defaults: defaults) } - + @Test("Creating default user") - mutating func defaultUser() async throws { + func defaultUser() async throws { #expect(settingsService.user.email == "") #expect(settingsService.user.token == "") @@ -60,8 +61,11 @@ struct SettingsServiceTests { settingsService.user = testUser - let userData = defaults.data(forKey: "user") - #expect(userData == testUser.data) + let userData = try #require(defaults.data(forKey: "user")) + let user = try JSONDecoder().decode(User.self, from: userData) + + #expect(user.email == testUser.email) + #expect(user.token == testUser.token) } @Test("Save settings", .serialized, arguments: [true, false]) diff --git a/AutoCatTests/LocationPickerTests.swift b/AutoCatTests/LocationPickerTests.swift index daa5147..562eb8a 100644 --- a/AutoCatTests/LocationPickerTests.swift +++ b/AutoCatTests/LocationPickerTests.swift @@ -20,19 +20,18 @@ struct LocationPickerTests { let address = "Test Address" let geocoder = GeocoderMock() - let locationManager = SwiftLocationMock() - let locationService: LocationService init() { - self.locationService = LocationService(geocoder: geocoder, - locationManager: locationManager) + + ServiceContainer.shared.register(GeocoderProtocol.self, instance: geocoder) + ServiceContainer.shared.register(SwiftLocationProtocol.self, instance: SwiftLocationMock()) + ServiceContainer.shared.register(LocationServiceProtocol.self, instance: LocationService()) } @Test("Set initial location (user)") func setInitialLocationUser() async throws { - let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0), - locationService: locationService) + let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0)) #expect(viewModel.position == .userLocation(fallback: .automatic)) } @@ -40,8 +39,7 @@ struct LocationPickerTests { @Test("Set initial location (custom)") func setInitialLocationCustom() async throws { - let viewModel = LocationPickerViewModel(event: .init(lat: latitude, lon: longitude), - locationService: locationService) + let viewModel = LocationPickerViewModel(event: .init(lat: latitude, lon: longitude)) #expect(viewModel.position.region?.center.latitude == latitude) #expect(viewModel.position.region?.center.longitude == longitude) @@ -50,8 +48,7 @@ struct LocationPickerTests { @Test("Update event") func updateEvent() async throws { - let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0), - locationService: locationService) + let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0)) geocoder.addLocation(latitude: latitude, longitude: longitude, diff --git a/AutoCatTests/NotesTests.swift b/AutoCatTests/NotesTests.swift index 80ac1a5..f58c55c 100644 --- a/AutoCatTests/NotesTests.swift +++ b/AutoCatTests/NotesTests.swift @@ -14,12 +14,10 @@ import MockableTest @MainActor final class NotesTests { - var storageServiceMock = StorageServiceMock() - var apiServiceMock = ApiServiceMock() + var storageServiceMock: StorageServiceMock + var apiServiceMock: ApiServiceMock - lazy var viewModel = NotesViewModel(vehicle: VehicleDto(), - storageService: storageServiceMock, - apiService: apiServiceMock) + var viewModel: NotesViewModel let noteText = "Test note text" let noteTextModified = "Test note text modified" @@ -27,6 +25,15 @@ final class NotesTests { lazy var vehicleWithNote: VehicleDto = .normal.addNote(text: noteText) lazy var unrecognizedVehicleWithNote: VehicleDto = .unrecognized.addNote(text: noteText) + init() { + storageServiceMock = StorageServiceMock() + apiServiceMock = ApiServiceMock() + + ServiceContainer.shared.register(StorageServiceProtocol.self, instance: storageServiceMock) + ServiceContainer.shared.register(ApiServiceProtocol.self, instance: apiServiceMock) + viewModel = NotesViewModel(vehicle: VehicleDto()) + } + @Test("Add note (normal vehicle)") func addNote() async throws {