From f2b55780c8794e9a97a11eaf56f0c298c9ee9d13 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Tue, 19 Nov 2024 18:00:10 +0200 Subject: [PATCH 01/12] Fix concurrency issues --- wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj | 3 +++ wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift | 2 +- .../Toolkit/FirebaseManager/FirebaseManager.swift | 2 +- .../Toolkit/FirebaseManager/RemoteConfigManager.swift | 10 +++------- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj b/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj index e5a2ed090..867db8e11 100644 --- a/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj +++ b/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj @@ -494,6 +494,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -528,6 +529,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -626,6 +628,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = MOCK; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift b/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift index 1ecd6c786..10fbd004f 100644 --- a/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift +++ b/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift @@ -7,7 +7,7 @@ import Foundation -public class WXMAnalytics { +public actor WXMAnalytics { public static let shared = WXMAnalytics() private var providers: [AnalyticsProviderImplementation] = [] diff --git a/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift b/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift index 114df966f..2c40bb864 100644 --- a/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift +++ b/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift @@ -13,7 +13,7 @@ import FirebaseMessaging import Combine public class FirebaseManager { - public static let shared: FirebaseManager = .init() + nonisolated(unsafe) public static let shared: FirebaseManager = .init() public var latestReceivedNotificationPublisher: AnyPublisher? { firebaseManagerImpl.latestReceivedNotificationPublisher } diff --git a/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift b/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift index 38cdc17d4..480f09d75 100644 --- a/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift +++ b/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift @@ -8,9 +8,10 @@ import Foundation import FirebaseRemoteConfig +@MainActor public class RemoteConfigManager: ObservableObject { - static public let shared: RemoteConfigManager = .init() + public let shared: RemoteConfigManager = .init() private let remoteConfigManagerImpl: RemoteConfigManagerImplementation // MARK: - Remote config entries @@ -154,12 +155,7 @@ private class DefaultRemoteConfigManager: RemoteConfigManagerImplementation { } func updateProperties() { - DispatchQueue.main.async { [weak self] in - guard let self else { - return - } - self.shouldUpdateCallback?() - } + shouldUpdateCallback?() } } From 0fbb61523a90af9475a352a42ba5cfe6969f8b31 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 20 Nov 2024 14:58:18 +0200 Subject: [PATCH 02/12] WIP --- wxm-ios/DataLayer/UserDevicesService.swift | 2 ++ .../DomainLayer/DomainLayer/UseCases/WidgetUseCase.swift | 3 ++- wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift | 2 +- .../Toolkit/FirebaseManager/NotificationsHandler.swift | 9 ++++----- .../Toolkit/FirebaseManager/RemoteConfigManager.swift | 5 ++--- wxm-ios/Toolkit/Toolkit/Logger/Logger.swift | 2 +- wxm-ios/Toolkit/Toolkit/Utils/Cancellables.swift | 7 +++---- wxm-ios/Toolkit/Toolkit/Utils/Combine+.swift | 5 +++-- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/wxm-ios/DataLayer/UserDevicesService.swift b/wxm-ios/DataLayer/UserDevicesService.swift index c451fd1a2..43a8b60de 100644 --- a/wxm-ios/DataLayer/UserDevicesService.swift +++ b/wxm-ios/DataLayer/UserDevicesService.swift @@ -158,6 +158,7 @@ public class UserDevicesService { .eraseToAnyPublisher() } + @MainActor func getFollowState(deviceId: String) async throws -> Result { // If the id is in cache the device id is for a user device if let cachedStates = followStatesCache.getValue(for: followStatesCacheKey) { @@ -189,6 +190,7 @@ public class UserDevicesService { } } + @MainActor func getFollowStates() async throws -> Result<[UserDeviceFollowState]?, NetworkErrorResponse> { if let cachedStates = followStatesCache.getValue(for: followStatesCacheKey) { return .success(cachedStates) diff --git a/wxm-ios/DomainLayer/DomainLayer/UseCases/WidgetUseCase.swift b/wxm-ios/DomainLayer/DomainLayer/UseCases/WidgetUseCase.swift index c8f5a2381..5c3f45229 100644 --- a/wxm-ios/DomainLayer/DomainLayer/UseCases/WidgetUseCase.swift +++ b/wxm-ios/DomainLayer/DomainLayer/UseCases/WidgetUseCase.swift @@ -29,10 +29,11 @@ public struct WidgetUseCase { meRepository.getCachedDevices()?.map { $0.toDeviceDetails } } + @MainActor public func getDevices(useCache: Bool = true) async throws -> Result<[DeviceDetails], NetworkErrorResponse> { let userDevices = try meRepository.getDevices(useCache: useCache) let publisher = userDevices.convertedToDeviceDetailsResultPublisher - return await withUnsafeContinuation { continuation in + return await withUnsafeContinuation { continuation in publisher.sink { result in continuation.resume(returning: result) } diff --git a/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift b/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift index 10fbd004f..93a1c15b6 100644 --- a/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift +++ b/wxm-ios/Toolkit/Toolkit/Analytics/WXMAnalytics.swift @@ -7,7 +7,7 @@ import Foundation -public actor WXMAnalytics { +public class WXMAnalytics: @unchecked Sendable { public static let shared = WXMAnalytics() private var providers: [AnalyticsProviderImplementation] = [] diff --git a/wxm-ios/Toolkit/Toolkit/FirebaseManager/NotificationsHandler.swift b/wxm-ios/Toolkit/Toolkit/FirebaseManager/NotificationsHandler.swift index e4be1790f..239478529 100644 --- a/wxm-ios/Toolkit/Toolkit/FirebaseManager/NotificationsHandler.swift +++ b/wxm-ios/Toolkit/Toolkit/FirebaseManager/NotificationsHandler.swift @@ -8,9 +8,9 @@ import Foundation import FirebaseMessaging import UIKit -import Combine +@preconcurrency import Combine -class NotificationsHandler: NSObject { +final class NotificationsHandler: NSObject, Sendable { let latestNotificationPublisher: AnyPublisher let authorizationStatusPublisher: AnyPublisher let fcmTokenPublisher: AnyPublisher @@ -19,7 +19,7 @@ class NotificationsHandler: NSObject { private let authorizationStatusSubject: CurrentValueSubject = .init(nil) private let fcmTokenSubject: PassthroughSubject = .init() - private var cancellableSet: Set = .init() + nonisolated(unsafe) private var cancellableSet: Set = .init() override init() { latestNotificationPublisher = latestNotificationSubject.eraseToAnyPublisher() @@ -66,7 +66,7 @@ private extension NotificationsHandler { } func refreshAuthorizationStatus() { - Task { + Task { @Sendable in let status = await getAuthorizationStatus() authorizationStatusSubject.send(status) } @@ -80,7 +80,6 @@ extension NotificationsHandler: UNUserNotificationCenterDelegate { completionHandler([.banner, .badge, .sound, .list]) } - @MainActor func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { latestNotificationSubject.send(response) } diff --git a/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift b/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift index 480f09d75..1dcd3dee6 100644 --- a/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift +++ b/wxm-ios/Toolkit/Toolkit/FirebaseManager/RemoteConfigManager.swift @@ -8,10 +8,9 @@ import Foundation import FirebaseRemoteConfig -@MainActor -public class RemoteConfigManager: ObservableObject { +public class RemoteConfigManager: ObservableObject, @unchecked Sendable { - public let shared: RemoteConfigManager = .init() + public static let shared: RemoteConfigManager = .init() private let remoteConfigManagerImpl: RemoteConfigManagerImplementation // MARK: - Remote config entries diff --git a/wxm-ios/Toolkit/Toolkit/Logger/Logger.swift b/wxm-ios/Toolkit/Toolkit/Logger/Logger.swift index 33ebf3364..fc3910c76 100644 --- a/wxm-ios/Toolkit/Toolkit/Logger/Logger.swift +++ b/wxm-ios/Toolkit/Toolkit/Logger/Logger.swift @@ -8,7 +8,7 @@ import Foundation import FirebaseCrashlytics -public class Logger { +public class Logger: @unchecked Sendable { public static let shared = Logger() private init() {} diff --git a/wxm-ios/Toolkit/Toolkit/Utils/Cancellables.swift b/wxm-ios/Toolkit/Toolkit/Utils/Cancellables.swift index 8649b8ff1..cd6dc0246 100644 --- a/wxm-ios/Toolkit/Toolkit/Utils/Cancellables.swift +++ b/wxm-ios/Toolkit/Toolkit/Utils/Cancellables.swift @@ -15,10 +15,9 @@ public class CancellableWrapper { public init() {} } + public extension AnyCancellable { - func storeThreadSafe(in set: inout Set) { - DispatchQueue.main.sync { - self.store(in: &set) - } + @MainActor func storeThreadSafe(in set: inout Set) { + store(in: &set) } } diff --git a/wxm-ios/Toolkit/Toolkit/Utils/Combine+.swift b/wxm-ios/Toolkit/Toolkit/Utils/Combine+.swift index 2f33cd393..b07a9f77c 100644 --- a/wxm-ios/Toolkit/Toolkit/Utils/Combine+.swift +++ b/wxm-ios/Toolkit/Toolkit/Utils/Combine+.swift @@ -6,7 +6,7 @@ // import Foundation -import Combine +@preconcurrency import Combine public extension AnyPublisher { func toAsync() async throws -> Output { @@ -22,7 +22,8 @@ public extension AnyPublisher { } cancellable?.cancel() } receiveValue: { value in - continuation.resume(with: .success(value)) + nonisolated(unsafe) let receivedValue = value + continuation.resume(returning: receivedValue) } } } From e84b37fad0e21420c483a608b0e0c5c840f2f3b5 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 20 Nov 2024 16:55:43 +0200 Subject: [PATCH 03/12] Eliminate warnings --- .../xcshareddata/xcschemes/station-intent.xcscheme | 2 +- .../xcshareddata/xcschemes/station-widgetExtension.xcscheme | 2 +- wxm-ios.xcodeproj/xcshareddata/xcschemes/wxm-ios-db.xcscheme | 2 +- .../xcshareddata/xcschemes/wxm-ios-mock.xcscheme | 2 +- .../xcshareddata/xcschemes/wxm-ios-release.xcscheme | 2 +- wxm-ios.xcodeproj/xcshareddata/xcschemes/wxm-ios.xcscheme | 2 +- .../xcshareddata/xcschemes/DataLayer.xcscheme | 2 +- wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj | 5 ++++- .../xcshareddata/xcschemes/Toolkit.xcscheme | 2 +- wxm-ios/Toolkit/Toolkit/Utils/Double+.swift | 2 +- wxm-ios/Toolkit/Toolkit/Utils/WEIConverter.swift | 3 +-- wxm-ios/Toolkit/Toolkit/Utils/WXMLocationManager.swift | 4 ++-- 12 files changed, 16 insertions(+), 14 deletions(-) diff --git a/wxm-ios.xcodeproj/xcshareddata/xcschemes/station-intent.xcscheme b/wxm-ios.xcodeproj/xcshareddata/xcschemes/station-intent.xcscheme index a05da9c28..cda92f0e7 100644 --- a/wxm-ios.xcodeproj/xcshareddata/xcschemes/station-intent.xcscheme +++ b/wxm-ios.xcodeproj/xcshareddata/xcschemes/station-intent.xcscheme @@ -1,6 +1,6 @@ String { - var formatter = NumberFormatter() + let formatter = NumberFormatter() formatter.maximumFractionDigits = precision formatter.minimumFractionDigits = minDecimals formatter.numberStyle = .decimal diff --git a/wxm-ios/Toolkit/Toolkit/Utils/WEIConverter.swift b/wxm-ios/Toolkit/Toolkit/Utils/WEIConverter.swift index d1017f382..693c8d2b6 100644 --- a/wxm-ios/Toolkit/Toolkit/Utils/WEIConverter.swift +++ b/wxm-ios/Toolkit/Toolkit/Utils/WEIConverter.swift @@ -29,8 +29,7 @@ extension WEIConverter { guard var decimal = Decimal(string: weiString) else { return nil } - - var etherInWei = self.etherInWei + var result: Decimal = .nan NSDecimalMultiplyByPowerOf10(&result, &decimal, -18, .up) return result diff --git a/wxm-ios/Toolkit/Toolkit/Utils/WXMLocationManager.swift b/wxm-ios/Toolkit/Toolkit/Utils/WXMLocationManager.swift index 4560ba3d6..5e5215cae 100644 --- a/wxm-ios/Toolkit/Toolkit/Utils/WXMLocationManager.swift +++ b/wxm-ios/Toolkit/Toolkit/Utils/WXMLocationManager.swift @@ -38,7 +38,7 @@ public class WXMLocationManager: NSObject { return await withCheckedContinuation { continuation in statusSubject?.sink { status in - continuation.resume(returning: status) + continuation.resume(returning: status) }.store(in: &cancellableSet) } } @@ -87,7 +87,7 @@ extension WXMLocationManager: CLLocationManagerDelegate { } extension WXMLocationManager { - public enum Status { + public enum Status: Sendable { case authorized case denied case notDetermined From f6ca18cbcb12f9c6388ee3b51b2400c27874d4eb Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Thu, 21 Nov 2024 10:29:28 +0200 Subject: [PATCH 04/12] Updated swift version to 6 --- wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj b/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj index a98c0ff43..9fed0e4f0 100644 --- a/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj +++ b/wxm-ios/Toolkit/Toolkit.xcodeproj/project.pbxproj @@ -497,7 +497,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -532,7 +532,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -632,7 +632,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = MOCK; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Mock; From b7acd1c64c15056df62eecbfdc5e0d27771c7d8b Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Mon, 25 Nov 2024 15:17:00 +0200 Subject: [PATCH 05/12] Eliminate warnings (WIP) --- PresentationLayer/Constants/WeatherFields.swift | 2 +- .../DomainExtensions/Common/CurrentWeather+.swift | 2 +- .../Common/UserDeviceFollowState+.swift | 2 +- .../UIComponents/Modifiers/SizeObserver.swift | 2 +- .../Utils/Weather/WeatherUnitsManager.swift | 4 ++-- station-widget/StationTimelineEntry.swift | 2 +- station-widget/Station_widget.swift | 15 +++++++++------ wxm-ios.xcodeproj/project.pbxproj | 3 +++ wxm-ios/Swinject/SwinjectHelper.swift | 6 +++--- 9 files changed, 22 insertions(+), 16 deletions(-) diff --git a/PresentationLayer/Constants/WeatherFields.swift b/PresentationLayer/Constants/WeatherFields.swift index b8ec14676..a3a0b340f 100644 --- a/PresentationLayer/Constants/WeatherFields.swift +++ b/PresentationLayer/Constants/WeatherFields.swift @@ -10,7 +10,7 @@ import SwiftUI import DomainLayer import Toolkit -extension WeatherField: CustomStringConvertible { +extension WeatherField: @retroactive CustomStringConvertible { static var stationListFields: [WeatherField] { [.temperature, .wind, .humidity, .precipitation] diff --git a/PresentationLayer/Extensions/DomainExtensions/Common/CurrentWeather+.swift b/PresentationLayer/Extensions/DomainExtensions/Common/CurrentWeather+.swift index d7b1c5496..251c9935b 100644 --- a/PresentationLayer/Extensions/DomainExtensions/Common/CurrentWeather+.swift +++ b/PresentationLayer/Extensions/DomainExtensions/Common/CurrentWeather+.swift @@ -9,7 +9,7 @@ import Foundation import DomainLayer import Toolkit -extension CurrentWeather: Identifiable { +extension CurrentWeather: @retroactive Identifiable { public var id: String { timestamp ?? "" } diff --git a/PresentationLayer/Extensions/DomainExtensions/Common/UserDeviceFollowState+.swift b/PresentationLayer/Extensions/DomainExtensions/Common/UserDeviceFollowState+.swift index 565f62e8e..ba38f6927 100644 --- a/PresentationLayer/Extensions/DomainExtensions/Common/UserDeviceFollowState+.swift +++ b/PresentationLayer/Extensions/DomainExtensions/Common/UserDeviceFollowState+.swift @@ -10,7 +10,7 @@ import DomainLayer typealias StateFontAwesome = (icon: FontIcon, color: ColorEnum, font: FontAwesome) extension UserDeviceFollowState { - static let defaultFAIcon: StateFontAwesome = (FontIcon.heart, ColorEnum.favoriteHeart, FontAwesome.FAPro) + nonisolated(unsafe) static let defaultFAIcon: StateFontAwesome = (FontIcon.heart, ColorEnum.favoriteHeart, FontAwesome.FAPro) enum State { case owned diff --git a/PresentationLayer/UIComponents/Modifiers/SizeObserver.swift b/PresentationLayer/UIComponents/Modifiers/SizeObserver.swift index 04ee6f3a5..86ba1bd6c 100644 --- a/PresentationLayer/UIComponents/Modifiers/SizeObserver.swift +++ b/PresentationLayer/UIComponents/Modifiers/SizeObserver.swift @@ -44,7 +44,7 @@ struct SizeObserver: ViewModifier { private struct FramePreferenceKey: PreferenceKey { typealias Value = CGRect - static var defaultValue: Value = .zero + nonisolated(unsafe) static var defaultValue: Value = .zero static func reduce(value _: inout Value, nextValue: () -> Value) { _ = nextValue() diff --git a/PresentationLayer/Utils/Weather/WeatherUnitsManager.swift b/PresentationLayer/Utils/Weather/WeatherUnitsManager.swift index 83f1a34e3..3c9c7c46f 100644 --- a/PresentationLayer/Utils/Weather/WeatherUnitsManager.swift +++ b/PresentationLayer/Utils/Weather/WeatherUnitsManager.swift @@ -10,8 +10,8 @@ import Toolkit import DomainLayer class WeatherUnitsManager: ObservableObject { - - static let `default`: WeatherUnitsManager = { + + nonisolated(unsafe) static let `default`: WeatherUnitsManager = { let useCase = SwinjectHelper.shared.getContainerForSwinject().resolve(MainUseCase.self)! let manager = WeatherUnitsManager(mainUseCase: useCase) diff --git a/station-widget/StationTimelineEntry.swift b/station-widget/StationTimelineEntry.swift index 095fae558..04a9cfb1e 100644 --- a/station-widget/StationTimelineEntry.swift +++ b/station-widget/StationTimelineEntry.swift @@ -9,7 +9,7 @@ import Foundation import WidgetKit import DomainLayer -struct StationTimelineEntry: TimelineEntry { +struct StationTimelineEntry: @unchecked Sendable, TimelineEntry { let date: Date let displaySize: CGSize let timelineCase: TimelineCase diff --git a/station-widget/Station_widget.swift b/station-widget/Station_widget.swift index 7939357c5..04c85bd2e 100644 --- a/station-widget/Station_widget.swift +++ b/station-widget/Station_widget.swift @@ -7,10 +7,10 @@ import WidgetKit import SwiftUI -import DomainLayer +@preconcurrency import DomainLayer import Toolkit -struct Provider: IntentTimelineProvider { +struct Provider: IntentTimelineProvider, @unchecked Sendable { private let useCase: WidgetUseCase private let cancellableWrapper: CancellableWrapper = .init() private let refreshInterval: TimeInterval = 5.0 * 60.0 // 5 mins @@ -49,19 +49,22 @@ struct Provider: IntentTimelineProvider { completion(entry) } - func getTimeline(for configuration: StationWidgetConfigurationIntent, + + func getTimeline(for configuration: StationWidgetConfigurationIntent, in context: Context, - completion: @escaping (Timeline) -> Void) { + completion: @escaping @Sendable (Timeline) -> Void) { let nextUpdate = Date.now.advanced(by: refreshInterval) let displaySize = context.displaySize - Task { - let entry = await getEntry(for: configuration.selectedStation, displaySize: displaySize) + nonisolated(unsafe) let station = configuration.selectedStation + Task { @MainActor in + let entry = await getEntry(for: station, displaySize: displaySize) let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) completion(timeline) } } + nonisolated private func getEntry(for station: Station?, displaySize: CGSize) async -> StationTimelineEntry { let isUserLoggedIn = useCase.isUserLoggedIn guard let result = try? await useCase.getDevices(useCache: true) else { diff --git a/wxm-ios.xcodeproj/project.pbxproj b/wxm-ios.xcodeproj/project.pbxproj index 8e0c97d0f..58d874b9e 100644 --- a/wxm-ios.xcodeproj/project.pbxproj +++ b/wxm-ios.xcodeproj/project.pbxproj @@ -3719,6 +3719,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3755,6 +3756,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3791,6 +3793,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/wxm-ios/Swinject/SwinjectHelper.swift b/wxm-ios/Swinject/SwinjectHelper.swift index 82e7a047d..a81ddb325 100644 --- a/wxm-ios/Swinject/SwinjectHelper.swift +++ b/wxm-ios/Swinject/SwinjectHelper.swift @@ -5,14 +5,14 @@ // Created by Hristos Condrea on 6/5/22. // -import DataLayer -import DomainLayer +@preconcurrency import DataLayer +@preconcurrency import DomainLayer import Foundation import Swinject import Toolkit class SwinjectHelper: SwinjectInterface { - static let shared = SwinjectHelper() + nonisolated(unsafe) static let shared = SwinjectHelper() private init() {} From 0d9016c8ebf4f6b0a3a958679bc7b0d81c969405 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Mon, 25 Nov 2024 15:29:39 +0200 Subject: [PATCH 06/12] fixup! Eliminate warnings (WIP) --- .../WeatherOverview/WeatherOverviewView+Content.swift | 2 +- .../WeatherOverview/WeatherOverviewView.swift | 2 +- station-widget/Extenstions/DeviceDetails+Widget.swift | 2 +- station-widget/StationTimelineEntry.swift | 2 +- station-widget/Views/ErrorView.swift | 2 +- station-widget/Views/StationWidgetView.swift | 2 +- wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj | 2 +- .../ApiRequestBuilders/MeApiRequestBuilder.swift | 2 +- .../Networking/Interceptors/AuthInterceptor.swift | 8 ++++---- wxm-ios/DomainLayer/DomainLayer.xcodeproj/project.pbxproj | 5 ++++- wxm-ios/Swinject/SwinjectHelper.swift | 2 +- 11 files changed, 17 insertions(+), 14 deletions(-) diff --git a/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView+Content.swift b/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView+Content.swift index 15d216dbe..b1a819905 100644 --- a/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView+Content.swift +++ b/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView+Content.swift @@ -6,7 +6,7 @@ // import SwiftUI -import DomainLayer +@preconcurrency import DomainLayer private extension WeatherField { func attributedString(from weather: CurrentWeather?, diff --git a/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView.swift b/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView.swift index 6a388188f..33989764b 100644 --- a/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView.swift +++ b/PresentationLayer/UIComponents/BaseComponents/WeatherOverview/WeatherOverviewView.swift @@ -6,7 +6,7 @@ // import SwiftUI -import DomainLayer +@preconcurrency import DomainLayer struct WeatherOverviewView: View { var mode: Mode = .default diff --git a/station-widget/Extenstions/DeviceDetails+Widget.swift b/station-widget/Extenstions/DeviceDetails+Widget.swift index b6540ec2c..69cda98ea 100644 --- a/station-widget/Extenstions/DeviceDetails+Widget.swift +++ b/station-widget/Extenstions/DeviceDetails+Widget.swift @@ -5,7 +5,7 @@ // Created by Pantelis Giazitsis on 18/10/23. // -import DomainLayer +@preconcurrency import DomainLayer // MARK: - Mock diff --git a/station-widget/StationTimelineEntry.swift b/station-widget/StationTimelineEntry.swift index 04a9cfb1e..34e0ce28f 100644 --- a/station-widget/StationTimelineEntry.swift +++ b/station-widget/StationTimelineEntry.swift @@ -7,7 +7,7 @@ import Foundation import WidgetKit -import DomainLayer +@preconcurrency import DomainLayer struct StationTimelineEntry: @unchecked Sendable, TimelineEntry { let date: Date diff --git a/station-widget/Views/ErrorView.swift b/station-widget/Views/ErrorView.swift index bb84f8c9f..eeee90588 100644 --- a/station-widget/Views/ErrorView.swift +++ b/station-widget/Views/ErrorView.swift @@ -6,7 +6,7 @@ // import SwiftUI -import DomainLayer +@preconcurrency import DomainLayer import WidgetKit struct ErrorView: View { diff --git a/station-widget/Views/StationWidgetView.swift b/station-widget/Views/StationWidgetView.swift index 4ac6db064..dd754ef58 100644 --- a/station-widget/Views/StationWidgetView.swift +++ b/station-widget/Views/StationWidgetView.swift @@ -7,7 +7,7 @@ import SwiftUI import WidgetKit -import DomainLayer +@preconcurrency import DomainLayer struct StationWidgetView: View { let entry: StationTimelineEntry diff --git a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj index 52648ee82..6d87c32a7 100644 --- a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj +++ b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj @@ -487,7 +487,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1530; + LastUpgradeCheck = 1610; TargetAttributes = { B5799E7228254CC100FEBB85 = { CreatedOnToolsVersion = 13.3; diff --git a/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift b/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift index c6400cde1..8b3f32db0 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift @@ -6,7 +6,7 @@ // import Alamofire -import DomainLayer +@preconcurrency import DomainLayer import Foundation enum MeApiRequestBuilder: URLRequestConvertible { diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift index 64d677672..9ebcf495a 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift @@ -5,13 +5,13 @@ // Created by Hristos Condrea on 8/8/22. // -import Alamofire -import Combine -import DomainLayer +@preconcurrency import Alamofire +@preconcurrency import Combine +@preconcurrency import DomainLayer import Toolkit import UIKit -class AuthInterceptor: RequestInterceptor { +class AuthInterceptor: @unchecked Sendable, RequestInterceptor { let retryLimit = 4 let retryDelay: TimeInterval = 1 let keychainHelperService = KeychainHelperService() diff --git a/wxm-ios/DomainLayer/DomainLayer.xcodeproj/project.pbxproj b/wxm-ios/DomainLayer/DomainLayer.xcodeproj/project.pbxproj index f37a3a39a..0d0e3f94f 100644 --- a/wxm-ios/DomainLayer/DomainLayer.xcodeproj/project.pbxproj +++ b/wxm-ios/DomainLayer/DomainLayer.xcodeproj/project.pbxproj @@ -487,7 +487,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1530; - LastUpgradeCheck = 1530; + LastUpgradeCheck = 1610; TargetAttributes = { B5799E8C28254DCD00FEBB85 = { CreatedOnToolsVersion = 13.3; @@ -619,6 +619,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; @@ -719,6 +720,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; @@ -783,6 +785,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; diff --git a/wxm-ios/Swinject/SwinjectHelper.swift b/wxm-ios/Swinject/SwinjectHelper.swift index a81ddb325..1958fe012 100644 --- a/wxm-ios/Swinject/SwinjectHelper.swift +++ b/wxm-ios/Swinject/SwinjectHelper.swift @@ -8,7 +8,7 @@ @preconcurrency import DataLayer @preconcurrency import DomainLayer import Foundation -import Swinject +@preconcurrency import Swinject import Toolkit class SwinjectHelper: SwinjectInterface { From bb89dff3d4bee926b25acad73e1f90b3188fec15 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Mon, 25 Nov 2024 15:33:58 +0200 Subject: [PATCH 07/12] Changed swift version to 6 --- wxm-ios.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wxm-ios.xcodeproj/project.pbxproj b/wxm-ios.xcodeproj/project.pbxproj index 58d874b9e..471996bd8 100644 --- a/wxm-ios.xcodeproj/project.pbxproj +++ b/wxm-ios.xcodeproj/project.pbxproj @@ -3720,7 +3720,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -3757,7 +3757,7 @@ SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Mock; @@ -3794,7 +3794,7 @@ SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; From 97a83615bb5f9cf6b53fb0a7824834cc49286ec6 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Mon, 25 Nov 2024 15:46:25 +0200 Subject: [PATCH 08/12] Updated swift version in intent --- station-intent/IntentHandler.swift | 2 +- wxm-ios.xcodeproj/project.pbxproj | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/station-intent/IntentHandler.swift b/station-intent/IntentHandler.swift index 85fc323e8..7708bbb30 100644 --- a/station-intent/IntentHandler.swift +++ b/station-intent/IntentHandler.swift @@ -6,7 +6,7 @@ // import Intents -import DomainLayer +@preconcurrency import DomainLayer import Combine import Toolkit diff --git a/wxm-ios.xcodeproj/project.pbxproj b/wxm-ios.xcodeproj/project.pbxproj index 471996bd8..75b984aca 100644 --- a/wxm-ios.xcodeproj/project.pbxproj +++ b/wxm-ios.xcodeproj/project.pbxproj @@ -3532,7 +3532,8 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -3565,7 +3566,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Mock; @@ -3599,7 +3601,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; From c85ee4cb66d6856134de6034918d3f10cc473e14 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 27 Nov 2024 11:57:23 +0200 Subject: [PATCH 09/12] WIP --- wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj | 3 +++ wxm-ios/DataLayer/DataLayer/Database/DatabaseService.swift | 2 +- .../Networking/Interceptors/RequestHeadersAdapter.swift | 6 +++--- .../DataLayer/DataLayer/Networking/Mock/MockProtocol.swift | 2 +- .../Entities/Codables/Me/Body/ClaimDeviceBody.swift | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj index 6d87c32a7..c972d717e 100644 --- a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj +++ b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj @@ -719,6 +719,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = MOCK; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -877,6 +878,7 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -912,6 +914,7 @@ SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; diff --git a/wxm-ios/DataLayer/DataLayer/Database/DatabaseService.swift b/wxm-ios/DataLayer/DataLayer/Database/DatabaseService.swift index 56c6375bf..266f682af 100644 --- a/wxm-ios/DataLayer/DataLayer/Database/DatabaseService.swift +++ b/wxm-ios/DataLayer/DataLayer/Database/DatabaseService.swift @@ -9,7 +9,7 @@ import CoreData class DatabaseService { - static let shared: DatabaseService = DatabaseService() + nonisolated(unsafe) static let shared: DatabaseService = DatabaseService() var context: NSManagedObjectContext { persistentContainer.viewContext diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift index 0f94c6230..5399de4ed 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift @@ -10,7 +10,7 @@ import Alamofire import Toolkit import UIKit -class RequestHeadersAdapter: RequestAdapter { +class RequestHeadersAdapter: @unchecked Sendable, RequestAdapter { func adapt(_ urlRequest: URLRequest, for session: Alamofire.Session, completion: @escaping (Result) -> Void) { var urlRequest = urlRequest @@ -22,8 +22,8 @@ class RequestHeadersAdapter: RequestAdapter { } private extension RequestHeadersAdapter { - func generateClientId(callback: @escaping GenericCallback) { - Task { + func generateClientId(callback: @escaping @Sendable GenericCallback) { + Task { // App Info let bundleId: String = Bundle.main.bundleID let appVersion: String = Bundle.main.releaseVersionNumberPretty diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift b/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift index 820146faf..9cd249989 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift @@ -17,7 +17,7 @@ class MockProtocol: URLProtocol { /// A static dictionary where we keep the moke file name for each mocked endpoint. Every entry is expected to be inserted from `ApiClient` /// The stucture is [`request url`: `file name`] /// eg ["https:/api-mock.weatherxm.com/api/v1/me/devices/123": "get_user_device"] - static var responses: [String: String] = [:] + nonisolated(unsafe) static var responses: [String: String] = [:] override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { super.init(request: request, cachedResponse: cachedResponse, client: client) diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Me/Body/ClaimDeviceBody.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Me/Body/ClaimDeviceBody.swift index 81d307a65..5a53a2b39 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Me/Body/ClaimDeviceBody.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Me/Body/ClaimDeviceBody.swift @@ -19,7 +19,7 @@ public struct ClaimDeviceBody { self.secret = secret } - public var dictionaryRepresentation: [String: Any] { + public var dictionaryRepresentation: [String: Any & Sendable] { var dict: [String: Any] = [ "serialNumber": serialNumber as String, "location": [ From 1748aa784bc345a6b63e0e252c731b3cc6cc9031 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 27 Nov 2024 17:52:33 +0200 Subject: [PATCH 10/12] fixup! WIP --- .../DataLayer/Networking/ApiClient.swift | 2 +- .../Interceptors/RequestHeadersAdapter.swift | 43 +++++++++---------- .../Networking/Mock/MockProtocol.swift | 2 +- .../AuthRepositoryImpl.swift | 2 +- .../Bluetooth/BTActionsWrapper.swift | 4 +- .../DeviceInfoRepositoryImpl.swift | 12 +++--- .../DevicesRepositoryImpl.swift | 2 +- .../ExplorerRepositoryImpl.swift | 2 +- .../LocationRepositoryImpl.swift | 2 +- .../MeRepositoryImpl.swift | 2 +- .../NetworkRepositoryImpl.swift | 2 +- wxm-ios/DataLayer/UserDevicesService.swift | 7 ++- wxm-ios/DataLayer/UserInfoService.swift | 4 +- .../BluetoothDevicesRepository.swift | 6 +-- .../DeviceInfoRepository.swift | 8 ++-- .../FirebaseManager/FirebaseManager.swift | 4 +- 16 files changed, 51 insertions(+), 53 deletions(-) diff --git a/wxm-ios/DataLayer/DataLayer/Networking/ApiClient.swift b/wxm-ios/DataLayer/DataLayer/Networking/ApiClient.swift index fe7bb5e11..d7fa8e1a9 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/ApiClient.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/ApiClient.swift @@ -30,7 +30,7 @@ public class ApiClient { return session }() - static let shared: ApiClient = ApiClient() + nonisolated(unsafe) static let shared: ApiClient = ApiClient() #warning("TODO: Improve publishers caching. For now, we perform everything in main thread to avoid race conditions") private let queue = DispatchQueue.main diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift index 5399de4ed..6ba359ee2 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/RequestHeadersAdapter.swift @@ -12,35 +12,34 @@ import UIKit class RequestHeadersAdapter: @unchecked Sendable, RequestAdapter { - func adapt(_ urlRequest: URLRequest, for session: Alamofire.Session, completion: @escaping (Result) -> Void) { - var urlRequest = urlRequest - generateClientId { clientId in - urlRequest.setValue(clientId, forHTTPHeaderField: NetworkConstants.HttpHeaderField.clientIdentifier.rawValue) - completion(.success(urlRequest)) - } + func adapt(_ urlRequest: URLRequest, for session: Alamofire.Session, completion: @escaping @Sendable (Result) -> Void) { + var urlRequest = urlRequest + Task { @MainActor in + let clientId = await generateClientId() + urlRequest.setValue(clientId, forHTTPHeaderField: NetworkConstants.HttpHeaderField.clientIdentifier.rawValue) + completion(.success(urlRequest)) + } } } private extension RequestHeadersAdapter { - func generateClientId(callback: @escaping @Sendable GenericCallback) { - Task { - // App Info - let bundleId: String = Bundle.main.bundleID - let appVersion: String = Bundle.main.releaseVersionNumberPretty - let buildNumber: String = Bundle.main.buildVersionNumber ?? "" + func generateClientId() async -> String { + // App Info + let bundleId: String = Bundle.main.bundleID + let appVersion: String = Bundle.main.releaseVersionNumberPretty + let buildNumber: String = Bundle.main.buildVersionNumber ?? "" - let installationId: String = await FirebaseManager.shared.getInstallationId() + let installationId: String = await FirebaseManager.shared.getInstallationId() - let appInfo = "wxm-ios (\(bundleId)); \(appVersion)(\(buildNumber)); \(installationId)" + let appInfo = "wxm-ios (\(bundleId)); \(appVersion)(\(buildNumber)); \(installationId)" - // iOS Info - let systemVersion = await UIDevice.current.systemVersion - let iOSInfo = "iOS: \(systemVersion)" + // iOS Info + let systemVersion = await UIDevice.current.systemVersion + let iOSInfo = "iOS: \(systemVersion)" - // Device Info - let deviceInfo = "Device: \(await UIDevice.modelName)" + // Device Info + let deviceInfo = "Device: \(await UIDevice.modelName)" - callback("\(appInfo); \(iOSInfo); \(deviceInfo)") - } - } + return "\(appInfo); \(iOSInfo); \(deviceInfo)" + } } diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift b/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift index 9cd249989..717dedbe9 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Mock/MockProtocol.swift @@ -13,7 +13,7 @@ protocol MockResponseBuilder { } /// Monitors the url requests and if provided a mock file we return the file data as response -class MockProtocol: URLProtocol { +class MockProtocol: URLProtocol, @unchecked Sendable { /// A static dictionary where we keep the moke file name for each mocked endpoint. Every entry is expected to be inserted from `ApiClient` /// The stucture is [`request url`: `file name`] /// eg ["https:/api-mock.weatherxm.com/api/v1/me/devices/123": "get_user_device"] diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/AuthRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/AuthRepositoryImpl.swift index 87de223ab..a33a9567a 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/AuthRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/AuthRepositoryImpl.swift @@ -7,7 +7,7 @@ import Alamofire import Combine -import DomainLayer +@preconcurrency import DomainLayer import Foundation import WidgetKit diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift index afafe2a28..789fabea4 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift @@ -7,7 +7,7 @@ import Foundation import Combine -import DomainLayer +@preconcurrency import DomainLayer /// Wraps the bluetooth actions such as connect to station, reboot, set frequency etc. /// The goal is to encapsulate the apple's API with delegation pattern and expose async functions @@ -220,7 +220,7 @@ private extension BTActionWrapper { return } self.stopScanning() - continuation.resume(returning: btDevice) + continuation.resume(returning: btDevice) }.store(in: &self.cancellables) } } diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift index ea98a37df..4f9d3f6ea 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift @@ -6,12 +6,12 @@ // import Foundation -import DomainLayer +@preconcurrency import DomainLayer import Combine import Alamofire -public class DeviceInfoRepositoryImpl: DeviceInfoRepository { - private lazy var bluetoothWrapper: BTActionWrapper = BTActionWrapper() +public class DeviceInfoRepositoryImpl: @unchecked Sendable, DeviceInfoRepository { + nonisolated(unsafe) private lazy var bluetoothWrapper: BTActionWrapper = BTActionWrapper() private var canellables: Set = [] private let userDevicesService: UserDevicesService @@ -41,15 +41,15 @@ extension DeviceInfoRepositoryImpl { public func rebootStation(device: DeviceDetails) -> AnyPublisher { let valueSubject = CurrentValueSubject(.connect) - Task { + Task { [weak self] in valueSubject.send(.connect) - if let error = await bluetoothWrapper.connect(device: device) { + if let error = await self?.bluetoothWrapper.connect(device: device) { valueSubject.send(.failed(error.toRebootError)) return } valueSubject.send(.rebooting) - if let error = await bluetoothWrapper.reboot(device: device) { + if let error = await self?.bluetoothWrapper.reboot(device: device) { valueSubject.send(.failed(error.toRebootError)) return } diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DevicesRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DevicesRepositoryImpl.swift index e24f0a339..f6ba5f977 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DevicesRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DevicesRepositoryImpl.swift @@ -7,7 +7,7 @@ import Alamofire import Combine -import DomainLayer +@preconcurrency import DomainLayer import Foundation import Toolkit diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/ExplorerRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/ExplorerRepositoryImpl.swift index 6e70a558f..a1ac501a6 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/ExplorerRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/ExplorerRepositoryImpl.swift @@ -8,7 +8,7 @@ import struct Alamofire.DataResponse import Combine import CoreLocation -import DomainLayer +@preconcurrency import DomainLayer import Toolkit public struct ExplorerRepositoryImpl: ExplorerRepository { diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift index a3a8c921f..e143ec27a 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift @@ -7,7 +7,7 @@ import Combine import DomainLayer -import MapboxSearch +@preconcurrency import MapboxSearch import Toolkit public class DeviceLocationRepositoryImpl: DeviceLocationRepository { diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/MeRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/MeRepositoryImpl.swift index 5a6451893..465f74796 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/MeRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/MeRepositoryImpl.swift @@ -7,7 +7,7 @@ import Alamofire import Combine -import DomainLayer +@preconcurrency import DomainLayer import Foundation import Toolkit diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/NetworkRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/NetworkRepositoryImpl.swift index 66a86d28e..8303e8d15 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/NetworkRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/NetworkRepositoryImpl.swift @@ -5,7 +5,7 @@ // Created by Pantelis Giazitsis on 12/6/23. // -import DomainLayer +@preconcurrency import DomainLayer import Combine import Alamofire diff --git a/wxm-ios/DataLayer/UserDevicesService.swift b/wxm-ios/DataLayer/UserDevicesService.swift index 43a8b60de..0cf3a8c81 100644 --- a/wxm-ios/DataLayer/UserDevicesService.swift +++ b/wxm-ios/DataLayer/UserDevicesService.swift @@ -8,7 +8,7 @@ import Foundation import Combine import Alamofire -import DomainLayer +@preconcurrency import DomainLayer import Toolkit import WidgetKit @@ -16,7 +16,7 @@ extension Notification.Name { static let userDevicesListUpdated = Notification.Name("userDevices.updated") } -public class UserDevicesService { +public class UserDevicesService: @unchecked Sendable { private var cancellableSet: Set = [] private let cacheValidationInterval: TimeInterval = 3.0 * 60.0 // 3 minutes @@ -158,7 +158,6 @@ public class UserDevicesService { .eraseToAnyPublisher() } - @MainActor func getFollowState(deviceId: String) async throws -> Result { // If the id is in cache the device id is for a user device if let cachedStates = followStatesCache.getValue(for: followStatesCacheKey) { @@ -182,7 +181,7 @@ public class UserDevicesService { continuation.resume(returning: .success(userDeviceState)) } } - .storeThreadSafe(in: &cancellableSet) + .store(in: &cancellableSet) } catch { continuation.resume(throwing: error) diff --git a/wxm-ios/DataLayer/UserInfoService.swift b/wxm-ios/DataLayer/UserInfoService.swift index baaab5bcd..1c53e2c8f 100644 --- a/wxm-ios/DataLayer/UserInfoService.swift +++ b/wxm-ios/DataLayer/UserInfoService.swift @@ -6,12 +6,12 @@ // import Foundation -import DomainLayer +@preconcurrency import DomainLayer import Toolkit import Combine import Alamofire -public class UserInfoService { +public class UserInfoService: @unchecked Sendable { private var cancellableSet: Set = [] private var userInfoSubject = CurrentValueSubject(nil) diff --git a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/BluetoothDevicesRepository.swift b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/BluetoothDevicesRepository.swift index 1ac65cd3d..842ae1c3b 100644 --- a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/BluetoothDevicesRepository.swift +++ b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/BluetoothDevicesRepository.swift @@ -33,7 +33,7 @@ public struct BTWXMDeviceInfo { } } -public enum BluetoothState { +public enum BluetoothState: Sendable { case unsupported case unauthorized case poweredOff @@ -42,7 +42,7 @@ public enum BluetoothState { case resetting } -public enum BTWXMDeviceState: String { +public enum BTWXMDeviceState: String, Sendable { case unknown case disconnected case connecting @@ -50,7 +50,7 @@ public enum BTWXMDeviceState: String { case disconnecting } -public struct BTWXMDevice: Equatable, Identifiable { +public struct BTWXMDevice: Equatable, Identifiable, Sendable { public var id: UUID { return identifier } diff --git a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/DeviceInfoRepository.swift b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/DeviceInfoRepository.swift index 4a247c0ba..8cf8e4ab3 100644 --- a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/DeviceInfoRepository.swift +++ b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/DeviceInfoRepository.swift @@ -17,28 +17,28 @@ public protocol DeviceInfoRepository { func changeFrequency(device: DeviceDetails, frequency: Frequency) -> AnyPublisher } -public enum RebootStationState { +public enum RebootStationState: Sendable { case connect case rebooting case failed(RebootError) case finished } -public enum RebootError { +public enum RebootError: Sendable { case bluetooth(BluetoothState) case notInRange case connect case unknown } -public enum ChangeFrequencyState { +public enum ChangeFrequencyState: Sendable { case connect case changing case failed(ChangeFrequencyError) case finished } -public enum ChangeFrequencyError { +public enum ChangeFrequencyError: Sendable { case bluetooth(BluetoothState) case notInRange case connect diff --git a/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift b/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift index 2c40bb864..08d3554cc 100644 --- a/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift +++ b/wxm-ios/Toolkit/Toolkit/FirebaseManager/FirebaseManager.swift @@ -12,8 +12,8 @@ import FirebaseAnalytics import FirebaseMessaging import Combine -public class FirebaseManager { - nonisolated(unsafe) public static let shared: FirebaseManager = .init() +public class FirebaseManager: @unchecked Sendable { + public static let shared: FirebaseManager = .init() public var latestReceivedNotificationPublisher: AnyPublisher? { firebaseManagerImpl.latestReceivedNotificationPublisher } From 51139da69795d9694b3053730cdff632a46a0ed5 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Thu, 28 Nov 2024 12:56:56 +0200 Subject: [PATCH 11/12] Eliminate warnings --- .../Bluetooth/BTActionsWrapper.swift | 2 +- .../RepositoryImplementations/DeviceInfoRepositoryImpl.swift | 5 +++-- .../RepositoryImplementations/FirmwareUpdateImpl.swift | 2 +- .../DomainRepositoryInterfaces/MeRepository.swift | 2 +- .../Entities/Codables/Devices/NetworkDevicesTypes.swift | 2 +- .../DomainLayer/Entities/DomainModels/DeviceDetails.swift | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift index 789fabea4..ace3f6f3d 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/Bluetooth/BTActionsWrapper.swift @@ -11,7 +11,7 @@ import Combine /// Wraps the bluetooth actions such as connect to station, reboot, set frequency etc. /// The goal is to encapsulate the apple's API with delegation pattern and expose async functions -class BTActionWrapper { +class BTActionWrapper: @unchecked Sendable { private let bluetoothManager: BluetoothManager private var cancellables: Set = [] private var scanningTimeoutWorkItem: DispatchWorkItem? diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift index 4f9d3f6ea..ddaffdfb9 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/DeviceInfoRepositoryImpl.swift @@ -7,7 +7,7 @@ import Foundation @preconcurrency import DomainLayer -import Combine +@preconcurrency import Combine import Alamofire public class DeviceInfoRepositoryImpl: @unchecked Sendable, DeviceInfoRepository { @@ -41,7 +41,8 @@ extension DeviceInfoRepositoryImpl { public func rebootStation(device: DeviceDetails) -> AnyPublisher { let valueSubject = CurrentValueSubject(.connect) - Task { [weak self] in + + Task { @MainActor [weak self, valueSubject] in valueSubject.send(.connect) if let error = await self?.bluetoothWrapper.connect(device: device) { valueSubject.send(.failed(error.toRebootError)) diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/FirmwareUpdateImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/FirmwareUpdateImpl.swift index dc3b75c03..f2ef92a07 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/FirmwareUpdateImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/FirmwareUpdateImpl.swift @@ -9,7 +9,7 @@ import Combine import DomainLayer import Foundation -public class FirmwareUpdateImpl { +public class FirmwareUpdateImpl: @unchecked Sendable { private let stateSubject = CurrentValueSubject(.unknown) private var cancellableSet: Set = [] private var updater: FirmwareUpdater? diff --git a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/MeRepository.swift b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/MeRepository.swift index 254e41223..633484d05 100644 --- a/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/MeRepository.swift +++ b/wxm-ios/DomainLayer/DomainLayer/DomainRepositoryInterfaces/MeRepository.swift @@ -25,7 +25,7 @@ public enum DeviceRewardsMode: String, CaseIterable { case year } -public struct UserDeviceFollowState: Hashable, Codable { +public struct UserDeviceFollowState: Hashable, Codable, Sendable { public let deviceId: String public let relation: DeviceRelation diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift index 5b440c77a..a81364760 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift @@ -23,7 +23,7 @@ public struct Firmware: Codable { } } -public enum DeviceRelation: String, Codable { +public enum DeviceRelation: String, Codable, Sendable { case owned case followed } diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceDetails.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceDetails.swift index 823a1251f..b3c131289 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceDetails.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceDetails.swift @@ -7,7 +7,7 @@ import Foundation -public struct DeviceDetails { +public struct DeviceDetails: @unchecked Sendable { public var id: String? public var name: String public var friendlyName: String? From d878904ff49b890062170120b6d3204c912c800a Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Thu, 28 Nov 2024 14:11:29 +0200 Subject: [PATCH 12/12] Updated swift version --- wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj index c972d717e..aadb7d935 100644 --- a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj +++ b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj @@ -720,7 +720,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 1; }; name = Mock; @@ -879,7 +879,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -915,7 +915,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release;