From 008b78cc7cde462b28b618f859438b262b66c8f4 Mon Sep 17 00:00:00 2001 From: Davide Date: Wed, 13 Nov 2024 12:07:30 +0100 Subject: [PATCH] Upgrade to Xcode 16 (#858) - Address further restrictions on actor-isolation by using `nonisolated` on: - Combine subjects - Core Data context/controller - Blocks - In previews using inline `@State`, create a custom view instead - Use `@retroactive` in l10n extensions - Fix compile error in WireGuardKit --- .../xcshareddata/swiftpm/Package.resolved | 11 +++--- Passepartout/Library/Package.swift | 6 +-- .../CDProviderRepositoryV3.swift | 10 ++--- .../AppUIMain/Views/Profile/NameSection.swift | 26 ++++++++----- .../Business/ProfileProcessor.swift | 10 ++--- .../IAP/FallbackReceiptReader.swift | 2 +- .../Mock/MockAppProductHelper.swift | 2 +- .../Business/CoreDataRepository.swift | 10 ++--- .../Extensions/UUID+RawRepresentable.swift | 36 ----------------- .../CommonUtils/IAP/StoreKitHelper.swift | 2 +- .../UILibrary/L10n/AppError+L10n.swift | 2 +- .../UILibrary/L10n/PassepartoutKit+L10n.swift | 2 +- .../Modules/OpenVPNView+Credentials.swift | 26 ++++++++----- .../Views/UI/EditableListSection.swift | 39 +++++++++++-------- 14 files changed, 85 insertions(+), 99 deletions(-) delete mode 100644 Passepartout/Library/Sources/CommonUtils/Extensions/UUID+RawRepresentable.swift diff --git a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e14806e4..f4b7839c 100644 --- a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,7 +41,8 @@ "kind" : "remoteSourceControl", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source", "state" : { - "revision" : "3a4c78af67dfe181acc657a5539ee3d62d1c9361" + "revision" : "3a4c78af67dfe181acc657a5539ee3d62d1c9361", + "version" : "0.11.0" } }, { @@ -58,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", "state" : { - "revision" : "8d142c806fb7dc4a2cd754d38d99da0d6398b811", - "version" : "0.9.1" + "revision" : "256a4a8265b7d214bb35f4e29e18c27e2dc49137", + "version" : "0.9.2" } }, { @@ -76,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/passepartoutvpn/wireguard-apple", "state" : { - "revision" : "a896f784bc5ed94f29d97e376be5cfa08d4a5d44", - "version" : "1.1.1" + "revision" : "d8bcdf22f1e75d80caac874f302dee86194bb71d", + "version" : "1.1.2" } } ], diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift index 336031cf..dd3591d5 100644 --- a/Passepartout/Library/Package.swift +++ b/Passepartout/Library/Package.swift @@ -49,13 +49,13 @@ let package = Package( ) ], dependencies: [ -// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"), - .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "3a4c78af67dfe181acc657a5539ee3d62d1c9361"), + .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.11.0"), +// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "3a4c78af67dfe181acc657a5539ee3d62d1c9361"), // .package(path: "../../../passepartoutkit-source"), .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"), // .package(path: "../../../passepartoutkit-source-openvpn-openssl"), - .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", from: "0.9.1"), + .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", from: "0.9.2"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", revision: "ea39fa396e98cfd2b9a235f0a801aaf03a37e30a"), // .package(path: "../../../passepartoutkit-source-wireguard-go"), .package(url: "https://github.com/Cocoanetics/Kvitto", from: "1.0.0") diff --git a/Passepartout/Library/Sources/AppDataProviders/CDProviderRepositoryV3.swift b/Passepartout/Library/Sources/AppDataProviders/CDProviderRepositoryV3.swift index 37485c0f..ca84bc07 100644 --- a/Passepartout/Library/Sources/AppDataProviders/CDProviderRepositoryV3.swift +++ b/Passepartout/Library/Sources/AppDataProviders/CDProviderRepositoryV3.swift @@ -40,15 +40,15 @@ extension AppData { } actor CDProviderRepositoryV3: NSObject, ProviderRepository { - private let context: NSManagedObjectContext + private nonisolated let context: NSManagedObjectContext - private let backgroundContext: NSManagedObjectContext + private nonisolated let backgroundContext: NSManagedObjectContext - private let providersSubject: CurrentValueSubject<[ProviderMetadata], Never> + private nonisolated let providersSubject: CurrentValueSubject<[ProviderMetadata], Never> - private let lastUpdateSubject: CurrentValueSubject<[ProviderID: Date], Never> + private nonisolated let lastUpdateSubject: CurrentValueSubject<[ProviderID: Date], Never> - private let providersController: NSFetchedResultsController + private nonisolated let providersController: NSFetchedResultsController init(context: NSManagedObjectContext, backgroundContext: NSManagedObjectContext) { self.context = context diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Profile/NameSection.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/NameSection.swift index 217d8c9c..db3f8209 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Profile/NameSection.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/NameSection.swift @@ -43,17 +43,25 @@ struct NameSection: View { } } +// MARK: - Previews + #Preview { + struct ContentView: View { - @State - var name = "" + @State + private var name = "" - return Form { - NameSection( - name: $name, - placeholder: "My name" - ) + var body: some View { + Form { + NameSection( + name: $name, + placeholder: "My name" + ) + } + .themeForm() + } } - .themeForm() - .withMockEnvironment() + + return ContentView() + .withMockEnvironment() } diff --git a/Passepartout/Library/Sources/CommonLibrary/Business/ProfileProcessor.swift b/Passepartout/Library/Sources/CommonLibrary/Business/ProfileProcessor.swift index 1d4327cb..d734fcb2 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Business/ProfileProcessor.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Business/ProfileProcessor.swift @@ -26,16 +26,16 @@ import Foundation import PassepartoutKit -public final class ProfileProcessor: ObservableObject { +public final class ProfileProcessor: ObservableObject, Sendable { private let iapManager: IAPManager - public let title: (Profile) -> String + public nonisolated let title: (Profile) -> String - private let _isIncluded: (IAPManager, Profile) -> Bool + private nonisolated let _isIncluded: (IAPManager, Profile) -> Bool - private let _willSave: (IAPManager, Profile.Builder) throws -> Profile.Builder + private nonisolated let _willSave: (IAPManager, Profile.Builder) throws -> Profile.Builder - private let _willConnect: (IAPManager, Profile) throws -> Profile + private nonisolated let _willConnect: (IAPManager, Profile) throws -> Profile public init( iapManager: IAPManager, diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift b/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift index 03517256..11016ed6 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift +++ b/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift @@ -31,7 +31,7 @@ import PassepartoutKit public actor FallbackReceiptReader: AppReceiptReader { private let reader: InAppReceiptReader? - private let localReader: (URL) -> InAppReceiptReader? + private nonisolated let localReader: (URL) -> InAppReceiptReader? private var pendingTask: Task? diff --git a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift b/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift index 7bace863..83853f8a 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift @@ -34,7 +34,7 @@ public actor MockAppProductHelper: AppProductHelper { public nonisolated let receiptReader: MockAppReceiptReader - private let didUpdateSubject: PassthroughSubject + private nonisolated let didUpdateSubject: PassthroughSubject // set .max to skip entitled products public init(build: Int = .max) { diff --git a/Passepartout/Library/Sources/CommonUtils/Business/CoreDataRepository.swift b/Passepartout/Library/Sources/CommonUtils/Business/CoreDataRepository.swift index e9b507bf..2e4dfb4f 100644 --- a/Passepartout/Library/Sources/CommonUtils/Business/CoreDataRepository.swift +++ b/Passepartout/Library/Sources/CommonUtils/Business/CoreDataRepository.swift @@ -46,17 +46,17 @@ public actor CoreDataRepository: NSObject, private let entityName: String - private let context: NSManagedObjectContext + private nonisolated let context: NSManagedObjectContext private let observingResults: Bool - private let fromMapper: (CD) throws -> T? + private nonisolated let fromMapper: (CD) throws -> T? - private let toMapper: (T, NSManagedObjectContext) throws -> CD + private nonisolated let toMapper: (T, NSManagedObjectContext) throws -> CD - private let onResultError: ((Error) -> CoreDataResultAction)? + private nonisolated let onResultError: ((Error) -> CoreDataResultAction)? - private let entitiesSubject: CurrentValueSubject, Never> + private nonisolated let entitiesSubject: CurrentValueSubject, Never> // cannot easily use CD as generic private var resultsController: NSFetchedResultsController diff --git a/Passepartout/Library/Sources/CommonUtils/Extensions/UUID+RawRepresentable.swift b/Passepartout/Library/Sources/CommonUtils/Extensions/UUID+RawRepresentable.swift deleted file mode 100644 index f959a10b..00000000 --- a/Passepartout/Library/Sources/CommonUtils/Extensions/UUID+RawRepresentable.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// UUID+RawRepresentable.swift -// Passepartout -// -// Created by Davide De Rosa on 8/11/24. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -extension UUID: RawRepresentable { - public var rawValue: String { - uuidString - } - - public init?(rawValue: RawValue) { - self.init(uuidString: rawValue) - } -} diff --git a/Passepartout/Library/Sources/CommonUtils/IAP/StoreKitHelper.swift b/Passepartout/Library/Sources/CommonUtils/IAP/StoreKitHelper.swift index 34853967..25e99a63 100644 --- a/Passepartout/Library/Sources/CommonUtils/IAP/StoreKitHelper.swift +++ b/Passepartout/Library/Sources/CommonUtils/IAP/StoreKitHelper.swift @@ -39,7 +39,7 @@ public final class StoreKitHelper: InAppHelper where ProductType: R private var activeTransactions: Set - private let didUpdateSubject: PassthroughSubject + private nonisolated let didUpdateSubject: PassthroughSubject private var observer: Task? diff --git a/Passepartout/Library/Sources/UILibrary/L10n/AppError+L10n.swift b/Passepartout/Library/Sources/UILibrary/L10n/AppError+L10n.swift index 7cd374e5..6a3dd652 100644 --- a/Passepartout/Library/Sources/UILibrary/L10n/AppError+L10n.swift +++ b/Passepartout/Library/Sources/UILibrary/L10n/AppError+L10n.swift @@ -55,7 +55,7 @@ extension AppError: LocalizedError { // MARK: - App side -extension PassepartoutError: LocalizedError { +extension PassepartoutError: @retroactive LocalizedError { public var errorDescription: String? { switch code { case .App.ineligibleProfile: diff --git a/Passepartout/Library/Sources/UILibrary/L10n/PassepartoutKit+L10n.swift b/Passepartout/Library/Sources/UILibrary/L10n/PassepartoutKit+L10n.swift index 6a32b980..ee94323e 100644 --- a/Passepartout/Library/Sources/UILibrary/L10n/PassepartoutKit+L10n.swift +++ b/Passepartout/Library/Sources/UILibrary/L10n/PassepartoutKit+L10n.swift @@ -144,7 +144,7 @@ extension OnDemandModule.Policy: LocalizableEntity { } } -extension ProviderID: CustomDebugStringConvertible { +extension ProviderID: @retroactive CustomDebugStringConvertible { public var debugDescription: String { rawValue } diff --git a/Passepartout/Library/Sources/UILibrary/Views/Modules/OpenVPNView+Credentials.swift b/Passepartout/Library/Sources/UILibrary/Views/Modules/OpenVPNView+Credentials.swift index 134e7ca1..5af7b766 100644 --- a/Passepartout/Library/Sources/UILibrary/Views/Modules/OpenVPNView+Credentials.swift +++ b/Passepartout/Library/Sources/UILibrary/Views/Modules/OpenVPNView+Credentials.swift @@ -209,18 +209,24 @@ private extension OpenVPNCredentialsView { } #Preview { + struct ContentView: View { - @State - var credentials: OpenVPN.Credentials? + @State + private var credentials: OpenVPN.Credentials? - @State - var isInteractive = true + @State + private var isInteractive = true - return NavigationStack { - OpenVPNCredentialsView( - isInteractive: $isInteractive, - credentials: $credentials - ) - .withMockEnvironment() + var body: some View { + NavigationStack { + OpenVPNCredentialsView( + isInteractive: $isInteractive, + credentials: $credentials + ) + } + } } + + return ContentView() + .withMockEnvironment() } diff --git a/Passepartout/Library/Sources/UILibrary/Views/UI/EditableListSection.swift b/Passepartout/Library/Sources/UILibrary/Views/UI/EditableListSection.swift index bce7ff76..cb9bbc0f 100644 --- a/Passepartout/Library/Sources/UILibrary/Views/UI/EditableListSection.swift +++ b/Passepartout/Library/Sources/UILibrary/Views/UI/EditableListSection.swift @@ -238,26 +238,33 @@ private extension EditableListSection { } #Preview { - @State - var originalItems = ["One", "Two", "Three"] + struct ContentView: View { - return Form { - EditableListSection( - "Title", - addTitle: "Add item", - originalItems: $originalItems - ) { - if $0 { - Text($1.wrappedValue) - } else { - TextField("", text: $1) + @State + private var originalItems = ["One", "Two", "Three"] + + var body: some View { + Form { + EditableListSection( + "Title", + addTitle: "Add item", + originalItems: $originalItems + ) { + if $0 { + Text($1.wrappedValue) + } else { + TextField("", text: $1) + } + } removeLabel: { action in + Button("Remove", action: action) + } editLabel: { + Image(systemName: "arrow.up.arrow.down") + } } - } removeLabel: { action in - Button("Remove", action: action) - } editLabel: { - Image(systemName: "arrow.up.arrow.down") } } + + return ContentView() } #endif