Amplify Library for Swift

repository·main·Indexed 19 days ago

https://github.com/aws-amplify/amplify-swift

A declarative interface for common cloud operations including Authentication, Storage, and Data, built on top of the AWS SDK for Swift. It provides high-level category APIs and an 'escape hatch' for direct access to low-level AWS services. Supported platforms include iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, and visionOS 1+. Includes tools like AmplifyXcode for syncing configuration files and Swift models with Xcode projects.

Tokens
9.1K
Snippets
23
Records
50
Agent score
67%

What's inside amplify-swift

  1. Use declarative interaction-based APIs instead of service-specific calls

    main

    Amplify encourages using declarative, interaction-based APIs rather than calling specific cloud provider service methods directly.

    Instead of learning and managing complex client-service interactions (like S3's CreateMultipartUpload or PutObject), you should use Amplify's human-readable APIs such as UploadData or UploadFile.

    Benefits of this approach:

    • Abstraction: You don't need to worry about which specific AWS service is being used under the hood.
    • Error Handling: Amplify provides human-readable errors and recovery suggestions.
    • Optimization: Amplify programmatically optimizes for cost and performance through its opinionated implementations (e.g., preferring API Gateway endpoints for JSON interactions).
  2. Understand the Amplify design philosophy: Categories and Plugins

    main

    Amplify is built on a modular architecture consisting of Categories and Plugins. This separation allows you to keep your app's bundle size small by only importing the functionality you need.

    • Categories: These are high-level, declarative collections of API calls that represent functional use cases (the "WHAT"). Examples include Storage, Authentication, API, DataStore, Analytics, and Geo.
    • Plugins: These provide the actual implementation (the "HOW") for a category. For example, the Storage category provides APIs like UploadData, while the AWSStoragePlugin provides the underlying implementation that communicates with AWS S3.

    By using this pattern, you can interact with high-level concepts without needing to manage the specific complexities of the underlying cloud services.

  3. Use AWS SDK for Swift for services not covered by Amplify

    main

    If you need to interact with AWS services that do not have a dedicated Amplify category (such as Amazon SQS, EventBridge, or DynamoDB Streams), you should import the AWS SDK for Swift directly.

    To maintain a seamless user experience, you can share credentials between Amplify and the AWS SDK by passing the credentials obtained from Amplify.Auth.fetchAuthSession() into your service client configuration.

    // Example concept: sharing credentials
    let authSession = try await Amplify.Auth.fetchAuthSession()
    // Use authSession credentials to configure your AWS SDK for Swift client
  4. Manage AppSyncRealTimeSubscription lifecycle

    main

    The AppSyncRealTimeSubscription manages the subscription lifecycle within an actor-isolated context.

    It provides a data stream representing the subscription's State. This state stream is merged into the main response stream, allowing the consumer to listen to a single stream that contains both the subscription status/state and the actual data responses.

  5. Use the Escape Hatch to access underlying AWS SDKs

    main

    If a feature is not directly exposed by an Amplify category, or if you need to call a specific method from the underlying AWS SDK (like AWSS3), you can use the 'Escape Hatch'.

    To do this, retrieve the plugin for the specific category using Amplify.Storage.getPlugin(for:) (or the relevant category method), cast it to the specific plugin type, and call .getEscapeHatch() to access the underlying service client.

    import Amplify
    import AWSS3StoragePlugin
    import AWSS3
    
    // ...
    
    // 1. Get the plugin and cast it
    guard let plugin = try Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin else {
        print("Unable to to cast to AWSS3StoragePlugin")
        return
    }
    
    // 2. Access the escape hatch
    let awsS3 = plugin.getEscapeHatch()
    
    // 3. Use the underlying AWS SDK directly
    let accelerateConfigInput = PutBucketAccelerateConfigurationInput()
    do {
        let accelerateConfigOutput = try await awsS3.putBucketAccelerateConfiguration(
            input: accelerateConfigInput
        )
        print("putBucketAccelerateConfiguration output: \(accelerateConfigOutput)")
    } catch {
        print("putBucketAccelerateConfiguration error: \(error)")
    }
  6. Automatic CLI integration for AmplifyXcode

    main

    If you use the standard Amplify CLI workflows, AmplifyXcode commands are executed automatically for you:

    1. Project Initialization: When running amplify init --quickstart --frontend ios, relevant Amplify files are automatically added to your Xcode project within a AmplifyConfig group.
    2. Model Generation: When running amplify codegen models, the generated *.swift files (located under amplify/generated/models/) are automatically added to your Xcode project within a group named AmplifyModels.
  7. Override Amplify's opinionated implementations with escape hatches

    main

    Amplify provides highly-opinionated, declarative interfaces designed to follow best practices for cloud interactions. However, if your application requires custom business logic or a specific service interaction that deviates from Amplify's defaults, you can use "escape hatches".

    Escape hatches allow you to override the standard "HOW" (the implementation) while still utilizing the "WHAT" (the functional use case), enabling you to plug in custom application-specific logic without losing the velocity provided by Amplify's tested designs.

  8. Use Temporal types for precise date persistence

    main

    The DataStore > Model > Temporal module provides specialized types to complement Swift's Date. While Date represents a single point in time with high flexibility, it can introduce ambiguity in persistent data (e.g., whether you intended to store only a date or a full timestamp).

    Use the following types to enforce specific granularity when persisting values to a database:

    • Temporal.Date: For date-only representations.
    • Temporal.DateTime: For date and time representations.
    • Temporal.Time: For time-only representations.

    These types rely on a fixed ISO-8601 Calendar implementation. Because DateTime and Time are backed by a standard Swift Date instance, they remain compatible with all existing Foundation Date APIs and third-party libraries.

    // Example of using Temporal types for specific granularity
    // (Note: Actual instantiation depends on the specific Temporal API implementation)
    // but they are designed to replace or wrap standard Date for persistence.
  9. How model associations and lazy loading work

    main

    When retrieving a model that has associations (e.g., a Post that has many Comments), Amplify performs a 'shallow' fetch by default. The initial query only retrieves the first level of data to ensure scalability and prevent massive object graphs. The associated data is stored as metadata within an AppSyncListProvider rather than being fully loaded.

    Developers can load these associations in two ways:

    1. Explicit Load: Call .fetch() on the association property. This is useful when you want to control exactly when the network request occurs.
    2. Implicit Load: Simply iterate over the association (e.g., in a for-in loop). The plugin will automatically perform the necessary query to fetch the associated items before the first element is returned.
    // Explicit load
    if let comments = post.comments {
      comments.fetch { result in
          switch result {
          case .success: 
              print("list data is now loaded")
          case .failure(let error):
              print("Error: \(error)")
          }
      }
    }
    
    // Implicit load
    for comment in post.comments {
       // comments are loaded automatically before the first iteration
    }
  10. Handle cancellation with Amplify.Publisher

    main

    When using Amplify.Publisher, cancelling a Combine subscription will automatically cancel the underlying Swift Concurrency Task.

    Important Note on Progress Sequences: If you are using a publisher for progress updates (e.g., during a file upload or download), cancelling the publisher will only stop the progress updates. It will not cancel the actual underlying task (the upload or download itself). To stop the actual operation, you must explicitly call .cancel() on the task itself or cancel its parent task.