SwiftMesh AI Skill
A comprehensive AI reference for using SwiftMesh — an Alamofire + Codable wrapper with async/await, Combine, fluent configuration, file upload/download, JSON key path parsing, resilient Codable wrappers, and built-in logging.
Quick Reference Card
| Feature | Method | Description |
|---------|--------|-------------|
| GET Request | .request(of: Model.self) | Decode response to Codable model |
| Key Path | .request(of: Model.self, modelKeyPath: "data.user") | Extract nested JSON |
| File Upload | .upload(of: Result.self) | Upload file/Data/stream/multipart |
| File Download | .download() | Download or resume download |
| Raw Data | .requestData() | Get raw response Data |
| Raw String | .requestString() | Get response as String |
| Retry Policy | .setInterceptor(RetryPolicy()) | Auto-retry with backoff |
| Logging | Mesh.enableLog() | Enable network logging |
Core Architecture
SwiftMesh uses a builder pattern on the Mesh class. Every configuration method returns Self, enabling fluent chaining. The flow is:
Configure (Mesh + Config) → Execute (Request/Upload/Download) → Handle (Handle)
File Structure
| File | Purpose |
|------|---------|
| Mesh.swift | Core builder class with all properties |
| Config.swift | Fluent chainable setters + global config |
| Request.swift | async/await request execution |
| Handle.swift | URL construction, error handling, response processing, RetryPolicy |
| Upload.swift | File upload (file, data, stream, multipart) |
| Download.swift | File download (standard, resumable) |
| KeyPath.swift | JSON key path decoder for nested extraction |
| Codable+.swift | Resilient property wrappers (@Default, @IgnoreError, @ConvertTo*) |
| Log.swift | Network logger (cURL, status, timing, JSON) |
Usage Patterns
1. Basic GET Request
let result = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://api.example.com")
.setUrlPath("/weather/city/101030100")
.request(of: Weather.self)
2. GET with JSON Key Path Extraction
Extract only a nested portion of the JSON response without parsing the entire structure:
// JSON: { "code": 200, "data": { "yesterday": { "temp": 25, "notice": "Sunny" } } }
let yesterday = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://api.example.com")
.setUrlPath("/weather")
.request(of: Forecast.self, modelKeyPath: "data.yesterday")
3. POST Request with Parameters
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/login")
.setParameters(["username": "admin", "password": "123456"])
.request(of: LoginResult.self)
4. POST with JSON Encoding
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/api/data")
.setRequestEncoding(JSONEncoding.default)
.setParameters(["key": "value"])
.request(of: Response.self)
5. Request with Custom Headers
let result = try await Mesh()
.setUrlHost("https://api.example.com")
.setUrlPath("/secure/data")
.setHeads(["Authorization": "Bearer token123"])
.request(of: SecureData.self)
6. Request with Retry Policy
let result = try await Mesh()
.setUrlHost("https://api.example.com")
.setUrlPath("/unstable-api")
.setInterceptor(RetryPolicy(maxRetryCount: 3))
.request(of: Data.self)
7. URLRequest-based Request
When you already have a URLRequestConvertible:
let urlRequest = try URLRequest(url: URL(string: "https://api.example.com/data")!, method: .get)
let result = try await Mesh()
.urlRequest(urlRequest, type: Response.self)
8. Raw Data Response
let data = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://api.example.com")
.setUrlPath("/raw")
.requestData()
9. Raw String Response
let string = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://api.example.com")
.setUrlPath("/text")
.requestString()
10. File Download
let fileURL = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://example.com")
.setUrlPath("/files/document.pdf")
.setDestination { _, _ in
let dest = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
.appendingPathComponent("document.pdf")
return (dest, [.removePreviousFile, .createIntermediateDirectories])
}
.download()
11. Resumable Download
let fileURL = try await Mesh()
.setUrlHost("https://example.com")
.setUrlPath("/files/large-file.zip")
.setDownloadType(.resume)
.setResumeData(savedResumeData) // from previous interrupted download
.download()
12. Single File Upload (URL)
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/upload")
.setUploadType(.file)
.setFileURL(fileURL)
.upload(of: UploadResult.self)
13. Single File Upload (Data)
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/upload")
.setUploadType(.data)
.setFileData(imageData)
.upload(of: UploadResult.self)
14. Multipart Form Upload
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/upload/multi")
.setUploadType(.multipart)
.setAddformData(name: "file",
fileName: "photo.jpg",
fileData: imageData,
mimeType: "image/jpeg")
.setAddformData(name: "description",
fileData: "My photo".data(using: .utf8))
.upload(of: UploadResult.self)
15. Multipart with Pre-built UploadDatas
let uploads = [
MultipleUpload.formData(name: "file1", fileName: "a.jpg", fileData: data1, mimeType: "image/jpeg"),
MultipleUpload.formData(name: "file2", fileName: "b.pdf", fileURL: fileURL)
]
let result = try await Mesh()
.setRequestMethod(.post)
.setUrlHost("https://api.example.com")
.setUrlPath("/upload/batch")
.setUploadType(.multipart)
.setUploadDatas(uploads)
.setParameters(["userId": "123"]) // additional form fields
.upload(of: BatchResult.self)
16. Custom Timeout
let result = try await Mesh()
.setUrlHost("https://api.example.com")
.setUrlPath("/slow-api")
.setTimeout(60) // 60 seconds
.request(of: Response.self)
Global Configuration
Set these once at app launch (e.g., in AppDelegate):
// Enable network logging
Mesh.enableLog(.log) // or .print
// Set global default headers
Mesh.setHeaders(["Authorization": "Bearer token", "App-Version": "1.0"])
// Set global default parameters
Mesh.setParameters(["platform": "ios", "sdk_version": "2.0"])
// Set global URL host
Mesh.setUrlHost("https://api.example.com")
Then per-request configuration only needs the path:
// Uses global headers, parameters, and urlHost
let result = try await Mesh()
.setUrlPath("/weather")
.request(of: Weather.self)
Resilient Codable Property Wrappers
Handle inconsistent API responses gracefully without decoding failures.
@Default Wrappers
Provide fallback values when fields are missing or invalid:
struct Response: Codable {
@Default.True var isEnabled: Bool // Missing → true
@Default.False var isDeleted: Bool // Missing → false
@Default.EmptyString var name: String // Missing → ""
@Default.EmptyInt var count: Int // Missing → 0
@Default.EmptyArray var tags: [String] // Missing → []
@Default.EmptyDictionary var meta: [String: Int] // Missing → [:]
@Default.Now var createdAt: Date // Missing → Date()
}
@IgnoreError
Returns nil instead of throwing when a field fails to decode:
struct Response: Codable {
@IgnoreError var description: String? // Invalid type → nil (no crash)
@IgnoreError var nested: NestedModel? // Missing/malformed → nil
}
@ConvertToString
Accepts String, Int, or Double from JSON → converts to String?:
struct Response: Codable {
@ConvertToString var version: String? // "1.0", 1, or 1.0 → "1.0", "1", "1.0"
}
@ConvertToInt
Accepts Int, String, or Double from JSON → converts to Int?:
struct Response: Codable {
@ConvertToInt var count: Int? // 42, "42", or 42.9 → 42, 42, 42
}
@ConvertToDouble
Accepts Double, Int, Float, or String from JSON → converts to Double?:
struct Response: Codable {
@ConvertToDouble var price: Double? // 9.99, 10, "9.99" → 9.99, 10.0, 9.99
}
@ConvertToFloat
Accepts Float, Int, Double, or String from JSON → converts to Float?:
struct Response: Codable {
@ConvertToFloat var rating: Float? // 4.5, 5, "4.5" → 4.5, 5.0, 4.5
}
JSON Key Path Decoder
Extract nested JSON values without parsing the entire response:
// JSON: { "code": 200, "data": { "list": [{ "id": 1 }, { "id": 2 }] } }
// Extract a single nested object
let firstItem = try await Mesh()
.setRequestMethod(.get)
.setUrlPath("/items")
.request(of: Item.self, modelKeyPath: "data.list.0")
// The KeyPath decoder also works directly:
let decoder = JSONDecoder.default
let item = try decoder.decode(Item.self, from: jsonData, keyPath: "data.list.0")
let items = try decoder.decodeArray([Item].self, from: jsonData, keyPath: "data.list")
The default decoder auto-configures:
keyDecodingStrategy = .convertFromSnakeCase(snake_case → camelCase)dateDecodingStrategy = .iso8601
Combine + SwiftUI Integration
ObservableObject Pattern
class RequestModel: ObservableObject {
@MainActor @Published var weather: Weather?
@MainActor @Published var errorMessage: String?
func fetchWeather() {
Task {
do {
let result = try await Mesh()
.setRequestMethod(.get)
.setUrlHost("https://api.example.com")
.setUrlPath("/weather")
.request(of: Weather.self)
await MainActor.run { self.weather = result }
} catch {
await MainActor.run { self.errorMessage = error.localizedDescription }
}
}
}
}
SwiftUI Usage
struct WeatherView: View {
@StateObject private var model = RequestModel()
var body: some View {
VStack {
if let weather = model.weather {
Text("\(weather.temperature)°C")
}
if let error = model.errorMessage {
Text(error).foregroundColor(.red)
}
}
.onAppear { model.fetchWeather() }
}
}
UIKit + Combine Usage
class ViewController: UIViewController {
private var model = RequestModel()
private var cancellables: Set<AnyCancellable> = []
override func viewDidLoad() {
super.viewDidLoad()
model.$weather
.receive(on: RunLoop.main)
.sink { weather in
print("Weather: \(String(describing: weather))")
}
.store(in: &cancellables)
model.fetchWeather()
}
}
Error Handling
SwiftMesh normalizes common network errors into user-friendly NSError:
| Error Code | Description |
|------------|-------------|
| NSURLErrorNotConnectedToInternet | No internet connection |
| NSURLErrorTimedOut | Request timeout |
| NSURLErrorCannotFindHost | Host not found |
| NSURLErrorCannotConnectToHost | Cannot connect to host |
| NSURLErrorNetworkConnectionLost | Connection lost during request |
All normalized errors return the message: "Unable to connect to the server"
Catch errors in your async code:
do {
let result = try await Mesh()
.setUrlPath("/api")
.request(of: Response.self)
} catch let error as NSError {
print("Error \(error.code): \(error.localizedDescription)")
}
RetryPolicy
Built-in retry with linear backoff (1s, 2s, 3s...):
// Default: 3 retries
let policy = RetryPolicy()
// Custom: 5 retries
let policy = RetryPolicy(maxRetryCount: 5)
// Use with request
let result = try await Mesh()
.setUrlPath("/unstable-api")
.setInterceptor(policy)
.request(of: Response.self)
Logging
Enable at app launch:
// Using Apple's unified Logger (os.log)
Mesh.enableLog(.log)
// Using Swift print()
Mesh.enableLog(.print)
Output includes:
- cURL command (for easy reproduction)
- HTTP status code
- Elapsed time
- Pretty-printed JSON response
Configuration Method Reference
All methods are chainable (return Self):
Basic Configuration
| Method | Type | Description |
|--------|------|-------------|
| .setTimeout(_:) | TimeInterval | Request timeout in seconds (default: 15) |
| .setInterceptor(_:) | RequestInterceptor? | Retry policy / credential handler |
| .setRequestMethod(_:) | HTTPMethod | GET, POST, PUT, DELETE, etc. |
| .setHeads(_:) | [String: String] | Per-request headers |
| .setRequestEncoding(_:) | ParameterEncoding | URLEncoding, JSONEncoding, etc. |
| .setUrlHost(_:) | String? | Per-request URL host |
| .setUrlPath(_:) | String? | URL path |
| .setParameters(_:) | [String: Any]? | Request parameters |
Download Configuration
| Method | Type | Description |
|--------|------|-------------|
| .setDownloadType(_:) | DownloadType | .download or .resume |
| .setDestination(_:) | DownloadRequest.Destination | File save location |
| .setResumeData(_:) | Data? | Resume data from interrupted download |
Upload Configuration
| Method | Type | Description |
|--------|------|-------------|
| .setUploadType(_:) | UploadType | .file, .data, .stream, .multipart |
| .setFileURL(_:) | URL? | File URL for upload |
| .setFileData(_:) | Data? | File data for upload |
| .setStream(_:) | InputStream? | Input stream for upload |
| .setUploadDatas(_:) | [MultipleUpload] | Multipart form entries |
| .setAddformData(name:fileName:fileData:fileURL:mimeType:) | Multiple | Quick add form field |
Global Static Methods
| Method | Description |
|--------|-------------|
| Mesh.enableLog(_:) | Enable network logging |
| Mesh.setHeaders(_:) | Set global default headers |
| Mesh.setParameters(_:) | Set global default parameters |
| Mesh.setUrlHost(_:) | Set global default URL host |
Enums Reference
DownloadType
| Case | Description |
|------|-------------|
| .download | Standard download |
| .resume | Resumable download (requires resumeData) |
UploadType
| Case | Description |
|------|-------------|
| .file | Upload from file URL |
| .data | Upload from Data object |
| .stream | Upload from InputStream |
| .multipart | Multipart form upload |
LogType
| Case | Description |
|------|-------------|
| .print | Output via Swift print() |
| .log | Output via Apple os.log |
AI Prompt Templates
When using an AI coding assistant, use these templates:
"Make a GET request"
Use SwiftMesh to make a GET request to {URL} and decode the response into a {Model} struct. Use key path "{keyPath}" to extract nested data.
"Upload a file"
Use SwiftMesh to upload a {file/data/stream} to {URL} with multipart form field name "{name}" and MIME type "{mimeType}". Decode the response as {Model}.
"Download a file"
Use SwiftMesh to download a file from {URL} and save it to {destination}. Use resumable download if needed.
"Handle inconsistent API types"
Create a Codable struct for this JSON: {json}. Use @Default, @IgnoreError, and @ConvertTo* wrappers to handle missing or inconsistent types.
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.