Changelog
All notable changes to this project are documented here. The format is based on
Keep a Changelog, and this project adheres to
Semantic Versioning.
8.0.0 — unreleased
A major, fully breaking release. The library was modernized from the callback-based API to
async/await + Result, Swift 6 strict concurrency, typed request bodies, a categorized error
model, a structured event stream, and composable request interceptors.
Requirements: Swift 6.2+, and iOS 18 / macOS 15 / tvOS 18 / watchOS 11.
Added
- Async API. Every verb —
get/post/put/patch/delete— isasyncand returns
Result<T, NetworkingError>, whereTis anyDecodable,Data,Void, orJSONResponse
(status code + headers + body) when you want the response metadata. - Typed request bodies (no
Any).post/put/patchtakebody:(anyEncodable→ JSON),
form:(any flatEncodable→ url-encoded),parts:+fields:(multipart), or
data:contentType:(raw).get/deletetakequery:(a[URLQueryItem]or any flatEncodable). - Categorized errors.
NetworkingErroris nowinvalidRequest/transport/http/
decoding/validation/invalidResponse/cancelled, each preserving the underlying cause.
HTTPErrorcarriesstatusCode/metadata; cross-cutting conveniencesstatusCode,
responseMetadata, and a conservativeisRetryable. The core makes no assumption about the error
body's shape —ResponseMetadataretains the fullbody: Datawith adecode(_:)convenience
(and a truncatedbodySnippetfor logs), so you decode your API's own error envelope into a typed
value. - Event stream.
events()returns anAsyncStream<NetworkingEvent>emitting.startedthen
.completedfor every request (verbs and downloads) — multi-consumer, carrying the outcome,
duration, andURLSessionTaskMetrics. - Built-in logging.
logLevel(.none/.failures(default) /.all), release-safe
redaction (redactsLogs,setRedactedHeaderFields), and an optional plain-text file sink
(setLogFileURL/ theNETWORKING_LOG_FILEenv var) on top ofos.Logger. - Request interceptors.
HTTPInterceptor— an asyncintercept(_:next:)seam registered via
setInterceptors, wrapping verbs and downloads. Built-ins:AuthRefreshInterceptor(401/403 →
refresh + replay once, concurrent refreshes deduped),RetryInterceptor(exponential backoff +
jitter,Retry-After, idempotent methods only by default), andResponseValidatorInterceptor
(reject a 2xx that isn't acceptable →.validation). - Download envelopes.
downloadImage/downloadDatacan returnImageResponse/DataResponse
(payload + status code + headers) in addition to the bareImage/Data. - Cache lifecycle.
clearCache()empties both the in-memory and on-disk tiers (consistent
cleanup); on-disk entries now expire with a sliding TTL (cacheTTL, default 7 days, set via
init(…, cacheTTL:)/setCacheTTL(_:)) whose clock is last use, so a long-lived cache no longer
grows without bound. Long URLs no longer fail to cache — the filename is hashed past the filesystem's
255-byte limit.
Changed
Networkingis now anactor, so concurrent use is compiler-checked. Configuration is via
async setters (setAuthorizationHeader,setHeaderFields,setInterceptors, …); the type can no
longer be subclassed.- Fakes take
Encodable(fakeGET/fakePOST/ …), with a no-body overload and the existing
fileName:variant — no moreAny. - Minimum platforms raised to iOS 18 / macOS 15 / tvOS 18 / watchOS 11; Swift 6.2 language mode.
- HTTP status-code classification moved off
Intonto the type it describes:
Networking.StatusCodeType(statusCode:)replaces theInt.statusCodeTypeproperty. composedURL(with:)now throws a typedNetworkingError.invalidRequest(.invalidURL)instead of an
untypedNSErrorwhen a URL can't be built.
Removed
- The callback-based API and the
old*verbs, along withJSONResult/NetworkingResult/
Response. parameters: Any?andParameterType— replaced by the typed body/query methods above.print-based error logging:isErrorLoggingEnabled/setErrorLoggingEnabled— replaced by
events()andlogLevel.unauthorizedRequestCallback/setUnauthorizedRequestCallback— replaced by
AuthRefreshInterceptor(pure "notify me on 401" is available onevents()).- The static
Networking.deleteCachedFiles()— replaced by the instanceclearCache(), which clears
both the in-memory and on-disk tiers (the static cleared only disk, leaving memory serving stale data). - The public
Int.statusCodeTypeextension (it polluted every consumer'sInt) — use
Networking.StatusCodeType(statusCode:).
Versions 7.0.0 and earlier predate this changelog; see the
GitHub releases and git history.