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
This commit is contained in:
Davide 2024-11-13 12:07:30 +01:00 committed by GitHub
parent c5047533b5
commit 008b78cc7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 85 additions and 99 deletions

View File

@ -41,7 +41,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
"state" : { "state" : {
"revision" : "3a4c78af67dfe181acc657a5539ee3d62d1c9361" "revision" : "3a4c78af67dfe181acc657a5539ee3d62d1c9361",
"version" : "0.11.0"
} }
}, },
{ {
@ -58,8 +59,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go",
"state" : { "state" : {
"revision" : "8d142c806fb7dc4a2cd754d38d99da0d6398b811", "revision" : "256a4a8265b7d214bb35f4e29e18c27e2dc49137",
"version" : "0.9.1" "version" : "0.9.2"
} }
}, },
{ {
@ -76,8 +77,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/passepartoutvpn/wireguard-apple", "location" : "https://github.com/passepartoutvpn/wireguard-apple",
"state" : { "state" : {
"revision" : "a896f784bc5ed94f29d97e376be5cfa08d4a5d44", "revision" : "d8bcdf22f1e75d80caac874f302dee86194bb71d",
"version" : "1.1.1" "version" : "1.1.2"
} }
} }
], ],

View File

@ -49,13 +49,13 @@ let package = Package(
) )
], ],
dependencies: [ dependencies: [
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"), .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.11.0"),
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "3a4c78af67dfe181acc657a5539ee3d62d1c9361"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "3a4c78af67dfe181acc657a5539ee3d62d1c9361"),
// .package(path: "../../../passepartoutkit-source"), // .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", from: "0.9.1"),
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
// .package(path: "../../../passepartoutkit-source-openvpn-openssl"), // .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(url: "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", revision: "ea39fa396e98cfd2b9a235f0a801aaf03a37e30a"),
// .package(path: "../../../passepartoutkit-source-wireguard-go"), // .package(path: "../../../passepartoutkit-source-wireguard-go"),
.package(url: "https://github.com/Cocoanetics/Kvitto", from: "1.0.0") .package(url: "https://github.com/Cocoanetics/Kvitto", from: "1.0.0")

View File

@ -40,15 +40,15 @@ extension AppData {
} }
actor CDProviderRepositoryV3: NSObject, ProviderRepository { 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<CDProviderV3> private nonisolated let providersController: NSFetchedResultsController<CDProviderV3>
init(context: NSManagedObjectContext, backgroundContext: NSManagedObjectContext) { init(context: NSManagedObjectContext, backgroundContext: NSManagedObjectContext) {
self.context = context self.context = context

View File

@ -43,17 +43,25 @@ struct NameSection: View {
} }
} }
// MARK: - Previews
#Preview { #Preview {
struct ContentView: View {
@State @State
var name = "" private var name = ""
return Form { var body: some View {
Form {
NameSection( NameSection(
name: $name, name: $name,
placeholder: "My name" placeholder: "My name"
) )
} }
.themeForm() .themeForm()
}
}
return ContentView()
.withMockEnvironment() .withMockEnvironment()
} }

View File

@ -26,16 +26,16 @@
import Foundation import Foundation
import PassepartoutKit import PassepartoutKit
public final class ProfileProcessor: ObservableObject { public final class ProfileProcessor: ObservableObject, Sendable {
private let iapManager: IAPManager 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( public init(
iapManager: IAPManager, iapManager: IAPManager,

View File

@ -31,7 +31,7 @@ import PassepartoutKit
public actor FallbackReceiptReader: AppReceiptReader { public actor FallbackReceiptReader: AppReceiptReader {
private let reader: InAppReceiptReader? private let reader: InAppReceiptReader?
private let localReader: (URL) -> InAppReceiptReader? private nonisolated let localReader: (URL) -> InAppReceiptReader?
private var pendingTask: Task<InAppReceipt?, Never>? private var pendingTask: Task<InAppReceipt?, Never>?

View File

@ -34,7 +34,7 @@ public actor MockAppProductHelper: AppProductHelper {
public nonisolated let receiptReader: MockAppReceiptReader public nonisolated let receiptReader: MockAppReceiptReader
private let didUpdateSubject: PassthroughSubject<Void, Never> private nonisolated let didUpdateSubject: PassthroughSubject<Void, Never>
// set .max to skip entitled products // set .max to skip entitled products
public init(build: Int = .max) { public init(build: Int = .max) {

View File

@ -46,17 +46,17 @@ public actor CoreDataRepository<CD, T>: NSObject,
private let entityName: String private let entityName: String
private let context: NSManagedObjectContext private nonisolated let context: NSManagedObjectContext
private let observingResults: Bool 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<EntitiesResult<T>, Never> private nonisolated let entitiesSubject: CurrentValueSubject<EntitiesResult<T>, Never>
// cannot easily use CD as generic // cannot easily use CD as generic
private var resultsController: NSFetchedResultsController<CD> private var resultsController: NSFetchedResultsController<CD>

View File

@ -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 <http://www.gnu.org/licenses/>.
//
import Foundation
extension UUID: RawRepresentable {
public var rawValue: String {
uuidString
}
public init?(rawValue: RawValue) {
self.init(uuidString: rawValue)
}
}

View File

@ -39,7 +39,7 @@ public final class StoreKitHelper<ProductType>: InAppHelper where ProductType: R
private var activeTransactions: Set<Transaction> private var activeTransactions: Set<Transaction>
private let didUpdateSubject: PassthroughSubject<Void, Never> private nonisolated let didUpdateSubject: PassthroughSubject<Void, Never>
private var observer: Task<Void, Never>? private var observer: Task<Void, Never>?

View File

@ -55,7 +55,7 @@ extension AppError: LocalizedError {
// MARK: - App side // MARK: - App side
extension PassepartoutError: LocalizedError { extension PassepartoutError: @retroactive LocalizedError {
public var errorDescription: String? { public var errorDescription: String? {
switch code { switch code {
case .App.ineligibleProfile: case .App.ineligibleProfile:

View File

@ -144,7 +144,7 @@ extension OnDemandModule.Policy: LocalizableEntity {
} }
} }
extension ProviderID: CustomDebugStringConvertible { extension ProviderID: @retroactive CustomDebugStringConvertible {
public var debugDescription: String { public var debugDescription: String {
rawValue rawValue
} }

View File

@ -209,18 +209,24 @@ private extension OpenVPNCredentialsView {
} }
#Preview { #Preview {
struct ContentView: View {
@State @State
var credentials: OpenVPN.Credentials? private var credentials: OpenVPN.Credentials?
@State @State
var isInteractive = true private var isInteractive = true
return NavigationStack { var body: some View {
NavigationStack {
OpenVPNCredentialsView( OpenVPNCredentialsView(
isInteractive: $isInteractive, isInteractive: $isInteractive,
credentials: $credentials credentials: $credentials
) )
.withMockEnvironment()
} }
}
}
return ContentView()
.withMockEnvironment()
} }

View File

@ -238,10 +238,13 @@ private extension EditableListSection {
} }
#Preview { #Preview {
@State struct ContentView: View {
var originalItems = ["One", "Two", "Three"]
return Form { @State
private var originalItems = ["One", "Two", "Three"]
var body: some View {
Form {
EditableListSection( EditableListSection(
"Title", "Title",
addTitle: "Add item", addTitle: "Add item",
@ -258,6 +261,10 @@ private extension EditableListSection {
Image(systemName: "arrow.up.arrow.down") Image(systemName: "arrow.up.arrow.down")
} }
} }
}
}
return ContentView()
} }
#endif #endif