diff --git a/packages/swift-client/README.md b/packages/swift-client/README.md new file mode 100644 index 0000000..a32827c --- /dev/null +++ b/packages/swift-client/README.md @@ -0,0 +1,26 @@ +# Crate Swift client + +The `CrateClient` Swift package contains Apple's generated OpenAPI client and +typed `Codable` request/response models. It targets iOS 16+ and macOS 13+. + +```swift +import CrateClient +import Foundation + +let client = try makeCrateClient(serverURL: URL(string: "http://127.0.0.1:3030")!) +let response = try await client.listFiles() +``` + +This client speaks to the optional Crate web backend. Native applications that +access a PDS directly must implement ATProto OAuth and the conventions in +`packages/protocol/README.md`; the generated HTTP client is not a native OAuth SDK. +The app owns its URLSession and authentication lifecycle. Do not copy web cookies +into another app as an authentication strategy. +The factory validates the server URL and sends its canonical `Origin` on mutations +to satisfy the backend's CSRF check. Creating the generated `Client` directly +requires equivalent middleware; a cookie alone does not authorize mutation requests. + +`Sources/CrateClient/GeneratedSources` and its sibling `openapi.json` are generated +from the Rust server. Run root `bun run generate:api`, then `swift test +--package-path packages/swift-client`. The generator/runtime/transport versions +are pinned and dependency resolution is checked in. diff --git a/packages/swift-client/Tests/CrateClientTests/ContractTests.swift b/packages/swift-client/Tests/CrateClientTests/ContractTests.swift new file mode 100644 index 0000000..d997342 --- /dev/null +++ b/packages/swift-client/Tests/CrateClientTests/ContractTests.swift @@ -0,0 +1,23 @@ +import Foundation +import Testing +@testable import CrateClient + +@Test func fileIdentityAndRevisionRemainDistinct() throws { + let data = Data(""" + {"id":"3mabcdef12345","uri":"at://did:plc:example/space/network.crate.drive/self/did:plc:example/network.crate.file/3mabcdef12345",\ + "revision":"record-cid","name":"Documents","kind":"directory","createdAt":"2026-09-05T12:00:00Z",\ + "updatedAt":"2026-09-05T12:00:00Z","size":0} + """.utf8) + let file = try JSONDecoder().decode(Components.Schemas.FileEntry.self, from: data) + #expect(file.id == "3mabcdef12345") + #expect(file.revision == "record-cid") + #expect(file.parentId == nil) + #expect(file.blob == nil) +} + +@Test func malformedResponseCannotDecodeAsFile() { + let data = Data(#"{"id":42,"kind":"anything"}"#.utf8) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(Components.Schemas.FileEntry.self, from: data) + } +}