Amplify Clients are standalone AWS service clients independent of the core Amplify framework. Unlike category plugins (which require Amplify.configure() and the plugin system), Amplify Clients are self-contained libraries that can be used directly.
┌─────────────────────────────────────────────────────────┐
│ Amplify Clients (e.g., AmplifyKinesisClient) │
│ High-level APIs, actor-based, retry │
├─────────────────────────────────────────────────────────┤
│ AmplifyRecordCache (Shared Caching Layer) │
│ SQLite-backed record buffering, used by Kinesis and │
│ Firehose │
├─────────────────────────────────────────────────────────┤
│ AmplifyFoundation (Protocol Layer) │
│ Pure Swift protocols — zero external deps │
│ Credentials, Logging, Errors, Metadata │
├─────────────────────────────────────────────────────────┤
│ AmplifyFoundationBridge (Adapter Layer) │
│ Foundation ↔ AWS SDK type adapters │
│ Credential converters, User-Agent injection │
├─────────────────────────────────────────────────────────┤
│ AWS SDK (aws-sdk-swift) │
└─────────────────────────────────────────────────────────┘
Current rule: Clients depend on AmplifyFoundation + AmplifyFoundationBridge. They may eventually depend on types from Amplify core (e.g., error types), but should avoid depending on AWSPluginsCore or the plugin registration system.
Zero-dependency protocol layer providing:
Credentials — AWSCredentials, AWSTemporaryCredentials, AWSCredentialsProvider (async resolution)
Logging — Logger protocol with error/warn/info/debug/verbose levels. BroadcastLogger routes to multiple LogSinkBehavior sinks. AmplifyOSLogSink provides os.log integration.
Errors — AmplifyError protocol requiring errorDescription + recoverySuggestion + underlyingError
Metadata — AmplifyMetadata.version and .platformName
Design: zero framework coupling, no external deps, async/await first, Sendable everywhere.
Depends on: AmplifyFoundation, AWSClientRuntime
Credential adapters — Four bidirectional adapters:
FoundationToSDKCredentialsAdapter/SDKToFoundationCredentialsAdapter(Smithy)FoundationToCRTCredentialsAdapter/CRTToFoundationCredentialsAdapter(CRT)
Each conforms to both Foundation and SDK protocols simultaneously.
UserAgentClientEngine — HTTP client wrapper that injects lib/amplify-swift#<version> into User-Agent headers.
Location: AmplifyClients/AmplifyKinesisClient/
Deps: AmplifyFoundation, AmplifyFoundationBridge, SQLite.swift, AWSKinesis
public class AmplifyKinesisClient {
init(region: String, credentialsProvider: any AmplifyFoundation.AWSCredentialsProvider, options: Options = Options()) throws
@discardableResult func record(data: Data, partitionKey: String, streamName: String) async throws -> RecordData
func flush() async throws -> FlushData
func enable() async / func disable() async
func clearCache() async throws -> ClearCacheData
func getKinesisClient() -> AWSKinesis.KinesisClient // escape hatch
}public enum KinesisError: AmplifyError {
case cache(ErrorDescription, RecoverySuggestion, Error?)
case cacheLimitExceeded(ErrorDescription, RecoverySuggestion, Error?)
case validation(ErrorDescription, RecoverySuggestion, Error?)
case unknown(ErrorDescription, RecoverySuggestion, Error?)
}AmplifyClients/Amplify<Service>Client/
├── Sources/
│ ├── Amplify<Service>Client.swift # Public facade
│ └── Support/ # Error type, actors, protocols, impls
├── Tests/
│ └── UnitTests/
Integration tests are not co-located per client. AmplifyConnectClient has its own host app under Tests/ConnectClientHostApp/, Kinesis and Firehose share AmplifyClients/Tests/IntegrationTests/KinesisFirehoseClientHostApp/, and AmplifyEventEnrichmentClient has unit tests only.
.target(
name: "Amplify<Service>Client",
dependencies: ["AmplifyFoundation", "AmplifyFoundationBridge",
// Add "AmplifyRecordCache" (+ the SQLite product) only if the
// client buffers records locally, as Kinesis and Firehose do.
.product(name: "AWS<Service>", package: "aws-sdk-swift")],
path: "AmplifyClients/Amplify<Service>Client/Sources",
resources: [.copy("Resources/PrivacyInfo.xcprivacy")],
swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]
)| Rule | Details |
|---|---|
| Minimal deps | Import AmplifyFoundation + AmplifyFoundationBridge; may use Amplify core types if needed, avoid AWSPluginsCore |
| Actor internals | All mutable shared state in actors |
| Strict concurrency | Enable flag, all types Sendable |
| Protocol-driven | Abstract storage/network/scheduler behind protocols |
AmplifyError errors |
Public error enum with errorDescription + recoverySuggestion |
| Credentials via Foundation | Accept AWSCredentialsProvider, convert via Bridge adapters |
| User-Agent injection | Use UserAgentClientEngine for HTTP requests |
| Escape hatch | Expose the underlying AWS SDK client |
| Configurable | Options struct with sensible defaults |
| Testable | In-memory storage alternatives, mock senders |
let sdkCredentials = FoundationToSDKCredentialsAdapter(provider: credentialsProvider)
var config = try AWS<Service>.<Service>Client.<Service>ClientConfiguration(
region: region, credentialIdentityResolver: sdkCredentials
)
config.httpClientEngine = UserAgentClientEngine(
target: config.httpClientEngine,
additionalMetadata: ["md/amplify-<service>"]
)Do not append the version here — UserAgentClientEngine already injects lib/amplify-swift#<version>, so adding AmplifyMetadata.version to the md/ segment stamps it twice.