As expected with a major release, JWTDecode.swift v4 contains breaking changes. Please review this guide thoroughly to understand the changes required to migrate your application to v4.
All public types now conform to Sendable for full Swift 6 concurrency compatibility. This ensures safe usage across concurrency boundaries, actors, and async contexts.
The JWT protocol now requires Sendable conformance.
Before (v3):
public protocol JWT {
var header: [String: Any] { get }
var body: [String: Any] { get }
// ...
}After (v4):
public protocol JWT: Sendable {
var header: [String: any Sendable] { get }
var body: [String: any Sendable] { get }
// ...
}The Claim struct now conforms to Sendable and uses any Sendable for value types.
Before (v3):
public struct Claim {
let value: Any?
public var rawValue: Any? { /* ... */ }
}After (v4):
public struct Claim: Sendable {
let value: (any Sendable)?
public var rawValue: (any Sendable)? { /* ... */ }
}If you directly access header or body properties, update the type annotations:
Before (v3):
let header: [String: Any] = jwt.header
let body: [String: Any] = jwt.bodyAfter (v4):
let header: [String: any Sendable] = jwt.header
let body: [String: any Sendable] = jwt.bodyIf you directly access rawValue, update the type annotation:
Before (v3):
let customClaim: Any? = jwt["custom"].rawValueAfter (v4):
let customClaim: (any Sendable)? = jwt["custom"].rawValueFor most users, no changes are needed:
// These continue to work without changes
let jwt = try decode(jwt: token)
let email = jwt["email"].string
let userId = jwt["sub"].string
let exp = jwt.expiresAt
let isExpired = jwt.expiredA new data property is available for complex claims (arrays and dictionaries only):
// Returns Data for arrays and dictionaries
let data = jwt["custom_object"].data
// Returns nil for primitives (use .string, .integer, etc. instead)
let primitiveData = jwt["email"].data // nilDecode complex claims directly to Decodable types:
struct Address: Decodable {
let street: String
let city: String
}
// Decode a custom claim
let address = try jwt["address"].decode(Address.self)
// With custom decoder configuration
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let userInfo = try jwt["user_info"].decode(UserInfo.self, using: decoder)A new error case claimDecodingFailed has been added for claim decoding failures:
public enum JWTDecodeError: LocalizedError, CustomDebugStringConvertible, Sendable {
case invalidBase64URL(String)
case invalidJSON(String)
case invalidPartCount(String, Int)
case claimDecodingFailed(String) // New in v4
}This error is thrown by the Claim.decode<T: Decodable>() method when:
- A claim is not found or has a nil value
- A claim value is a primitive type when an object/array is expected (use
.string,.integer,.boolean, etc. instead) - Claim serialization to JSON data fails
Example:
do {
let address = try jwt["address"].decode(Address.self)
} catch JWTDecodeError.claimDecodingFailed(let message) {
print("Decoding failed: \(message)")
}