Skip to content

Migrate to Swift 6 language mode - #65

Open
LucijaUlaga wants to merge 2 commits into
masterfrom
swift-6-migration
Open

LucijaUlaga wants to merge 2 commits into
masterfrom
swift-6-migration

Conversation

@LucijaUlaga

@LucijaUlaga LucijaUlaga commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Builds every target in the Swift 6 language mode and resolves the strict concurrency diagnostics it surfaces. No async/await or actors are introduced — the library work is Sendable annotation only.

Breaking changes

  • responseJSONAPI completions now receive AFDataResponse<JSONAPIDocument> instead of AFDataResponse<Japx.Parameters>. Alamofire requires a response serializer's SerializedObject to be Sendable, which [String: Any] cannot be. JSONAPIDocument forwards subscripts, so response.value?["data"] is unchanged; .dictionary gives the whole thing. The .rx variants still emit Single<Japx.Parameters>.
  • responseCodableJSONAPI requires T: Decodable & Sendable. Structs and enums get this for free; non-final classes need their own conformance.
  • Minimum platforms raised to iOS 12 / macOS 10.13. This is toolchain-forced rather than concurrency-driven — under tools-version 6.0 SwiftPM deprecates .v10 / .v10_12. Nothing here needs an iOS 12 runtime.

README has a "Migrating to 5.0" section covering all three.

Notable choices

  • The four Alamofire wrappers are @preconcurrency, mirroring the attribute on Alamofire's own response(queue:responseSerializer:completionHandler:). Without it Japx is stricter than the API it wraps: a non-Sendable capture at a call site is a hard error through Japx but only a warning through responseDecodable.
  • JSONAPIDocument.init is internal — the serializer is the only producer, which is what keeps its @unchecked Sendable conformance sound.
  • JapxError keeps its Any payloads. The doc comment was narrowed instead: the no-mutation guarantee holds for the Data-based entry points, but for jsonObject(withJSONAPIObject:) / encode(json:) the payload aliases the caller's own collection.
  • Rx observers use nonisolated(unsafe), with a TODO to remove it once RxSwift annotates its observer closures.

Build every target in the Swift 6 language mode and resolve the strict
concurrency diagnostics it surfaces. No async/await or actors are
introduced; the library work is Sendable annotation only.

- Raise the floor to iOS 12 / macOS 10.13, the oldest versions the 6.0
  toolchain still accepts.
- Conform JapxEncoder, JapxDecoder and both Options types to Sendable,
  and document the precondition the two coder wrappers rely on.
- Box the parsed dictionary in JSONAPIDocument, since Alamofire requires
  a response serializer's SerializedObject to be Sendable and
  [String: Any] cannot be. This changes the type responseJSONAPI hands
  back; subscripting is forwarded, so reading a single key is unchanged.
  The initializer is internal to keep the unchecked conformance sound.
- Mark the four Alamofire wrappers @preconcurrency, matching the
  Alamofire API they wrap, so a non-Sendable capture at a call site
  stays a warning instead of becoming an error.
- Require T: Decodable & Sendable on the codable response helpers.
- Declare the RxSwift ReactiveCompatible conformances @retroactive, and
  use nonisolated(unsafe) for the observers, with a note to remove it
  once RxSwift annotates them.
- Isolate the example app's Resultable extension to @mainactor.
- Bump to 5.0.0. The response type change, the new Sendable requirement
  and the platform floor are all source-breaking.
@hhrvoic
hhrvoic requested review from Truba, hhrvoic and jcavar September 15, 2026 12:08
The podspec floor moved to iOS 12 with the Swift 6 migration, but the
example's Podfile still declared iOS 10, so a fresh `pod install` failed
resolution before installing anything:

    [!] CocoaPods could not find compatible versions for pod "Japx/ObjC":
        Specs satisfying the dependency were found, but they required a
        higher minimum deployment target.

An existing checkout kept working off its already-installed Pods, so this
only bit a clean clone — which is exactly what the README tells people to
do. Regenerates the lockfile on 5.0.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant