diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 8c8eeb4..60c2bb7 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -1891,7 +1891,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1916,7 +1916,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -1943,7 +1943,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AutoCat.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AutoCat"; }; @@ -1970,7 +1970,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AutoCat.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AutoCat"; }; @@ -2001,7 +2001,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MOCKING"; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2032,7 +2032,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/AutoCatCore/Services/ApiService/ApiService.swift b/AutoCatCore/Services/ApiService/ApiService.swift index 0dec687..0dca813 100644 --- a/AutoCatCore/Services/ApiService/ApiService.swift +++ b/AutoCatCore/Services/ApiService/ApiService.swift @@ -2,7 +2,7 @@ import Foundation public actor ApiService: ApiServiceProtocol { - var settingsService: SettingsServiceProtocol + let settingsService: SettingsServiceProtocol public init(settingsService: SettingsServiceProtocol) { diff --git a/AutoCatCore/Services/LocationService/GeocoderProtocol.swift b/AutoCatCore/Services/LocationService/GeocoderProtocol.swift index c3ac212..72ccf82 100644 --- a/AutoCatCore/Services/LocationService/GeocoderProtocol.swift +++ b/AutoCatCore/Services/LocationService/GeocoderProtocol.swift @@ -9,6 +9,7 @@ import CoreLocation import Mockable +@MainActor @Mockable public protocol GeocoderProtocol { diff --git a/AutoCatCore/Services/LocationService/LocationService.swift b/AutoCatCore/Services/LocationService/LocationService.swift index ce4a9ee..c5e7dfb 100644 --- a/AutoCatCore/Services/LocationService/LocationService.swift +++ b/AutoCatCore/Services/LocationService/LocationService.swift @@ -55,7 +55,7 @@ public final class LocationService { return VehicleEventDto( lat: coordinate.latitude, lon: coordinate.longitude, - addedBy: await settingsService.user.email + addedBy: settingsService.user.email ) } diff --git a/AutoCatCore/Services/LocationService/SwiftLocationProtocol.swift b/AutoCatCore/Services/LocationService/SwiftLocationProtocol.swift index d0b081c..4064b1a 100644 --- a/AutoCatCore/Services/LocationService/SwiftLocationProtocol.swift +++ b/AutoCatCore/Services/LocationService/SwiftLocationProtocol.swift @@ -10,6 +10,7 @@ import SwiftLocation import CoreLocation import Mockable +@MainActor @Mockable public protocol SwiftLocationProtocol { @@ -21,3 +22,8 @@ public protocol SwiftLocationProtocol { } extension Location: SwiftLocationProtocol { } + +// Force sendable conformance for SwiftLocation types +extension Tasks.ContinuousUpdateLocation.StreamEvent: @retroactive @unchecked Sendable { } +extension LocationPermission: @retroactive @unchecked Sendable { } +extension AccuracyFilter: @retroactive @unchecked Sendable { } diff --git a/AutoCatCore/Services/StorageService/DbUpdatePolicy.swift b/AutoCatCore/Services/StorageService/DbUpdatePolicy.swift index 2e3c9de..9bc9da4 100644 --- a/AutoCatCore/Services/StorageService/DbUpdatePolicy.swift +++ b/AutoCatCore/Services/StorageService/DbUpdatePolicy.swift @@ -6,7 +6,7 @@ // Copyright © 2025 Selim Mustafaev. All rights reserved. // -public enum DbUpdatePolicy: CaseIterable { +public enum DbUpdatePolicy: CaseIterable, Sendable { case always case ifExists diff --git a/AutoCatCore/Services/StorageService/StorageService+Events.swift b/AutoCatCore/Services/StorageService/StorageService+Events.swift index 6e01ee6..4c5b8b9 100644 --- a/AutoCatCore/Services/StorageService/StorageService+Events.swift +++ b/AutoCatCore/Services/StorageService/StorageService+Events.swift @@ -44,19 +44,19 @@ public extension StorageService { func edit(event: VehicleEventDto, for number: String) async throws -> VehicleDto { - guard let vehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: number) else { - throw StorageError.vehicleNotFound - } - try await realm.asyncWrite { + guard let vehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: number) else { + throw StorageError.vehicleNotFound + } + if vehicle.events.contains(where: { $0.id == event.id }) { realm.add(VehicleEvent(dto: event), update: .modified) vehicle.updatedDate = Date().timeIntervalSince1970 } else { throw StorageError.eventNotFound } + + return vehicle.dto } - - return vehicle.dto } } diff --git a/AutoCatCore/Services/StorageService/StorageService+Notes.swift b/AutoCatCore/Services/StorageService/StorageService+Notes.swift index f956be1..49aac0f 100644 --- a/AutoCatCore/Services/StorageService/StorageService+Notes.swift +++ b/AutoCatCore/Services/StorageService/StorageService+Notes.swift @@ -28,20 +28,20 @@ extension StorageService { public func deleteNote(id: String, for number: String) async throws -> VehicleDto { - guard let vehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: number) else { - throw StorageError.vehicleNotFound - } - - guard let realmNote = realm.object(ofType: VehicleNote.self, forPrimaryKey: id) else { - throw StorageError.noteNotFound - } - try await realm.asyncWrite { + guard let vehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: number) else { + throw StorageError.vehicleNotFound + } + + guard let realmNote = realm.object(ofType: VehicleNote.self, forPrimaryKey: id) else { + throw StorageError.noteNotFound + } + realm.delete(realmNote) vehicle.updatedDate = Date().timeIntervalSince1970 + + return vehicle.dto } - - return vehicle.dto } public func editNote(id: String, text: String, for number: String) async throws -> VehicleDto { diff --git a/AutoCatCore/Services/StorageService/StorageService.swift b/AutoCatCore/Services/StorageService/StorageService.swift index efe3667..677a61a 100644 --- a/AutoCatCore/Services/StorageService/StorageService.swift +++ b/AutoCatCore/Services/StorageService/StorageService.swift @@ -17,8 +17,15 @@ public actor StorageService: StorageServiceProtocol { public init(settingsService: SettingsServiceProtocol, isTest: Bool = false) async throws { - var realmConfig: Realm.Configuration + self.settingsService = settingsService + realm = try await setup(isTest: isTest) + } + + // Swift 6.0 and 6.1 crashes if this code is placed directly into init + private func setup(isTest: Bool) async throws -> Realm { + var realmConfig: Realm.Configuration + if isTest { realmConfig = .defaultConfiguration realmConfig.inMemoryIdentifier = UUID().uuidString @@ -28,9 +35,8 @@ public actor StorageService: StorageServiceProtocol { migrationBlock: { migration, oldSchemaVersion in } ) } - - self.settingsService = settingsService - realm = try await Realm(configuration: realmConfig, actor: self) + + return try await Realm.open(configuration: realmConfig) } public var dbFileURL: URL? { diff --git a/AutoCatCore/Utils/Constants.swift b/AutoCatCore/Utils/Constants.swift index 9dd283e..5f54947 100644 --- a/AutoCatCore/Utils/Constants.swift +++ b/AutoCatCore/Utils/Constants.swift @@ -2,7 +2,7 @@ import Foundation public enum Constants { - public enum Backend: String, CaseIterable { + public enum Backend: String, CaseIterable, Sendable { case de = "https://vps.aliencat.pro:8443/" case ru = "https://charon.aliencat.pro:8443/" diff --git a/AutoCatCoreTests/VehicleRecordServiceTests.swift b/AutoCatCoreTests/VehicleRecordServiceTests.swift index 7bba7b6..d560f6e 100644 --- a/AutoCatCoreTests/VehicleRecordServiceTests.swift +++ b/AutoCatCoreTests/VehicleRecordServiceTests.swift @@ -36,6 +36,18 @@ struct VehicleRecordServiceTests { locationService: locationServiceMock, settingsService: settingsServiceMock ) + + given(settingsServiceMock) + .recognizeAlternativeOrder + .willReturn(false) + + given(settingsServiceMock) + .recognizeShortenedNumbers + .willReturn(false) + + given(settingsServiceMock) + .defaultRegion + .willReturn("") } @Test("Requesting permissions") diff --git a/AutoCatTests/EventsTests.swift b/AutoCatTests/EventsTests.swift index 55a5eba..1ddfa8f 100644 --- a/AutoCatTests/EventsTests.swift +++ b/AutoCatTests/EventsTests.swift @@ -29,13 +29,13 @@ struct EventsTests { storageServiceMock = MockStorageServiceProtocol() apiServiceMock = MockApiServiceProtocol() + given(settingsServiceMock) + .user.willReturn(User()) + viewModel = EventsViewModel(apiService: apiServiceMock, storageService: storageServiceMock, settingsService: settingsServiceMock, vehicle: VehicleDto()) - - given(settingsServiceMock) - .user.willReturn(User()) } func makeViewModel(vehicle: VehicleDto) -> EventsViewModel {