Releases: siamakrostami/Simorgh
2.0.0
# Simorgh 2.0.0
**Release date:** June 24, 2026
**Merged:** `feature/download-manager` → `main` (PR #5)
Simorgh 2.0.0 is the first release under the new name. The library formerly shipped as **SRNetworkManager**. One package now covers HTTP, WebSocket, subscriptions, background downloads, streaming, uploads, and live network/VPN monitoring — with Combine and async/await APIs and Swift 6 strict-concurrency support.
---
## Highlights
- **Rebrand to Simorgh** — new package/product name, module import, and example app
- **`DownloadManager`** — production download engine with pause/resume, priority queue, batch enqueue, speed/ETA, and background sessions
- **Live VPN detection** — dual `NWPathMonitor` architecture for instant connect/disconnect events
- **7-tab example app** — Posts, Realtime, Subscription, Downloads, Upload, Stream, Network
- **Complete README** — navigable table of contents and full API documentation
---
## Breaking Changes
### Package & module rename
| Before (1.x) | After (2.0) |
|---|---|
| `SRNetworkManager` | **`Simorgh`** |
| `import SRNetworkManager` | `import Simorgh` |
| `SRNetworkManagerTests` | `SimorghTests` |
| Repo/product: SRGenericNetworkLayer | **Simorgh** |
Update `Package.swift`:
```swift
dependencies: [
.package(url: "https://github.com/siamakrostami/Simorgh.git", from: "2.0.0")
]Example app rename
SRNetworkManagerExampleApp→SimorghExampleApp- Open
Example/SimorghExampleApp.xcodeproj
New Features
Download Manager
Built on URLSessionDownloadTask with Combine and async/await APIs.
- True pause/resume via resume data (persisted to disk)
- Priority queue:
critical>high>normal>low - Concurrency cap with automatic queue draining (default: 3)
- Batch enqueue — single call or per-item
fileName+priority - Speed & ETA — 3-second sliding window; auto-scaling display units (B/s → GB/s)
- HTTP validation — non-2xx responses rejected (error bodies not saved as files)
- Duplicate URL guard, disk-space guard, MIME auto-detection
- Percent-encoded filenames decoded automatically
- Background sessions via
backgroundSessionIdentifier - Structured download lifecycle logging (
ENQUEUED,STARTED,PAUSED,COMPLETED,RETRY, …)
let manager = try DownloadManager(config: .default, logLevel: .standard)
let id = try manager.enqueue(url: fileURL, priority: .high)
for await progress in try manager.download(url: fileURL) {
if progress.isCompleted { break }
}Network Monitoring (reachability rewrite)
- Dual
NWPathMonitor— general path + tunnel interface monitor - Instant VPN connect/disconnect detection (even when WiFi stays active)
- Eliminates
getifaddrs()race window - Combine (
monitor.status) and async/await (statusStream()) APIs
Example App
| Tab | Demonstrates |
|---|---|
| Posts | HTTP GET |
| Realtime | WebSocket |
| Subscription | Binance-style subscribe/unsubscribe |
| Downloads | Batch catalog, pause/resume, Quick Look |
| Upload | Multipart upload with progress |
| Stream | HTTP streaming (NDJSON) |
| Network | Live WiFi / Cellular / VPN status |
Documentation
- Collapsible table of contents in README
- Architecture diagram, API reference, production config guide
.github/ABOUT.mdfor GitHub repo description & topics
Improvements (from 1.1.0)
Already on main via PR #4; included in the full Simorgh 2.0.0 stack:
- WebSocket — full-duplex, auto-reconnect, typed messages, ping/pong
- Subscription protocol — Amplify/AppSync-style JSON handshake (Binance, Hasura, AppSync)
- Structured logging — HTTP, WebSocket, subscription, and download events at 4 log levels
- HTTP streaming —
streamRequest/asyncStreamRequest
Bug Fixes
- Download speed always 0 — delta now computed before
downloadedBytesis updated - MIME detector — fixed 12 dead cases, bounds crashes, and logic errors in
MimeTypeDetector - Example app scheme — fixed stale
SRNetworkManagerExampleAppreferences causing No Destinations in Xcode - Legacy cleanup — removed duplicate test targets and unused
SRNetworkManagerstub files
Requirements
- iOS 13+ · macOS 13+ · tvOS 13+ · watchOS 7+
- Swift 5.9+ (Swift 6 supported)
- Xcode 15+
Migration from SRNetworkManager 1.x
// 1. Update dependency URL and version
.package(url: "https://github.com/siamakrostami/Simorgh.git", from: "2.0.0")
// 2. Replace imports everywhere
- import SRNetworkManager
+ import Simorgh
// 3. API surface is unchanged for HTTP, WebSocket, and Subscription
let client = APIClient()
let users: [User] = try await client.request(GetUsersEndpoint())
// 4. Optional — add DownloadManager where needed
let downloads = try DownloadManager()No API changes to existing HTTP/WebSocket/Subscription calls beyond the module rename.
Installation
dependencies: [
.package(url: "https://github.com/siamakrostami/Simorgh.git", from: "2.0.0")
]Full Changelog (since 1.1.0)
feat: DownloadManager — pause/resume, priority queue, background sessionsfeat: batch enqueue, speed/ETA, download lifecycle loggingfeat: example app — Download, Stream, Network Monitor tabsfix: live VPN detection via dual NWPathMonitorfix: download speed calculation, HTTP status validation, percent-encoded filenamesfix: MimeTypeDetector crashes and logic errorschore: rename SRNetworkManager → Simorghdocs: complete README overhaul with TOC and API referencechore: gitignore local dev files (CLAUDE.md, RELEASE_NOTES.md, secrets)chore: fix example app Xcode scheme
Links
1.1.0
SRNetworkManager 1.1.0
What's New
WebSocket (complete rewrite)
The WebSocket layer has been rewritten from the ground up for correctness and reliability.
Key fixes:
.connectednow fires only after the server confirms the HTTP upgrade handshake (URLSessionWebSocketDelegate.urlSession(_:webSocketTask:didOpenWithProtocol:)), not optimistically onconnect()- Eliminated the URLSession retain cycle — a separate
_WebSocketDelegateclass holds aweakreference back toWebSocketConnection, allowing normaldeinit - Each
WebSocketConnectionowns its ownURLSession(created from the sameURLSessionConfigurationasAPIClient). Closing a WebSocket no longer affects in-flight HTTP requests close()is now safe to call during a reconnect delay — themanuallyClosedflag prevents the reconnect timer from overriding an explicit close- JSON payloads sent via
send<T: Encodable>are now encoded as UTF-8 text frames (.string), not binary frames
New APIs:
WebSocketConnectionStateenum:.idle,.connecting,.connected,.reconnecting(attempt:delay:),.disconnectedWebSocketConnection.stateproperty — readable at any timeWebSocketConnection.urlpropertyWebSocketConnection.reconnect()— resets the retry counter and reconnects immediatelyWebSocketOptions.reconnectPolicy— exponential backoff with configurableinitialDelay,multiplier,maximumDelay,maximumAttempts
Subscription Protocol (new)
Amplify-style subscribe/unsubscribe lifecycle over WebSocket. No GraphQL required — works with any JSON-based real-time API (Binance, Hasura, custom backends).
New protocol:
protocol SubscriptionRouter: WebSocketRouter {
associatedtype SubscribeMessage: Encodable & Sendable
associatedtype Event: Decodable & Sendable
var subscribeMessage: SubscribeMessage { get }
var unsubscribeMessage: SubscribeMessage? { get } // default: nil
func decodeEvent(from: WebSocketMessage, using: JSONDecoder) throws -> Event?
}The library handles the full lifecycle automatically:
- Connects the WebSocket
- Sends
subscribeMessageon every (re)connect - Decodes frames via
decodeEvent—nilreturn silently drops the frame (useful for acks) - Sends
unsubscribeMessagewhen stopped
Three consumer APIs — all with both async/await and Combine:
// Explicit lifecycle
let sub = try apiClient.subscription(MyRouter())
sub.connect()
for try await event in sub.events() { ... }
await sub.disconnect()
// Inline async/await (fire-and-forget)
for try await event in apiClient.subscribe(MyRouter()) { ... }
// Inline Combine (fire-and-forget)
apiClient.subscribe(MyRouter())
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
.store(in: &cancellables)Comprehensive Logging
All real-time layers now emit structured log output through URLSessionLogger, gated by the existing LogLevel on APIClient.
| Level | Coverage |
|---|---|
.minimal |
WebSocket URL on connect |
.standard |
+ connect/disconnect/reconnect events, SUBSCRIBE/UNSUBSCRIBE messages |
.verbose |
+ every sent/received WebSocket frame, every decoded stream chunk, every subscription event |
🔌🔌🔌 WEBSOCKET CONNECTED 🔌🔌🔌
🔈 wss://stream.example.com/ws
🔼🔼🔼 END 🔼🔼🔼
📡📡📡 SUBSCRIPTION SUBSCRIBE 📡📡📡
🔈 wss://stream.example.com/ws
Body: {"method":"SUBSCRIBE","params":["btcusdt@trade"],"id":1}
🔼🔼🔼 END 🔼🔼🔼
🔄🔄🔄 WEBSOCKET RECONNECTING 🔄🔄🔄
🔈 wss://stream.example.com/ws
💡 Attempt 1, delay: 1.0s
🔼🔼🔼 END 🔼🔼🔼
Migration Guide
WebSocket
No breaking API changes. If you were previously inspecting the .connected event timing, note that it now fires slightly later (after the actual server handshake) rather than immediately after connect().
Streaming
streamRequest and asyncStreamRequest are unchanged. Verbose logging now emits a 🌊 STREAM CHUNK line per decoded NDJSON line — no action needed unless you want to filter it.
Compatibility
- Swift 5 and Swift 6 (
swiftLanguageModes: [.v5, .v6]) - iOS 13+ · macOS 13+ · tvOS 13+ · watchOS 7+
1.0.29
fix(concurrency): add Sendable constraint to upload methods for Swift…
1.0.28
docs(readme): document configuration and cache policy updates
1.0.27
feat: Add comprehensive documentation and enhance library features - Add complete API documentation for all public interfaces - Implement comprehensive README with usage examples and best practices - Add thread safety documentation and implementation notes - Document all core components: APIClient, NetworkRouter, NetworkError - Add detailed documentation for RetryHandler and DefaultRetryHandler - Document SendablePromise with thread safety examples - Add APIVersion documentation with usage patterns - Document Data extensions with practical examples - Add comprehensive NetworkParameterEncoding documentation - Document HeaderHandler with type-safe header management
1.0.26
Fix(RetryHandler): Minor changes in RetryHandler Protocol
1.0.25
Fixed
- Swift 6 Compatibility: Added
Sendableconstraint to streaming methods to resolve data race warnings- Fixed "sending 'decodedObject' risks causing data races" errors in
StreamingSessionDelegate - All streaming methods now require decoded types to conform to both
CodableandSendable - Ensures thread-safe operation when passing decoded objects through continuations
- Fixed "sending 'decodedObject' risks causing data races" errors in
- Multi-Platform Support: Resolved Swift 6 strict concurrency build failures across all supported platforms:
- iOS 13.0+
- tvOS 13.0+
- watchOS 7.0+
- macOS 13.0+
Technical Details
- Updated
streamRequest<T>to requireT: Codable & Sendable - Updated
asyncStreamRequest<T>to requireT: Codable & Sendable - Modified
StreamingSessionDelegate<T>generic constraint toT: Codable & Sendable - No breaking changes for non-streaming API methods
1.0.24
🛡️ Thread Safety & Swift 6 Compatibility
Major Improvements
Swift 6 Ready: Full compatibility with Swift 6 concurrency model
Sendable Compliance: Properly implemented Sendable protocol across all network components
Race Condition Fixes: Eliminated potential data races in concurrent operations
Specific Enhancements
Added thread-safe property accessors throughout APIClient
Implemented queue-synchronized collections for requestsToRetry and activeSessions
Created proper barrier flags for write operations to ensure safety
Fixed StreamingSessionDelegate to safely handle concurrent data buffer access
Enhanced SendablePromise with dedicated synchronization queue
Added proper @unchecked Sendable handling with manual synchronization
Improved session tracking with thread-safe cleanup
Under the Hood
Standardized synchronization patterns with queue.sync
Optimized lock usage to prevent potential deadlocks
Reduced shared mutable state exposure
This version marks a significant improvement in stability for multi-threaded environments and prepares the framework for Swift 6's stricter concurrency requirements.
1.0.23
🛡️ Thread Safety & Swift 6 Compatibility
Major Improvements
- Swift 6 Ready: Full compatibility with Swift 6 concurrency model
- Sendable Compliance: Properly implemented Sendable protocol across all network components
- Race Condition Fixes: Eliminated potential data races in concurrent operations
Specific Enhancements
- Added thread-safe property accessors throughout APIClient
- Implemented queue-synchronized collections for requestsToRetry and activeSessions
- Created proper barrier flags for write operations to ensure safety
- Fixed StreamingSessionDelegate to safely handle concurrent data buffer access
- Enhanced SendablePromise with dedicated synchronization queue
- Added proper @unchecked Sendable handling with manual synchronization
- Improved session tracking with thread-safe cleanup
Under the Hood
- Standardized synchronization patterns with queue.sync
- Optimized lock usage to prevent potential deadlocks
- Reduced shared mutable state exposure
This version marks a significant improvement in stability for multi-threaded environments and prepares the framework for Swift 6's stricter concurrency requirements.