this repo has no description
1# CoreATProtocol
2
3A Swift package providing the foundational networking layer for interacting with the [AT Protocol](https://atproto.com) (Authenticated Transfer Protocol). This library handles the core HTTP communication, authentication token management, and request/response encoding required to build AT Protocol clients.
4
5## Overview
6
7CoreATProtocol is designed to be protocol-agnostic within the AT Protocol ecosystem. It provides the networking infrastructure that higher-level packages (like [bskyKit](https://tangled.org/@sparrowtek.com/bskyKit) for Bluesky) can build upon to implement specific lexicons.
8
9### Key Features
10
11- **Modern Swift Concurrency** - Built with Swift 6.2 using async/await and actors for thread-safe operations
12- **Global Actor Isolation** - Uses `@APActor` for consistent thread safety across all AT Protocol operations
13- **Flexible Network Routing** - Generic `NetworkRouter` that works with any endpoint conforming to `EndpointType`
14- **Automatic Token Management** - Built-in support for JWT access/refresh token handling with automatic retry on expiration
15- **Multiple Parameter Encodings** - URL, JSON, and combined encoding strategies for request parameters
16- **AT Protocol Error Handling** - Typed error responses matching AT Protocol error specifications
17- **Testable Architecture** - Protocol-based design allows easy mocking for unit tests
18
19## Requirements
20
21- Swift 6.2+
22- iOS 26.0+ / macOS 26.0+ / watchOS 26.0+ / tvOS 26.0+ / Mac Catalyst 26.0+
23
24## Installation
25
26### Swift Package Manager
27
28Add CoreATProtocol to your `Package.swift` dependencies:
29
30```swift
31dependencies: [
32 .package(url: "https://tangled.org/@sparrowtek.com/CoreATProtocol", branch: "main"),
33]
34```
35
36Then add it to your target dependencies:
37
38```swift
39.target(
40 name: "YourTarget",
41 dependencies: ["CoreATProtocol"]
42),
43```
44
45Or in Xcode: File > Add Package Dependencies and enter:
46```
47https://tangled.org/@sparrowtek.com/CoreATProtocol
48```
49
50## Usage
51
52### Initial Setup
53
54Configure the environment with your host URL and authentication tokens:
55
56```swift
57import CoreATProtocol
58
59// Setup with host and tokens
60await setup(
61 hostURL: "https://bsky.social",
62 accessJWT: "your-access-token",
63 refreshJWT: "your-refresh-token"
64)
65
66// Or update tokens later
67await updateTokens(access: newAccessToken, refresh: newRefreshToken)
68
69// Change host
70await update(hostURL: "https://different-pds.example")
71```
72
73### Defining Endpoints
74
75Create endpoints by conforming to `EndpointType`:
76
77```swift
78import CoreATProtocol
79
80enum MyEndpoint: EndpointType {
81 case getProfile(actor: String)
82 case createPost(text: String)
83
84 var baseURL: URL {
85 get async {
86 URL(string: APEnvironment.current.host ?? "https://bsky.social")!
87 }
88 }
89
90 var path: String {
91 switch self {
92 case .getProfile:
93 return "/xrpc/app.bsky.actor.getProfile"
94 case .createPost:
95 return "/xrpc/com.atproto.repo.createRecord"
96 }
97 }
98
99 var httpMethod: HTTPMethod {
100 switch self {
101 case .getProfile: return .get
102 case .createPost: return .post
103 }
104 }
105
106 var task: HTTPTask {
107 get async {
108 switch self {
109 case .getProfile(let actor):
110 return .requestParameters(encoding: .urlEncoding(parameters: ["actor": actor]))
111 case .createPost(let text):
112 let body: [String: Any] = ["text": text]
113 return .requestParameters(encoding: .jsonEncoding(parameters: body))
114 }
115 }
116 }
117
118 var headers: HTTPHeaders? {
119 get async { nil }
120 }
121}
122```
123
124### Making Requests
125
126Use `NetworkRouter` to execute requests:
127
128```swift
129@APActor
130class MyATClient {
131 private let router = NetworkRouter<MyEndpoint>()
132
133 init() {
134 router.delegate = APEnvironment.current.routerDelegate
135 }
136
137 func getProfile(actor: String) async throws -> ProfileResponse {
138 try await router.execute(.getProfile(actor: actor))
139 }
140}
141```
142
143### Custom JSON Decoding
144
145Use the pre-configured AT Protocol decoder for proper date handling:
146
147```swift
148let router = NetworkRouter<MyEndpoint>(decoder: .atDecoder)
149```
150
151### Error Handling
152
153Handle AT Protocol specific errors:
154
155```swift
156do {
157 let profile: Profile = try await router.execute(.getProfile(actor: "did:plc:example"))
158} catch let error as AtError {
159 switch error {
160 case .message(let errorMessage):
161 print("AT Error: \(errorMessage.error) - \(errorMessage.message ?? "")")
162 case .network(let networkError):
163 switch networkError {
164 case .statusCode(let code, let data):
165 print("HTTP \(code?.rawValue ?? 0)")
166 case .encodingFailed:
167 print("Failed to encode request")
168 default:
169 print("Network error: \(networkError)")
170 }
171 }
172}
173```
174
175## Architecture
176
177### Core Components
178
179| Component | Description |
180|-----------|-------------|
181| `APActor` | Global actor ensuring thread-safe access to AT Protocol state |
182| `APEnvironment` | Singleton holding host URL, tokens, and delegates |
183| `NetworkRouter` | Generic router executing typed endpoint requests |
184| `EndpointType` | Protocol defining API endpoint requirements |
185| `ParameterEncoding` | Enum supporting URL, JSON, and hybrid encoding |
186| `AtError` | AT Protocol error types with message parsing |
187
188### Thread Safety
189
190All AT Protocol operations are isolated to `@APActor` ensuring thread-safe access:
191
192```swift
193@APActor
194public func myFunction() async {
195 // Safe access to APEnvironment.current
196}
197```
198
199## Parameter Encoding Options
200
201```swift
202// URL query parameters
203.urlEncoding(parameters: ["key": "value"])
204
205// JSON body
206.jsonEncoding(parameters: ["key": "value"])
207
208// Pre-encoded JSON data
209.jsonDataEncoding(data: jsonData)
210
211// Encodable objects
212.jsonEncodableEncoding(encodable: myStruct)
213
214// Combined URL + JSON body
215.urlAndJsonEncoding(urlParameters: ["q": "search"], bodyParameters: ["data": "value"])
216```
217
218## Related Packages
219
220- **[bskyKit](https://tangled.org/@sparrowtek.com/bskyKit)** - Bluesky-specific lexicon implementations built on CoreATProtocol
221
222## License
223
224This project is licensed under an [MIT license](https://tangled.org/sparrowtek.com/CoreATProtocol/blob/main/LICENSE).
225
226## Contributing
227
228It is always a good idea to discuss before taking on a significant task. That said, I have a strong bias towards enthusiasm. If you are excited about doing something, I'll do my best to get out of your way.
229
230By participating in this project you agree to abide by the [Contributor Code of Conduct](https://tangled.org/sparrowtek.com/CoreATProtocol/blob/main/CODE_OF_CONDUCT.md).