Adding dependency injection with property wrappers
This commit is contained in:
parent
17d00fbf65
commit
2a8f1d921a
@ -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 = "<group>"; };
|
||||
7A6DD90D24337930009DE740 /* PlateNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = "<group>"; };
|
||||
7A6F095F26DBF588003A965D /* VehicleNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNote.swift; sourceTree = "<group>"; };
|
||||
7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceContainer.swift; sourceTree = "<group>"; };
|
||||
7A7097C52C9EC77A007CFDCA /* ServicePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicePropertyWrapper.swift; sourceTree = "<group>"; };
|
||||
7A7157FF2C43EA6900852088 /* OwnersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersScreen.swift; sourceTree = "<group>"; };
|
||||
7A7158032C43EAA200852088 /* OwnersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersCoordinator.swift; sourceTree = "<group>"; };
|
||||
7A7158062C44085600852088 /* OsagoScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OsagoScreen.swift; sourceTree = "<group>"; };
|
||||
@ -836,6 +840,15 @@
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A7097C02C9EC113007CFDCA /* DependencyInjection */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */,
|
||||
7A7097C52C9EC77A007CFDCA /* ServicePropertyWrapper.swift */,
|
||||
);
|
||||
path = DependencyInjection;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 */,
|
||||
|
||||
@ -33,5 +33,21 @@
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "4C063CE0-2872-4A19-8434-06F8B142D421"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "../../../../../var/folders/g9/lb6r30c97kq6yx2kcl8h66500000gn/T/swift-generated-sources/@__swiftmacro_7AutoCat17SettingsViewModelC14settingService18ObservationTrackedfMa_.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "1"
|
||||
endingLineNumber = "1"
|
||||
landmarkName = "unknown"
|
||||
landmarkType = "0">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
||||
@ -2,6 +2,8 @@ import UIKit
|
||||
import RealmSwift
|
||||
import PKHUD
|
||||
import AutoCatCore
|
||||
import SwiftLocation
|
||||
import CoreLocation
|
||||
|
||||
enum QuickAction {
|
||||
case none
|
||||
@ -70,11 +72,27 @@ 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
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
|
||||
@ -10,7 +10,7 @@ import SwiftUI
|
||||
|
||||
struct AdsScreen: View {
|
||||
|
||||
@StateObject var viewModel: AdsViewModel
|
||||
@State var viewModel: AdsViewModel
|
||||
|
||||
@State var galleryModel: ACImageSliderModel?
|
||||
|
||||
|
||||
@ -10,7 +10,8 @@ import Foundation
|
||||
import AutoCatCore
|
||||
|
||||
@MainActor
|
||||
class AdsViewModel: ObservableObject {
|
||||
@Observable
|
||||
class AdsViewModel {
|
||||
|
||||
let ads: [VehicleAdDto]
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ struct LocationEditScreen: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
@StateObject var viewModel: LocationEditViewModel
|
||||
@State var viewModel: LocationEditViewModel
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
|
||||
@ -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?
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -83,5 +83,5 @@ struct SettingsScreen: View {
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsScreen(viewModel: .init(settingService: SettingsService(defaults: .standard)))
|
||||
SettingsScreen(viewModel: .init())
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
64
AutoCatCore/DependencyInjection/ServiceContainer.swift
Normal file
64
AutoCatCore/DependencyInjection/ServiceContainer.swift
Normal file
@ -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: Service.Type,
|
||||
factory: @autoclosure @escaping () -> Service) {
|
||||
|
||||
factories[String(describing: service.self)] = factory
|
||||
}
|
||||
|
||||
public func register<Service>(_ service: Service.Type,
|
||||
instance: Service) {
|
||||
|
||||
cache[String(describing: service.self)] = instance
|
||||
}
|
||||
|
||||
public func resolve<Service>(_ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
24
AutoCatCore/DependencyInjection/ServicePropertyWrapper.swift
Normal file
24
AutoCatCore/DependencyInjection/ServicePropertyWrapper.swift
Normal file
@ -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<Service> {
|
||||
|
||||
public var service: Service
|
||||
|
||||
public init() {
|
||||
|
||||
self.service = try! ServiceContainer.shared.resolve(Service.self)
|
||||
}
|
||||
|
||||
public var wrappedValue: Service {
|
||||
get { service }
|
||||
set { service = newValue }
|
||||
}
|
||||
}
|
||||
@ -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<B,P>(api: String, method: String, body: B? = nil, params: [String:P]? = nil) -> URLRequest? where B: Encodable, P: LosslessStringConvertible {
|
||||
private func createRequest<B,P>(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
|
||||
}
|
||||
|
||||
|
||||
@ -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<VehicleEventDto,Error>?
|
||||
|
||||
public init(geocoder: GeocoderProtocol, locationManager: SwiftLocationProtocol) {
|
||||
self.geocoder = geocoder
|
||||
self.locationManager = locationManager
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
private func checkPermissions() async throws {
|
||||
|
||||
@ -11,8 +11,6 @@ import SwiftUI
|
||||
@Observable
|
||||
public final class SettingsService: SettingsServiceProtocol {
|
||||
|
||||
public static let shared = SettingsService(defaults: .standard)
|
||||
|
||||
let defaults: UserDefaults
|
||||
var observations: [NSKeyValueObservation] = []
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -37,7 +38,7 @@ struct SettingsServiceTests {
|
||||
}
|
||||
|
||||
@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])
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user