Skip to content

Commit 107098a

Browse files
authored
feat: Add backwards compatability to Swift 5.10 (#221)
* feat: Add backwards compatability to Swift 5.10 * Update Xcode version in CI workflow * Code review nits * Change iOS Simulator destination in CI workflow Updated the iOS Simulator destination from iPhone 16 Pro Max to iPhone 15 Pro Max in the CI workflow. * Add extra sendables * code review nits
1 parent 26fbee4 commit 107098a

10 files changed

Lines changed: 93 additions & 35 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
branches: [ main ]
99

1010
env:
11-
CI_XCODE_OLDEST: '/Applications/Xcode_16.1.app/Contents/Developer'
11+
CI_XCODE_OLDEST: '/Applications/Xcode_15.4.app/Contents/Developer'
1212
CI_XCODE_LATEST: '/Applications/Xcode_26.1.app/Contents/Developer'
1313

1414
concurrency:
@@ -109,7 +109,7 @@ jobs:
109109
steps:
110110
- uses: actions/checkout@v6
111111
- name: Build-Test
112-
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -destination platform\=iOS\ Simulator,name\=iPhone\ 16\ Pro\ Max -derivedDataPath DerivedData test 2>&1 | xcbeautify --renderer github-actions
112+
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -destination platform\=iOS\ Simulator,name\=iPhone\ 15\ Pro\ Max -derivedDataPath DerivedData test 2>&1 | xcbeautify --renderer github-actions
113113
env:
114114
DEVELOPER_DIR: ${{ env.CI_XCODE_OLDEST }}
115115
- name: Prepare codecov

Package.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
// swift-tools-version:6.0
1+
// swift-tools-version:5.10
22

33
import PackageDescription
44

55
let sharedSwiftSettings: [SwiftSetting] = [
6+
// Only necessary in Swift 5.10+
7+
.enableUpcomingFeature("ConciseMagicFile"),
8+
.enableUpcomingFeature("BareSlashRegexLiterals"),
9+
.enableUpcomingFeature("DeprecateApplicationMain"),
10+
.enableUpcomingFeature("DisableOutwardActorInference"),
11+
.enableUpcomingFeature("DynamicActorIsolation"),
12+
.enableUpcomingFeature("GlobalActorIsolatedTypesUsability"),
13+
.enableUpcomingFeature("ForwardTrailingClosures"),
14+
.enableUpcomingFeature("ImportObjcForwardDeclarations"),
15+
.enableUpcomingFeature("ImplicitOpenExistentials"),
16+
.enableUpcomingFeature("InferSendableFromCaptures"),
17+
.enableUpcomingFeature("IsolatedDefaultValues"),
18+
.enableUpcomingFeature("RegionBasedIsolation"),
19+
// Optional features in Swift 6+
620
.enableUpcomingFeature("MemberImportVisibility"),
721
.enableUpcomingFeature("InferIsolatedConformances"),
8-
.enableUpcomingFeature("ImmutableWeakCaptures")
22+
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
23+
.enableUpcomingFeature("ImmutableWeakCaptures"),
24+
.enableExperimentalFeature("StrictConcurrency=complete")
925
]
1026

1127
let package = Package(

ParseSwift.xcodeproj/project.pbxproj

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,9 +1936,22 @@
19361936
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
19371937
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
19381938
SWIFT_STRICT_CONCURRENCY = complete;
1939+
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
1940+
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
1941+
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
1942+
SWIFT_UPCOMING_FEATURE_DYNAMIC_ACTOR_ISOLATION = YES;
1943+
SWIFT_UPCOMING_FEATURE_FORWARD_TRAILING_CLOSURES = YES;
1944+
SWIFT_UPCOMING_FEATURE_GLOBAL_ACTOR_ISOLATED_TYPES_USABILITY = YES;
1945+
SWIFT_UPCOMING_FEATURE_GLOBAL_CONCURRENCY = YES;
1946+
SWIFT_UPCOMING_FEATURE_IMPLICIT_OPEN_EXISTENTIALS = YES;
1947+
SWIFT_UPCOMING_FEATURE_IMPORT_OBJC_FORWARD_DECLS = YES;
19391948
SWIFT_UPCOMING_FEATURE_INFER_ISOLATED_CONFORMANCES = YES;
1949+
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
1950+
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
19401951
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
1941-
SWIFT_VERSION = 6.0;
1952+
SWIFT_UPCOMING_FEATURE_NONFROZEN_ENUM_EXHAUSTIVITY = YES;
1953+
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
1954+
SWIFT_VERSION = 5.0;
19421955
TVOS_DEPLOYMENT_TARGET = 13.0;
19431956
VERSIONING_SYSTEM = "apple-generic";
19441957
VERSION_INFO_PREFIX = "";
@@ -2004,9 +2017,22 @@
20042017
SWIFT_COMPILATION_MODE = wholemodule;
20052018
SWIFT_OPTIMIZATION_LEVEL = "-O";
20062019
SWIFT_STRICT_CONCURRENCY = complete;
2020+
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
2021+
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
2022+
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
2023+
SWIFT_UPCOMING_FEATURE_DYNAMIC_ACTOR_ISOLATION = YES;
2024+
SWIFT_UPCOMING_FEATURE_FORWARD_TRAILING_CLOSURES = YES;
2025+
SWIFT_UPCOMING_FEATURE_GLOBAL_ACTOR_ISOLATED_TYPES_USABILITY = YES;
2026+
SWIFT_UPCOMING_FEATURE_GLOBAL_CONCURRENCY = YES;
2027+
SWIFT_UPCOMING_FEATURE_IMPLICIT_OPEN_EXISTENTIALS = YES;
2028+
SWIFT_UPCOMING_FEATURE_IMPORT_OBJC_FORWARD_DECLS = YES;
20072029
SWIFT_UPCOMING_FEATURE_INFER_ISOLATED_CONFORMANCES = YES;
2030+
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
2031+
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
20082032
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
2009-
SWIFT_VERSION = 6.0;
2033+
SWIFT_UPCOMING_FEATURE_NONFROZEN_ENUM_EXHAUSTIVITY = YES;
2034+
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
2035+
SWIFT_VERSION = 5.0;
20102036
TVOS_DEPLOYMENT_TARGET = 13.0;
20112037
VALIDATE_PRODUCT = YES;
20122038
VERSIONING_SYSTEM = "apple-generic";
@@ -2046,7 +2072,7 @@
20462072
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
20472073
SWIFT_STRICT_CONCURRENCY = complete;
20482074
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
2049-
SWIFT_VERSION = 6.0;
2075+
SWIFT_VERSION = 5.0;
20502076
TARGETED_DEVICE_FAMILY = "1,2,3,4,6,7";
20512077
TVOS_DEPLOYMENT_TARGET = 13.0;
20522078
WATCHOS_DEPLOYMENT_TARGET = 6.0;
@@ -2083,7 +2109,7 @@
20832109
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
20842110
SWIFT_STRICT_CONCURRENCY = complete;
20852111
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
2086-
SWIFT_VERSION = 6.0;
2112+
SWIFT_VERSION = 5.0;
20872113
TARGETED_DEVICE_FAMILY = "1,2,3,4,6,7";
20882114
TVOS_DEPLOYMENT_TARGET = 13.0;
20892115
WATCHOS_DEPLOYMENT_TARGET = 6.0;

Sources/ParseSwift/API/API+Command.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal extension API {
3636
parseURL: URL? = nil,
3737
otherURL: URL? = nil,
3838
stream: InputStream? = nil,
39-
mapper: @escaping (@Sendable (Data) async throws -> U)) {
39+
mapper: (@escaping @Sendable (Data) async throws -> U)) {
4040
self.method = method
4141
self.path = path
4242
self.body = body

Sources/ParseSwift/API/API+NonParseBodyCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import FoundationNetworking
1313

1414
internal extension API {
1515
// MARK: API.NonParseBodyCommand
16-
struct NonParseBodyCommand<T, U>: Encodable where T: Encodable & Sendable, U: Sendable {
16+
struct NonParseBodyCommand<T, U>: Encodable, Sendable where T: Encodable & Sendable, U: Sendable {
1717
typealias ReturnType = U // swiftlint:disable:this nesting
1818
let method: API.Method
1919
let path: API.Endpoint
@@ -25,7 +25,7 @@ internal extension API {
2525
path: API.Endpoint,
2626
params: [String: String]? = nil,
2727
body: T? = nil,
28-
mapper: @escaping (@Sendable (Data) async throws -> U)) {
28+
mapper: (@escaping @Sendable (Data) async throws -> U)) {
2929
self.method = method
3030
self.path = path
3131
self.params = params

Sources/ParseSwift/API/API.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import FoundationNetworking
1414
// swiftlint:disable line_length
1515

1616
/// The REST API for communicating with a Parse Server.
17-
public struct API {
17+
public struct API: Sendable {
1818

1919
public enum Method: String, Encodable, Sendable {
2020
case GET, POST, PUT, PATCH, DELETE

Sources/ParseSwift/Extensions/AnyHashable.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@
77

88
import Foundation
99

10-
extension AnyHashable: @unchecked @retroactive Sendable {}
10+
#if compiler(>=6.0)
11+
extension AnyHashable: @retroactive @unchecked Sendable {}
12+
#else
13+
extension AnyHashable: @unchecked Sendable {}
14+
#endif

Sources/ParseSwift/Extensions/InputStream.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ import Foundation
1111
import FoundationNetworking
1212
#endif
1313

14-
extension InputStream: @unchecked @retroactive Sendable {}
14+
#if compiler(>=6.0)
15+
extension InputStream: @retroactive @unchecked Sendable {}
16+
#else
17+
extension InputStream: @unchecked Sendable {}
18+
#endif

Sources/ParseSwift/Extensions/JSONEncoder+ParseEncoder.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,9 @@ import Foundation
1111
// swiftlint:disable line_length
1212

1313
#if canImport(Darwin)
14+
#if compiler(>=6.0)
1415
extension JSONEncoder: @retroactive @unchecked Sendable {} // JSONEncoder Sendable conformance is not available before macOS 13.0/iOS 16.0/watchOS 9.0/tvOS 16.0 16.0
16+
#else
17+
extension JSONEncoder: @unchecked Sendable {} // JSONEncoder Sendable conformance is not available before macOS 13.0/iOS 16.0/watchOS 9.0/tvOS 16.0 16.0
18+
#endif
1519
#endif

Sources/ParseSwift/Extensions/URLSession.swift

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ internal extension URLSession {
3030
static let lock = NSLock()
3131

3232
// swiftlint:disable:next function_body_length cyclomatic_complexity
33-
func makeResult<U>(request: URLRequest,
34-
responseData: Data?,
35-
urlResponse: URLResponse?,
36-
responseError: Error?,
37-
mapper: @escaping (Data) async throws -> U) async -> Result<U, ParseError> {
33+
func makeResult<U: Sendable>(
34+
request: URLRequest,
35+
responseData: Data?,
36+
urlResponse: URLResponse?,
37+
responseError: Error?,
38+
mapper: (@escaping @Sendable (Data) async throws -> U)
39+
) async -> Result<U, ParseError> {
3840
if let responseError = responseError {
3941
let parseError = responseError as? ParseError ?? ParseError(message: "Unable to connect with parse-server",
4042
swift: responseError)
@@ -97,11 +99,13 @@ internal extension URLSession {
9799
message: "Unable to connect with parse-server: \(String(describing: urlResponse))."))
98100
}
99101

100-
func makeResult<U>(request: URLRequest,
101-
location: URL?,
102-
urlResponse: URLResponse?,
103-
responseError: Error?,
104-
mapper: @escaping (Data) async throws -> U) async -> Result<U, ParseError> {
102+
func makeResult<U: Sendable>(
103+
request: URLRequest,
104+
location: URL?,
105+
urlResponse: URLResponse?,
106+
responseError: Error?,
107+
mapper: (@escaping @Sendable (Data) async throws -> U)
108+
) async -> Result<U, ParseError> {
105109
guard let response = urlResponse else {
106110
let parseError = responseError as? ParseError ?? ParseError(code: .otherCause,
107111
message: "No response from server")
@@ -136,8 +140,8 @@ internal extension URLSession {
136140
callbackQueue: DispatchQueue,
137141
attempts: Int = 1,
138142
allowIntermediateResponses: Bool,
139-
mapper: @escaping @Sendable (Data) async throws -> U,
140-
completion: @escaping @Sendable (Result<U, ParseError>) -> Void
143+
mapper: (@escaping @Sendable (Data) async throws -> U),
144+
completion: (@escaping @Sendable (Result<U, ParseError>) -> Void)
141145
) async {
142146
do {
143147
let (responseData, urlResponse) = try await dataTask(for: request)
@@ -277,14 +281,14 @@ internal extension URLSession {
277281
}
278282

279283
internal extension URLSession {
280-
func uploadTask<U>( // swiftlint:disable:this function_body_length function_parameter_count
284+
func uploadTask<U: Sendable>( // swiftlint:disable:this function_body_length function_parameter_count
281285
notificationQueue: DispatchQueue,
282286
with request: URLRequest,
283287
from data: Data?,
284288
from file: URL?,
285289
progress: (@Sendable (URLSessionTask, Int64, Int64, Int64) -> Void)?,
286-
mapper: @escaping @Sendable (Data) async throws -> U,
287-
completion: @escaping @Sendable (Result<U, ParseError>) -> Void
290+
mapper: (@escaping @Sendable (Data) async throws -> U),
291+
completion: (@escaping @Sendable (Result<U, ParseError>) -> Void)
288292
) {
289293
var task: URLSessionTask?
290294
if let data = data {
@@ -345,12 +349,12 @@ internal extension URLSession {
345349
}
346350
}
347351

348-
func downloadTask<U>(
352+
func downloadTask<U: Sendable>(
349353
notificationQueue: DispatchQueue,
350354
with request: URLRequest,
351355
progress: (@Sendable (URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?,
352-
mapper: @escaping @Sendable (Data) async throws -> U,
353-
completion: @escaping @Sendable (Result<U, ParseError>) -> Void
356+
mapper: (@escaping @Sendable (Data) async throws -> U),
357+
completion: (@escaping @Sendable (Result<U, ParseError>) -> Void)
354358
) async {
355359
let task = downloadTask(with: request) { (location, urlResponse, responseError) in
356360
Task {
@@ -367,10 +371,10 @@ internal extension URLSession {
367371
task.resume()
368372
}
369373

370-
func downloadTask<U>(
374+
func downloadTask<U: Sendable>(
371375
with request: URLRequest,
372-
mapper: @escaping @Sendable (Data) async throws -> U,
373-
completion: @escaping @Sendable (Result<U, ParseError>) -> Void
376+
mapper: (@escaping @Sendable (Data) async throws -> U),
377+
completion: (@escaping @Sendable (Result<U, ParseError>) -> Void)
374378
) {
375379
Task {
376380
do {

0 commit comments

Comments
 (0)