

# Using the SDK for Swift
<a name="using"></a>

After completing the steps in [Setting up the AWS SDK for Swift](setting-up.md), you’re ready to make requests to AWS services such as Amazon S3, DynamoDB, IAM, Amazon EC2, and more by instantiating service objects and making calls on them using the AWS SDK for Swift. The guides below cover setting up and using AWS services for common use cases.

**Topics**
+ [Making AWS service requests](making-requests.md)
+ [Handling errors](using-errors.md)
+ [Pagination](using-paginators.md)
+ [Testing and debugging](using-testing-debugging.md)
+ [Waiters](using-waiters.md)
+ [Lambda functions](lambda.md)
+ [Event streaming](using-event-streaming.md)
+ [Presigning requests](using-presigning.md)
+ [Integrating with Apple](apple-integration.md)

# Making AWS service requests using the AWS SDK for Swift
<a name="making-requests"></a>

 To programmatically access AWS services, SDKs use a client class/object for each AWS service. For example, if your application needs to access Amazon EC2, your application creates an Amazon EC2 client object to interface with that service. You then use the service client to make requests to that AWS service. 

To make a request to an AWS service, you must first create and [configure](configuring.md) a service client. For each AWS service your code uses, it has its own crate/library/gem/package and its own dedicated type for interacting with it. The client exposes one method for each API operation exposed by the service. 

The process of sending requests to AWS services is as follows:

1. Create a service client object with the desired configuration, such as the specific AWS Region.

1. Create an input object with the values and data needed to make the request. For example, when sending a `GetObject` request to Amazon S3, you need to specify the bucket name and the key of the Amazon S3 object that you want to access. For a service action named `SomeOperation`, the input parameters object is created using a function called `SomeOperationInput()`.

1. Call the service object method on the client object that sends the desired request, with the input object created in the previous step.

1. Use `await` to wait for the response, and handle thrown exceptions to appropriately handle error conditions.

1. Examine the contents of the returned structure for the results you need. Every SDK function returns a structure with a type whose name is the same as the service action performed by the function, followed by the word `Output`. For example, when calling the Amazon S3 function `[S3Client.createBucket(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/createbucket(input:))`, the return type is `[CreateBucketOutput](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/createbucketoutput)`.

The following sections demonstrate this process in more detail.

## Creating and using AWS client objects
<a name="service-objects"></a>

Before you can send requests to an AWS service, you must first instantiate a client object corresponding to the service. These client classes are helpfully named using the service name and the word `Client`. Examples include `[S3Client](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client)` and `[IAMClient](https://sdk.amazonaws.com/swift/api/awsiam/latest/documentation/awsiam/iamclient)`. 

After creating the client object, use it to make your requests. When you're done, release the object. If the service connection is open, it is closed for you automatically.

```
do {
    let s3 = try await S3Client()
        
    // ...
} catch {
    dump(error)
}
```

If an error occurs while trying to instantiate an AWS service — or at any time while using the service — an exception is thrown. Your `catch` block should handle the error appropriately.

Unless you are in a testing environment in which you expect a knowledgeable user to have configured reasonable default options, specify the appropriate service configuration when instantiating the client object. This is described in [Configuring AWS SDK for Swift service clients in code](config-code.md).

## Specifying service client function parameters
<a name="service-object-parameters"></a>

When calling service client methods, you pass the input object corresponding to that operation. For example, before calling the `[getObject()](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/getobject(input:))` method on the Amazon S3 service class `[S3Client](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client)`, you need to create the input parameter object using the initializer `[GetObjectInput()](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/getobjectinput)`.

```
do {
    let s3 = try S3Client()
    let inputObject = GetObjectInput(bucket: "amzn-s3-demo-bucket", key: "keyName")
    let output = try await s3.getObject(input: inputObject)
        
    // ...
} catch {
    dump(error)
}
```

In this example, `[GetObjectInput()](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/getobjectinput)` is used to create an input object for the ` [getObject(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/getobject(input:))` method. The resulting input object specifies that the desired data has the `keyName` key and should be fetched from the Amazon S3 bucket named `amzn-s3-demo-bucket`.

## Calling SDK functions
<a name="calling-sdk-functions"></a>

Nearly all AWS SDK for Swift functions are asynchronous and can be called using Swift's `async`/`await` model. 

To call one of the SDK's asynchronous functions from synchronous code, call the function from a Swift `Task` created and run from your synchronous code.

### Calling SDK functions asynchronously
<a name="using-asynchronous-functions"></a>

The following function fetches and returns the content of a file named `text/motd.txt` from a bucket named amzn-s3-demo-bucket, using Amazon S3.

```
func getMOTD() async throws -> String? {
    let s3 = try S3Client()
    let motdInput = GetObjectInput(bucket: "amzn-s3-demo-bucket",
                                    key: "text/motd.txt")
    let output = try await s3.getObject(input: motdInput)

    guard let data = output.body?.toBytes().toData() else {
        return nil
    }
    return String(decoding: data, as: UTF8.self)
}
```

The `getMOTD()` function can only be called from another `async` function, and returns a string that contains the text in the `MOTD` file or `nil` if the file is empty. It throws an exception on errors. Thus, you call the `getMOTD()` function.

```
do {
    let motd = try await getMOTD()
    // ...
} catch {
    dump(error)
}
```

Here, the fetched "message of the day" text is available in the variable `motd` immediately following the call to `getMOTD()`. If an error occurs attempting to fetch the text, an appropriate exception is delivered to the `catch` clause. The standard Swift variable `error` describes the problem that occurred.

### Calling SDK functions from synchronous code
<a name="using-asynchronous-tasks"></a>

To call AWS SDK for Swift functions from synchronous code, enclose the code that needs to run asynchronously in a `Task`. The `Task` uses `await` for each SDK function call that returns its results asynchronously. You might need to use an atomic flag or other means to know that the operation has finished.

**Note**  
It's important to properly manage asynchronous requests. Be sure that any operation that's dependent on a previous result waits until that result is available before it begins. When used properly, the `async`/`await` model handles most of this for you.

```
func updateMOTD() {
    Task() {
        var motd: String = ""

        do {
            let s3 = try S3Client()
            let motdInput = GetObjectInput(bucket: "amzn-s3-demo-bucket",
                            key: "text/motd.txt")
            let output = try await s3.getObject(input: motdInput)

            if let bytes = output.body?.toBytes() {
                motd = String(decoding: bytes.toData(), as: UTF8.self)
            }
        } catch {
            motd = ""
        }

        setMOTD(motd)
    }
}
```

In this example, the code inside the `Task` block runs asynchronously, returning no output value to the caller. It fetches the contents of a text file with the key `text/motd.txt` and calls a function named `setMOTD()`, with the contents of the file decoded into a UTF-8 string.

A `do`/`catch` block is used to capture any thrown exceptions and set the `motd` variable to an empty string, which indicates that no message is available.

A call to this `updateMOTD()` function will spawn the task and return immediately. In the background, the program continues to run while the asynchronous task code fetches and uses the text from the specified file on Amazon S3. When the task has completed, the `Task` automatically ends.

# Handling errors in the AWS SDK for Swift
<a name="using-errors"></a>

## Overview
<a name="errors-overview"></a>

The AWS SDK for Swift uses Swift's standard error handling mechanism to report errors that occur while using AWS services. Errors are reported using Swift's `throw` statement.

To catch the errors that an AWS SDK for Swift function might return, use the Swift `do`/`catch` statement. Encapsulate the code that calls the SDK inside the `do` block, then use one or more `catch` blocks to capture and handle the errors. Each `catch` block can capture a specific error, all errors of a specific category, or all uncaught errors. This lets an application recover from errors it knows how to handle, notify the user of transient errors or errors that can't be recovered from but are non-fatal, and safely exit the program if the error is fatal.

**Note**  
The APs for each service client are generated using models specified using the [Smithy](https://smithy.io/) interface definition language (IDL). The Smithy models describe the underlying types and APIs of each AWS service. These models are used to generate the Swift types and classes that comprise the SDK. This is useful to understand both while reading this guide and while writing error handling code.

## AWS SDK for Swift error protocols
<a name="errors-protocols"></a>

SDK for Swift errors conform to one or more error protocols. The protocols implemented by the error depend on the type of error that occurred and the context in which it occurred.

### The `Error` protocol
<a name="errors-protocol-error"></a>

Every error thrown by the AWS SDK for Swift conforms to the standard Swift `Error` protocol. As such, every error has a `localizedDescription` property that returns a string containing a useful description of the error.

When the underlying AWS service provides an error message, that string is used as the `localizedDescription`. These are usually in English. Otherwise, the SDK generates an appropriate message, which may or may not be localized.

### The `AWSServiceError` protocol
<a name="errors-protocols-serviceerror"></a>

When the AWS service responds with a service error, the error object conforms to the `AWSClientRuntime.AWSServiceError` protocol. 

**Note**  
If an `AWSServiceError` occurs while performing a service action over an HTTP connection, the error also implements the `HTTPError` protocol. Currently, all AWS protocols use HTTP, but if this were to change, an appropriate error protocol would be added.

Errors that conform to `AWSServiceError` include these additional properties:

`errorCode`  
An `optional` string identifying the error type.

`requestID`  
An `optional` string that gives the request ID of the request that resulted in the error.

### The `ModeledError` protocol
<a name="errors-protocols-modelederror"></a>

When an error occurs that matches a defined, modeled error type, the error object conforms to the protocol `ClientRuntime.ModeledError`, in addition to any other appropriate protocols such as `HTTPError`. This includes most of the errors defined by an AWS service.

`ModeledError` adds several useful properties to the error:

`fault`  
A value from the `ClientRuntime.ErrorFault` enum. The value is `.client` if the source of the error is the client, or `.server` if the server is the source of the error.

`isRetryable`  
A Boolean value indicating whether or not the model indicates that the failed operation can be retried.

`isThrottling`  
A Boolean value indicating whether or not the model indicates that the error is due to throttling.

### The `HTTPError` protocol
<a name="errors-protocols-httperror"></a>

Errors that occur during an action that uses an HTTP connection conform to the `ClientRuntime.HTTPError` protocol. An error conforming to `HTTPError` contains an HTTP response whose status code is in either the 4xx range or the 5xx range.

`HTTPError` adds one property to the error:

`httpResponse`  
An object of type `HttpResponse`, which describes the entire HTTP response from the AWS service. It has properties that include the response's headers, body, and the HTTP status code.

## Handling errors
<a name="errors-handling"></a>

All errors returned by the SDK for Swift implement the standard Swift `Error` protocol. The error's type depends on the service and the error being reported, so it could be any Swift type including but not limited to `enum`, `struct`, or `class`, depending on what kind of error occurred. For example, an error reporting that an Amazon S3 bucket is missing may conform to `Error`, `AWSServiceError`, and `HTTPError`. This lets you know it's a service error that occurred while communicating using the HTTP protocol. In this case, the HTTP status code is 404 (Not Found), because of the missing bucket.

Even if no other information is provided, the error's `localizedDescription` property is always a string describing the error.

When catching errors thrown by the AWS SDK for Swift, follow these guidelines:
+ If the error is modeled, the error is a `struct` describing the error. Catch these errors using that `struct`'s name. In many cases, you can find these modeled errors listed in the documentation of an action in the [AWS SDK for Swift API Reference](https://sdk.amazonaws.com/swift/api).
+ If the error isn't modeled, but still originates from an AWS service, it will conform to the protocol `AWSServiceError`. Use `catch let error as AWSServiceError`, then look at the error's `errorCode` property to determine what error occurred.
+ **Don't catch any concrete types that represent unknown errors**, such as `UnknownAWSHTTPServiceError`. These are reserved for internal use.

### Service errors
<a name="errors-handling-service"></a>

An error thrown because of an AWS service response, whether it could be parsed or not, conforms to the `AWSServiceError` protocol. An error defined by the underlying Smithy model for service also conforms to `ModeledError` and has a concrete type. One example is the Amazon S3 error `CreateBucketOutputError`, which is thrown by the `[S3Client.createBucket()](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/createbucket(input:))` method.

Any `AWSServiceError` received over an HTTP connection also conforms to `HTTPError`. This is currently all service errors, but that could change in the future if a service adds support for other network protocols.

The following code tries to create an object on Amazon S3, with code to handle service errors. It features a `catch` clause that specifically handles the error code `NoSuchBucket`, which indicates that the bucket doesn't exist. This snippet assumes that the given bucket name doesn't exist.

```
        do {
            let client = try S3Client(region: "us-east-1")

            _ = try await client.putObject(input: PutObjectInput(
                body: ByteStream.data(Data(body.utf8)),
                bucket: bucketName,
                key: objectKey
            ))
            print("Done.")
        } catch let error as AWSServiceError {
            let errorCode = error.errorCode ?? "<none>"
            let message = error.message ?? "<no message>"

            switch errorCode {
            case "NoSuchBucket":
                print("   | The bucket \"\(bucketName)\" doesn't exist. This is the expected result.")
                print("   | In a real app, you might ask the user whether to use a different name or")
                print("   | create the bucket here.")
            default:
                print("   | Service error of type \(error.errorCode ?? "<unknown>"): \(message)")
            }
        } catch {
            print("Some other error occurred.")
        }
```

### HTTP errors
<a name="errors-handling-http"></a>

When the SDK encounters an error while communicating with an AWS service over HTTP, it throws an error that conforms to the protocol `ClientRuntime.HTTPError`. This kind of error represents an HTTP response whose status codes are in the 4xx and 5xx ranges.

**Note**  
Currently, HTTP is the only network protocol used by AWS. If a future AWS product uses a non-HTTP network protocol, a corresponding error protocol would be added to the SDK. Errors that occur while using the new wire protocol would conform to that Swift protocol instead of `HTTPError`.

`HTTPError` includes an `httpResponse` property that contains an object of the class `HttpResponse`. The `httpResponse` provides information received in the response to the failed HTTP request. This provides access to the response headers, including the HTTP status code.

```
        do {
            let client = try S3Client(region: "us-east-1")

            _ = try await client.getObject(input: GetObjectInput(
                bucket: "not-a-real-bucket",
                key: "not-a-real-key"
            ))
            print("   | Found a matching bucket but shouldn't have!")
        } catch let error as HTTPError {
            print("   | HTTP error; status code: \(error.httpResponse.statusCode.rawValue). This is the")
            print("   | expected result.")
        } catch {
            dump(error, name: "   | An unexpected error occurred.")
        }
```

This example creates an Amazon S3 client, then calls its `[getObject(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/getobject(input:))` function to fetch an object using a bucket name and key that don't exist. Two `catch` blocks are used. The first matches errors of type `HTTPError`. It retrieves the HTTP status code from the response. The status code can then be used to handle specific scenarios, recover from recoverable errors, or whatever the project requires.

The second `catch` block is a catch-all that just dumps the error to the console. In a full application, this block would ideally either clean up after the failed access attempt and return the application to a safe state, or perform as clean an application exit as possible.

### Handling other errors
<a name="errors-handling-other"></a>

To catch any errors not already caught for a given `do` block, use the `catch` keyword with no qualifiers. The following snippet simply catches all errors.

```
do {
    let s3 = try await S3Client()
        
    // ...
} catch {
    // Handle the error here.
}
```

Within the context of the `catch` block, the caught error, reported in the constant with the default name `error`, conforms to at least the standard Swift `[Error](https://developer.apple.com/documentation/swift/error)` type. It may also conform to a combination of the other AWS SDK for Swift error protocols.

If you use a catch-all like this in your code, it needs to safely stop whatever task it was trying to perform and clean up after itself. In extreme cases, it may be necessary to safely terminate the application and ideally provide diagnostic output to be relayed to the developer.

While developing a project, it can be helpful to temporarily output error details to the console. This can be useful when debugging, or to help determine which errors that occur may need special handling. The Swift `dump()` function can be used to do this.

```
do {
    let output = try await client.listBuckets(input: ListBucketsInput())
        
    // ...
} catch {
    dump(error, name: "Getting the bucket list")
}
```

The `dump()` function outputs the entire contents of the `error` to the console. The `name` argument is an `optional` string used as a label for the output, to help identify the source of the error in the program's output.

# Using paginated results in the AWS SDK for Swift
<a name="using-paginators"></a>

Many AWS operations return truncated results when the payload is too large to return in a single response. Instead, the service returns a portion of the data and a token to retrieve the next set of items. This pattern is known as pagination.

The AWS SDK for Swift provides specialized versions of the functions that provide paginated results. These special functions end with the word `Paginated`. All your code needs to do is process the results as they arrive.

Each paginator is a function that returns an object of type `PaginatorSequence<input-type, output-type>`. The `PaginatorSequence<>` is an `AsyncSequence`. The `PaginatorSequence` type is a "lazy" sequence, so no AWS service requests are made until you start iterating over the pages. This also means that any errors that occur during the operation don't reach you until iteration begins.

**Note**  
The examples in this section of the developer guide use Amazon S3. However, the concept is the same for any service that has one or more paginated APIs.

For example, the paginated version of the `S3Client` function `listBuckets(input:)`, `listBucketsPaginated(input:)`, returns an object of type `PaginatorSequence<ListBucketsInput, ListBucketsOutput>`:

```
        let pages = client.listBucketsPaginated(
            input: ListBucketsInput(maxBuckets: PAGE_SIZE)
        )
```

In this example, the number of results in each page is specified by adding a `maxBuckets` property to the `ListBucketsInput` object. Each paginator uses an appropriate name for this property. As of the time `listBucketsPaginated(input:)` returns, no requests have been sent to the Amazon S3 service.

The `PaginatorSequence<>` is a sequence of pages which are asynchronously added to the sequence as the results are received. The type of each entry in the sequence is the `Output` `struct` corresponding to the function called. For example, if you call `S3Client.listBucketsPaginated(input:)`, each entry in the sequence is a `ListBucketsOutput` object. Each entry's buckets can be found in the its `ListBucketsOutput.buckets` property, which is an array of objects of type `S3ClientTypes.Bucket`.

To begin sending requests and receiving results, asynchronously iterate over each page, then iterate over each page's items:

```
        var pageNumber = 0

        do {
            for try await page in pages {
                pageNumber += 1

                guard let pageBuckets = page.buckets else {
                    print("ERROR: No buckets returned in page \(pageNumber)")
                    continue
                }

                print("\nPage \(pageNumber):")

                // Print this page's bucket names.

                for bucket in pageBuckets {
                    print("  " + (bucket.name ?? "<unknown>"))
                }
            }
        } catch {
            print("ERROR: Unable to process bucket list pages.")
        }
```

The outer `for` loop uses `await` to process pages of results as they're delivered, asynchronously. Once a page is received, the inner loop iterates over the buckets found in each entry's `buckets` property. The full example is [available on GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift/example_code/swift-sdk/pagination).

# Testing and debugging with the AWS SDK for Swift
<a name="using-testing-debugging"></a>

## Logging
<a name="using-logging"></a>

The AWS SDK for Swift and the underlying Common RunTime (CRT) library use Apple’s [SwiftLog](https://github.com/apple/swift-log) mechanism to log informational and debugging messages. The output from the logging system appears in the console provided by most IDEs, and also in the standard system console. This section covers how to control the output level for both the SDK for Swift and the CRT library.

### Configuring SDK debugging
<a name="logging-enable"></a>

By default, the SDK's logging system emits logs containing trace and debug level information. The default log level is `info`. To see more informational text as the SDK works, you can change the log level to `error` as shown in the following example:

```
import ClientRuntime

await SDKLoggingSystem().initialize(logLevel: .error)
```

Call `SDKLoggingSystem.initialize(logLevel:)` no more than one time in your application, to set the log level cutoff.

The log levels supported by the AWS SDK for Swift are defined in the `SDKLogLevel` `enum`. These correspond to similarly-named log levels defined in SwiftLog. Each log level is inclusive of the messages at and more severe that level. For example, setting the log level to `warning` also causes log messages to be output for levels `error` and `critical`.

The SDK for Swift log levels (from least severe to most severe) are:
+ `.trace`
+ `.debug`
+ `.info` (the default)
+ `.notice`
+ `.warning`
+ `.error`
+ `.critical`

### Configuring Common RunTime logging
<a name="logging-crt"></a>

If the level of detail generated by the SDK logs isn't providing what you need, you can try configuring the Common RunTime (CRT) log level. CRT is responsible for the lower-level networking and other system interactions performed by the SDK. Its log output includes details about HTTP traffic, for example.

To set CRT's log level to `debug`:

```
import ClientRuntime
         
SDKDefaultIO.setLogLevel(level: .debug)
```

The CRT log levels (from least severe to most severe) are:
+ `.none`
+ `.trace`
+ `.debug`
+ `.info`
+ `.warn`
+ `.error`
+ `.fatal`

Setting the CRT log level to `trace` provides an enormous level of detail that can take some effort to read, but it can be useful in demanding debugging situations.

## Mocking the AWS SDK for Swift
<a name="using-mocking"></a>

When writing unit tests for your AWS SDK for Swift project, it's useful to be able to mock the SDK. Mocking is a technique for unit testing in which external dependencies — such as the SDK for Swift — are replaced with code that simulates those dependencies in a controlled and predictable way. Mocking the SDK removes network requests, which eliminates the chance that tests can be unreliable due to intermittent network issues.

In addition, well-written mocks are almost always faster than the operations they simulate, letting you test more thoroughly in less time.

The Swift language doesn't provide the read/write [reflection](https://en.wikipedia.org/wiki/Reflective_programming) needed for direct mocking. Instead, you adapt your code to allow an indirect form of mocking. This article describes how to do so with minimal changes to the main body of your code.

To mock the AWS SDK for Swift implementation of a service class, create a protocol. In the protocol, define each of that class's functions that you need to use. This serves as the abstraction layer that you need to implement mocking. It's up to you whether to use a separate protocol for each AWS service class used in your project. Alternatively, you can use a single protocol that encapsulates every SDK function that you call. The examples in this guide use the latter approach, but this is often the same as using a protocol for each service.

After you define the protocol, you need two classes that conform to the protocol: one class in which each function calls through to the corresponding SDK function, and one that mocks the results as if the SDK function was called. Because these two classes both conform to the same protocol, you can create functions that perform AWS actions by calling functions on an object conforming to the protocol.

## Example: Mocking an Amazon S3 function
<a name="using-mocking-example"></a>

Consider a program that needs to use the `[S3Client](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client)` function `[listBuckets(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/listbuckets(input:))`. To support mocking, this project implements the following:
+ `S3SessionProtocol`, a Swift protocol which declares the Amazon S3 functions used by the project. This example uses just one Amazon S3 function: `listBuckets(input:)`.
+ `S3Session`, a class conforming to `S3SessionProtocol`, whose implementation of `listBuckets(input:)` calls `[S3Client.listBuckets(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/listbuckets(input:))`. This is used when running the program normally.
+ `MockS3Session`, a class conforming to `S3SessionProtocol`, whose implementation of `listBuckets(input:)` returns mocked results based on the input parameters. This is used when running tests.
+ `BucketManager`, a class that implements access to Amazon S3. This class should accept an object conforming to the session protocol `S3SessionProtocol` during initialization, then perform all AWS requests by making calls through that object. This makes the code testable: the *application* initializes the class by using an `S3Session` object for AWS access, while *tests* use a `MockS3Session` object.

The rest of this section takes an in-depth look at this implementation of mocking. The [complete example is available](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift/example_code/swift-sdk/mocking) on GitHub.

### Protocol
<a name="using-mocking-protocol"></a>

In this example, the `S3SessionProtocol` protocol declares the one `S3Client` function that it needs:

```
/// The S3SessionProtocol protocol describes the Amazon S3 functions this
/// program uses during an S3 session. It needs to be implemented once to call
/// through to the corresponding SDK for Swift functions, and a second time to
/// instead return mock results.
public protocol S3SessionProtocol {
    func listBuckets(input: ListBucketsInput) async throws
            -> ListBucketsOutput
}
```

This protocol describes the interface by which the pair of classes perform Amazon S3 actions.

### Main program implementation
<a name="using-mocking-class"></a>

To let the main program make Amazon S3 requests using the session protocol, you need an implementation of the protocol in which each function calls the corresponding SDK function. In this example, you create a class named `S3Session` with an implementation of `listBuckets(input:)` that calls `S3Client.listBuckets(input:)`:

```
public class S3Session: S3SessionProtocol {
    let client: S3Client
    let region: String

    /// Initialize the session to use the specified AWS Region.
    ///
    /// - Parameter region: The AWS Region to use. Default is `us-east-1`.
    init(region: String = "us-east-1") throws {
        self.region = region

        // Create an ``S3Client`` to use for AWS SDK for Swift calls.
        self.client = try S3Client(region: self.region)
    }

    /// Call through to the ``S3Client`` function `listBuckets()`.
    ///
    /// - Parameter input: The input to pass through to the SDK function
    ///   `listBuckets()`.
    ///
    /// - Returns: A ``ListBucketsOutput`` with the returned data.
    ///
    public func listBuckets(input: ListBucketsInput) async throws
            -> ListBucketsOutput {
        return try await self.client.listBuckets(input: input)
    }
}
```

The initializer creates the underlying `S3Client` through which the SDK for Swift is called. The only other function is `listBuckets(input:)`, which returns the result of calling the `S3Client` function of the same name. Calls to AWS services work the same way they do when calling the SDK directly.

### Mock implementation
<a name="using-mocking-mock"></a>

In this example, add support for mocking calls to Amazon S3 by using a second implementation of `S3SessionProtocol` called `MockS3Session`. In this class, the `listBuckets(input:)` function generates and returns mock results:

```
    /// An implementation of the Amazon S3 function `listBuckets()` that
    /// returns the mock data instead of accessing AWS.
    ///
    /// - Parameter input: The input to the `listBuckets()` function.
    ///
    /// - Returns: A `ListBucketsOutput` object containing the list of
    ///   buckets.
    public func listBuckets(input: ListBucketsInput) async throws
            -> ListBucketsOutput {
        let response = ListBucketsOutput(
            buckets: self.mockBuckets,
            owner: nil
        )
        return response
    }
```

This works by creating and returning a `[ListBucketsOutput](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/listbucketsoutput)` object, like the actual `S3Client` function does. Unlike the SDK function, this makes no actual AWS service requests. Instead, it fills out the response object with data that simulates actual results. In this case, an array of `[S3ClientTypes.Bucket](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3clienttypes/bucket)` objects describing a number of mock buckets is returned in the `buckets` property.

Not every property of the returned response object is filled out in this example. The only properties that get values are those that always contain a value and those actually used by the application. Your project might require more detailed results in its mock implementations of functions.

### Encapsulating access to AWS services
<a name="using-mocking-main"></a>

A convenient way to use this approach in your application design is to create an access manager class that encapsulates all your SDK calls. For example, when using Amazon DynamoDB (DynamoDB) to manage a product database, create a `ProductDatabase` class that has functions to perform needed activities. This might include adding products and searching for products. This Amazon S3 example has a class that handles bucket interactions, called `BucketManager`.

The `BucketManager` class initializer needs to accept an object conforming to `S3SessionProtocol` as an input. This lets the caller specify whether to interact with AWS by using actual SDK for Swift calls or by using a mock. Then, every other function in the class that uses AWS actions should use that session object to do so. This lets `BucketManager` use actual SDK calls or mocked ones based on whether testing is underway.

With this in mind, the `BucketManager` class can now be implemented. It needs an `init(session:)` initializer and a `getBucketNames(input:)` function:

```
public class BucketManager {
    /// The object based on the ``S3SessionProtocol`` protocol through which to
    /// call SDK for swift functions. This may be either ``S3Session`` or
    /// ``MockS3Session``.
    var session: S3SessionProtocol

    /// Initialize the ``BucketManager`` to call Amazon S3 functions using the
    /// specified object that implements ``S3SessionProtocol``.
    ///
    /// - Parameter session: The session object to use when calling Amazon S3.
    init(session: S3SessionProtocol) {
        self.session = session
    }

    /// Return an array listing all of the user's buckets by calling the
    /// ``S3SessionProtocol`` function `listBuckets()`.
    /// 
    /// - Returns: An array of bucket name strings.
    ///
    public func getBucketNames() async throws -> [String] {
        let output = try await session.listBuckets(input: ListBucketsInput())

        guard let buckets = output.buckets else {
            return []
        }
        
        return buckets.map { $0.name ?? "<unknown>" }
    }
}
```

The `BucketManager` class in this example has an initializer that takes an object that conforms to `S3SessionProtocol` as an input. That session is used to access or simulate access to AWS actions instead of calling the SDK directly, as shown by the `getBucketNames()` function.

### Using the access manager in the main program
<a name="using-mocking-main-program"></a>

The main program can now specify an `S3Session` when creating a `BucketManager` object, which directs its requests to AWS:

```
        /// An ``S3Session`` object that passes calls through to the SDK for
        /// Swift.
        let session: S3Session
        /// A ``BucketManager`` object that will be initialized to call the
        /// SDK using the session.
        let bucketMgr: BucketManager
        
        // Create the ``S3Session`` and a ``BucketManager`` that calls the SDK
        // using it.
        do {
            session = try S3Session(region: "us-east-1")
            bucketMgr = BucketManager(session: session)
        } catch {
            print("Unable to initialize access to Amazon S3.")
            return
        }
```

### Writing tests using the protocol
<a name="using-mocking-tests"></a>

Whether you write tests using Apple's XCTest framework or another framework, you must design the tests to use the mock implementation of the functions that access AWS services. In this example, tests use a class of type `XCTestCase` to implement a standard Swift test case:

```
final class MockingTests: XCTestCase {
    /// The session to use for Amazon S3 calls. In this case, it's a mock
    /// implementation. 
    var session: MockS3Session? = nil
    /// The ``BucketManager`` that uses the session to perform Amazon S3
    /// operations.
    var bucketMgr: BucketManager? = nil

    /// Perform one-time initialization before executing any tests.
    override class func setUp() {
        super.setUp()
    }

    /// Set up things that need to be done just before each
    /// individual test function is called.
    override func setUp() {
        super.setUp()

        self.session = MockS3Session()
        self.bucketMgr = BucketManager(session: self.session!)
    }

    /// Test that `getBucketNames()` returns the expected results.
    func testGetBucketNames() async throws {
        let returnedNames = try await self.bucketMgr!.getBucketNames()
        XCTAssertTrue(self.session!.checkBucketNames(names: returnedNames),
                "Bucket names don't match")
    }
}
```

This `XCTestCase` example's per-test `setUp()` function creates a new `MockS3Session`. Then it uses the mock session to create a `BucketManager` that will return mock results. The `testGetBucketNames()` test function tests the `getBucketNames()` function in the bucket manager object. This way, the tests operate using known data, without needing to access the network, and without accessing AWS services at all.

# Using waiters with the AWS SDK for Swift
<a name="using-waiters"></a>

A waiter is a client-side abstraction that automatically polls a resource until a desired state is reached, or until it's determined that the resource won't enter that state. This kind of polling is commonly used when working with services that typically reach a consistent state, such as Amazon Simple Storage Service (Amazon S3), or services that create resources asynchronously, like Amazon Elastic Compute Cloud (Amazon EC2).

Instead of writing logic to continuously poll an AWS resource, which can be cumbersome and error-prone, you can use a waiter to poll the resource. When the waiter returns, your code knows whether or not the desired state was reached and can act accordingly.

## How to use waiters
<a name="waiters-using"></a>

Many service client classes in the AWS SDK for Swift provide waiters for common "poll and wait" situations that occur while using AWS services. These situations can include both waiting until a resource is available and waiting until the resource becomes unavailable. For example:

**`[S3Client.waitUntilBucketExists(options:input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/waituntilbucketexists(options:input:))``[S3Client.waitUntilBucketNotExists(options:input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/waituntilbucketnotexists(options:input:))`**  
Wait until an Amazon S3 bucket exists (or no longer exists).

**`[DynamoDBClient.waitUntilTableExists(options:input:)](https://sdk.amazonaws.com/swift/api/awsdynamodb/latest/documentation/awsdynamodb/dynamodbclient/waituntiltableexists(options:input:))``[DynamoDBClient.waitUntilTableNotExists(options:input:)](https://sdk.amazonaws.com/swift/api/awsdynamodb/latest/documentation/awsdynamodb/dynamodbclient/waituntiltablenotexists(options:input:))`**  
Wait until a specific Amazon DynamoDB table exists (or no longer exists).

Waiter functions in the AWS SDK for Swift take two parameters, `options` and `input`.

### Waiter options
<a name="waiters-options"></a>

The first parameter for any waiter function, `options`, is a structure of type `WaiterOptions` (part of the `ClientRuntime` package) that describes the waiter's polling behavior. These options specify the maximum number of seconds to wait before polling times out, and let you optionally set the minimum and maximum delays between retries.

The following example shows how to configure a waiter to wait between a tenth of a second and a half second between retries. The maximum polling time is two seconds.

```
let options = WaiterOptions(maxWaitTime: 2, minDelay: 0.1, maxDelay: 0.5)
```

### Waiter parameters
<a name="waiters-input"></a>

A waiter function's inputs are specified using its `input` parameter. The input's data type corresponds to that of the SDK for Swift function used internally by the waiter to poll the AWS service. For example, the type is `[HeadBucketInput](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/headbucketinput)` for Amazon S3 waiters like `S3Client.waitUntilBucketExists(options:input:)` because this waiter uses the `[S3Client.headBucket(input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/headbucket(input:))` function to poll the bucket. The DynamoDB waiter `DynamoDBClient.waitUntilTableExists(options:input:)` takes as its input a structure of type `[DescribeTableInput](https://sdk.amazonaws.com/swift/api/awsdynamodb/latest/documentation/awsdynamodb/describetableinput)` because it calls `[DynamoDBClient.describeTable(input:)](https://sdk.amazonaws.com/swift/api/awsdynamodb/latest/documentation/awsdynamodb/dynamodbclient/describetable(input:))` internally.

## Example: Waiting for an S3 bucket to exist
<a name="waiters-waituntilbucketexists"></a>

The AWS SDK for Swift offers several waiters for Amazon S3. One of them is `waitUntilBucketExists(options:input:)`, which polls the server until the specified bucket exists.

```
    /// Wait until a bucket with the specified name exists, then return
    /// to the caller. Times out after 60 seconds. Throws an error if the
    /// wait fails.
    ///
    /// - Parameter bucketName: A string giving the name of the bucket
    ///   to wait for.
    /// 
    /// - Returns: `true` if the bucket was found or `false` if not.
    ///
    public func waitForBucket(name bucketName: String) async throws -> Bool {
        // Because `waitUntilBucketExists()` internally uses the Amazon S3
        // action `HeadBucket` to look for the bucket, the input is specified
        // with a `HeadBucketInput` structure.

        let output = try await client.waitUntilBucketExists(
            options: WaiterOptions(maxWaitTime: 60.0),
            input: HeadBucketInput(bucket: bucketName)
        )

        switch output.result {
            case .success:
                return true
            case .failure:
                return false
        }
    }
```

This example creates a function that waits until the specified bucket exists, then returns `true`. It returns `false` if polling fails, and might throw an exception if an error occurs. It works by calling the waiter function `[S3Client.waitUntilBucketExists(options:input:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/waituntilbucketexists(options:input:))` to poll the server. The `options` specify that polling should time out after 60 seconds.

Because this polling is done using the Amazon S3 action `HeadBucket`, a `HeadBucketInput` object is created with the input parameters for that operation, including the name of the bucket to poll for. This is used as the `input` parameter's value.

`S3Client.waitUntilBucketExists(options:input:)` returns a `result` property whose value is either `.success` if the polling successfully found the bucket, or `.failure` if polling failed. The function returns the corresponding Boolean value to the caller.

# Creating AWS Lambda functions using the AWS SDK for Swift
<a name="lambda"></a>

## Overview
<a name="lambda-overview"></a>

You can use the AWS SDK for Swift from within an AWS Lambda function by using the Swift AWS Lambda Runtime package in your project. See the [documentation for the `swift-aws-lambda-runtime`](https://github.com/swift-server/swift-aws-lambda-runtime) repository on GitHub for more information about the runtime package.

## Setting up a project to use AWS Lambda
<a name="lambda-setup"></a>

If you're starting a new project, create the project in Xcode or open a shell session and use the following command to use Swift Package Manager (SwiftPM) to manage your project:

```
$ swift package init --type executable --name LambdaExample
```

Remove the file `Sources/main.swift`. The source code file will have be `Sources/lambda.swift` to work around a [known Swift bug](https://github.com/swiftlang/swift/issues/55127) that can cause problems when the entry point is in a file named `main.swift`.

Add the `swift-aws-lambda-runtime` package to the project. There are two ways to accomplish this:
+ If you're using Xcode, choose the **Add package dependencies...** option in the **File** menu, then provide the package URL: `https://github.com/awslabs/swift-aws-lambda-runtime.git`. Choose the `AWSLambdaRuntime` module.
+ If you're using SwiftPM to manage your project dependencies, add the runtime package and its `AWSLambdaRuntime` module to your `Package.swift` file to make the module available to your project:

  ```
  import PackageDescription
  
  let package = Package(
      name: "LambdaExample",
      platforms: [
          .macOS(.v12)
      ],
      // The product is an executable named "LambdaExample", which is built
      // using the target "LambdaExample".
      products: [
          .executable(name: "LambdaExample", targets: ["LambdaExample"])
      ],
      // Add the dependencies: these are the packages that need to be fetched
      // before building the project.
      dependencies: [
          .package(
              url: "https://github.com/awslabs/swift-aws-lambda-runtime.git",
              from: "2.0.0"),
          .package(url: "https://github.com/awslabs/aws-sdk-swift.git",
              from: "1.0.0"),
      ],
      targets: [
          // Add the executable target for the main program. These are the
          // specific modules this project uses within the packages listed under
          // "dependencies."
          .executableTarget(
              name: "LambdaExample",
              dependencies: [
                  .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
                  .product(name: "AWSS3", package: "aws-sdk-swift"),
              ]
          )
      ]
  )
  ```

  This example adds a dependency on the Amazon S3 module of the AWS SDK for Swift in addition to the Lambda runtime.

  You may find it useful to build the project at this point. Doing so will pull the dependencies and may make them available for your editor or IDE to generate auto-completion or inline help:

  ```
  $ swift build
  ```

## Creating a Lambda function
<a name="lambda-function"></a>

To create a Lambda function in Swift, you generally need to define several components:
+ A `struct` that represents the data your Lambda function will receive from the client. It must implement the `Decodable` protocol. The [Swift AWS Lambda Runtime Events library](https://github.com/swift-server/swift-aws-lambda-events) contains a variety of struct definitions that represent common messages posted to a Lambda function by other AWS services.
+ A Lambda function handler that performs the Lambda function's work.
+ An optional `struct` that represents the data returned by your Lambda function. This is usually an `Encodable` `struct` describing the contents of a JSON document returned to the client.

### Imports
<a name="lambda-function-imports"></a>

Create the file `Sources/lambda.swift` and begin by importing the needed modules and types:

```
import Foundation
import AWSLambdaRuntime
@preconcurrency import AWSS3

import protocol AWSClientRuntime.AWSServiceError
import enum Smithy.ByteStream
```

These imports are:

1. The standard Apple `Foundation` API.

1. `AWSLambdaRuntime`is the Swift Lambda Runtime's main module.

1. `AWSS3` is the Amazon S3 module from the AWS SDK for Swift.

1. The `AWSClientRuntime.AWSServiceError` protocol describes service errors returned by the SDK.

1. The `Smithy.ByteStream` enum is a type that represents a stream of data. The Smithy library is one of the SDK's core modules.

### Defining structs and enums
<a name="lambda-function-types"></a>

Next, define the structs that represent the incoming requests and the responses sent back by the Lambda function, along with the enum used to identify errors thrown by the handler function:

```
/// Represents the contents of the requests being received from the client.
/// This structure must be `Decodable` to indicate that its initializer
/// converts an external representation into this type.
struct Request: Decodable, Sendable {
    /// The request body.
    let body: String
}

/// The contents of the response sent back to the client. This must be
/// `Encodable`.
struct Response: Encodable, Sendable {
    /// The ID of the request this response corresponds to.
    let req_id: String
    /// The body of the response message.
    let body: String
}

/// The errors that the Lambda function can return.
enum S3ExampleLambdaErrors: Error {
    /// A required environment variable is missing. The missing variable is
    /// specified.
    case noEnvironmentVariable(String)
}
```

### Function handler
<a name="lambda-function-handler-handle"></a>

To receive, process, and respond to incoming requests, implement the Swift Lambda Runtime's `LambdaRuntime` class like this:

```
let runtime = LambdaRuntime {
    (event: Request, context: LambdaContext) async throws -> Response in

    var responseMessage: String

    // Get the name of the bucket to write the new object into from the
    // environment variable `BUCKET_NAME`.
    guard let bucketName = ProcessInfo.processInfo.environment["BUCKET_NAME"] else {
        context.logger.error("Set the environment variable BUCKET_NAME to the name of the S3 bucket to write files to.")
        throw S3ExampleLambdaErrors.noEnvironmentVariable("BUCKET_NAME")
    }

    do {
        let filename = try await putObject(body: event.body, bucketName: bucketName)

        // Generate the response text and update the log.
        responseMessage = "The Lambda function has successfully stored your data in S3 with name '\(filename)'"
        context.logger.info("Data successfully stored in S3.")
    } catch let error as AWSServiceError {
        // Generate the error message and update the log.
        responseMessage = "The Lambda function encountered an error and your data was not saved. Root cause: \(error.errorCode ?? "") - \(error.message ?? "")"
        context.logger.error("Failed to upload data to Amazon S3.")
    }

    return Response(req_id: context.requestID, body: responseMessage)
}
```

The name of the bucket to use is first fetched from the environment variable `BUCKET_NAME`. Then the `putObject(body:bucketName:)` function is called to write the text into an Amazon S3 object, and the text of the response is set depending on whether or not the object is successfully written to storage. The response is created with the response message and the request ID string that was in the original Lambda request.

### Helper function
<a name="lambda-function-handler-support"></a>

This example uses a helper function, `putObject(body:bucketName:)`, to write strings to Amazon S3. The string is stored in the specified bucket by creating an object whose name is based on the current number of seconds since the start of the year 1970:

```
/// Create a new object on Amazon S3 whose name is based on the current
/// timestamp, containing the text specified.
/// 
/// - Parameters: 
///   - body: The text to store in the new S3 object.
///   - bucketName: The name of the Amazon S3 bucket to put the new object
///     into.
/// 
/// - Throws: Errors from `PutObject`.
/// 
/// - Returns: The name of the new Amazon S3 object that contains the
///   specified body text.
func putObject(body: String, bucketName: String) async throws -> String {
    // Generate an almost certainly unique object name based on the current
    // timestamp.
    
    let objectName = "\(Int(Date().timeIntervalSince1970*1_000_000)).txt"

    // Create a Smithy `ByteStream` that represents the string to write into
    // the bucket.
    
    let inputStream = Smithy.ByteStream.data(body.data(using: .utf8))

    // Store the text into an object in the Amazon S3 bucket.

    _ = try await s3Client.putObject(
        input: PutObjectInput(
            body: inputStream,
            bucket: bucketName,
            key: objectName
        )
    )

    // Return the name of the file

    return objectName
}
```

### Start the runtime
<a name="lambda-function-handler-run"></a>

The final step is to have a global statement that calls the example runtime's `run()` function:

```
try await runtime.run()
```

## Build and test locally
<a name="lambda-function-test"></a>

While you can test your Lambda function by adding it in the Lambda console, the Swift AWS Lambda Runtime provides an integrated Lambda server you can use for testing. This server accepts requests and dispatches them to your Lambda function. This integrated server is started automatically when you run your program locally.

In this example, the program is built and run with the Region set to `eu-west-1`, the bucket name set to `amzn-s3-demo-bucket`, and the local Lambda server enabled:

```
$ AWS_REGION=eu-west-1             \
BUCKET_NAME=amzn-s3-demo-bucket    \
swift run
```

After running this command, the Lambda function is available on the local server. Test it by opening another terminal session and using it to send a Lambda request to `http://127.0.0.1:7000/invoke`, or to port 7000 on `localhost`:

```
$ curl -X POST                          \
     --data '{"body":"This is the message to store on Amazon S3."}' \
		 http://127.0.0.1:7000/invoke
```

Upon success, a JSON object similar to this is returned:

```
{
  "req_id": "290935198005708",
  "body": "The Lambda function has successfully stored your data in S3 with name '1720098625801368.txt'"
}
```

You can remove the created object from your bucket using this AWS CLI command:

```
$ aws s3 rm s3://amzn-s3-demo-bucket/file-name
```

## Packaging and uploading the app
<a name="lambda-packaging"></a>

To use a Swift app as a Lambda function, compile it for an x86\$164 or ARM Linux target depending on the build machine's architecture. This may involve cross-compiling, so you may need to resolve dependency issues, even if they don't happen when building for your build system.

The Swift Lambda Runtime includes an `archive` command as a plugin for the Swift compiler. This plugin lets you cross-compile from macOS to Linux just using the standard `swift` command. The plugin uses a Docker container to build the Linux executable, so [you'll need Docker installed](https://docs.docker.com/desktop/install/mac-install/).

To build your app for use as a Lambda function:

1. Build the app using the SwiftPM `archive` plugin. This automatically selects the architecture based on that of your build machine (x86\$164 or ARM).

   ```
   $ swift package archive --allow-network-connections docker
   ```

   This creates a ZIP file containing the function executable, placing the output in `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/target-name/executable-name.zip`

1. Create a Lambda function using the method appropriate for your needs, such as:
   + [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/)
   + [AWS Command Line Interface User Guide](https://docs.aws.amazon.com/cli/latest/userguide/)
   + [AWS Cloud Development Kit (AWS CDK) Developer Guide](https://docs.aws.amazon.com/cdk/latest/guide/)
**Warning**  
Things to keep in mind when deploying the Lambda function:  
Use the same architecture (x86\$164 or ARM64) for your function and your binary.
Use the Amazon Linux 2 runtime.
Define any environment variables required by the function. In this example, the `BUCKET_NAME` variable needs to be set to the name of the bucket to write objects into.
Give your function the needed permissions to access AWS resources. For this example, the function needs IAM permission to use `PutObject` on the bucket specified by `BUCKET_NAME`.

1. Once you've created and deployed the Swift-based Lambda function, it should be ready to accept requests. You can invoke the function using the `[Invoke](https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html)` Lambda API.

   ```
   $ aws lambda invoke                     \
   --region eu-west-1                      \
   --function-name LambdaExample           \
   --cli-binary-format raw-in-base64-out   \
   --payload '{"body":"test message"}'     \
   output.json
   ```

   The file `output.json` contains the results of the invocation (or the error message injected by our code).

## Additional information
<a name="additional-information"></a>
+ [Swift AWS Lambda Runtime package on GitHub](https://github.com/swift-server/swift-aws-lambda-runtime)
+ [Swift AWS Lambda Events package on GitHub](https://github.com/swift-server/swift-aws-lambda-events)
+ [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/)
+ [AWS Lambda API Reference](https://docs.aws.amazon.com/lambda/latest/api/)

# Event streaming with AWS SDK for Swift
<a name="using-event-streaming"></a>

## Overview
<a name="event-streaming-overview"></a>

Some AWS services provide timely feedback about the state of a system or process by streaming to your application a series of events describing that state, if your application wants to receive them. Likewise, other services may be able to receive a stream of events from your application to provide needed data as it becomes available. The AWS SDK for Swift provides support for sending and receiving streams of events with services that support this feature.

This section of the guide demonstrates how to stream events to a service and receive events from a service, with an example that uses Amazon Transcribe to transcribe voice content from an audio file into text displayed on the screen.

## Event streaming example
<a name="event-streaming-example"></a>

This Amazon Transcribe example uses the [swift-argument-parser](https://github.com/apple/swift-argument-parser) package from Apple to parse the command line neatly, as well as the `AWSTranscribeStreaming` module from the AWS SDK for Swift.

The example's complete source code is [available on GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift/example_code/transcribe-streaming/transcribe-events).

### Importing modules
<a name="event-streaming-example-imports"></a>

The example begins by importing the modules it needs:

```
import ArgumentParser
import AWSClientRuntime
import AWSTranscribeStreaming
import Foundation
```

### Enum definition
<a name="event-streaming-example-enum"></a>

Then an enum is defined to represent the three audio formats Amazon Transcribe supports for streaming. These are used to match against the format specified on the command line using the **--format** option.:

```
/// Identify one of the media file formats supported by Amazon Transcribe.
enum TranscribeFormat: String, ExpressibleByArgument {
    case ogg = "ogg"
    case pcm = "pcm"
    case flac = "flac"
}
```

### Creating the audio stream
<a name="event-streaming-example-createaudiostream"></a>

A function named `createAudioStream()` returns an `[AsyncThrowingStream](https://developer.apple.com/documentation/swift/asyncthrowingstream)` that contains the audio file's contents, broken into 125ms chunks. The `AsyncThrowingStream` supplies audio data to Amazon Transcribe. The stream is specified as an input property when calling the client's `[startStreamTranscription(input:)](https://sdk.amazonaws.com/swift/api/awstranscribestreaming/latest/documentation/awstranscribestreaming/transcribestreamingclient/startstreamtranscription(input:))` function.

```
    /// Create and return an Amazon Transcribe audio stream from the file
    /// specified in the arguments.
    /// 
    /// - Throws: Errors from `TranscribeError`.
    ///
    /// - Returns: `AsyncThrowingStream<TranscribeStreamingClientTypes.AudioStream, Error>`
    func createAudioStream() async throws
                -> AsyncThrowingStream<TranscribeStreamingClientTypes.AudioStream, Error> {

        let fileURL: URL = URL(fileURLWithPath: path)
        let audioData = try Data(contentsOf: fileURL)

        // Properties defining the size of audio chunks and the total size of
        // the audio file in bytes. You should try to send chunks that last on
        // average 125 milliseconds.

        let chunkSizeInMilliseconds = 125.0
        let chunkSize = Int(chunkSizeInMilliseconds  / 1000.0 * Double(sampleRate) * 2.0)
        let audioDataSize = audioData.count

        // Create an audio stream from the source data. The stream's job is
        // to send the audio in chunks to Amazon Transcribe as
        // `AudioStream.audioevent` events.

        let audioStream = AsyncThrowingStream<TranscribeStreamingClientTypes.AudioStream,
                                Error> { continuation in
            Task {
                var currentStart = 0
                var currentEnd = min(chunkSize, audioDataSize - currentStart)

                // Generate and send chunks of audio data as `audioevent`
                // events until the entire file has been sent. Each event is
                // yielded to the SDK after being created.

                while currentStart < audioDataSize {
                    let dataChunk = audioData[currentStart ..< currentEnd]
                    
                    let audioEvent = TranscribeStreamingClientTypes.AudioStream.audioevent(
                        .init(audioChunk: dataChunk)
                    )
                    let yieldResult = continuation.yield(audioEvent)
                    switch yieldResult {
                        case .enqueued(_):
                            // The chunk was successfully enqueued into the
                            // stream. The `remaining` parameter estimates how
                            // much room is left in the queue, but is ignored here.
                            break
                        case .dropped(_):
                            // The chunk was dropped because the queue buffer
                            // is full. This will cause transcription errors.
                            print("Warning: Dropped audio! The transcription will be incomplete.")
                        case .terminated:
                            print("Audio stream terminated.")
                            continuation.finish()
                            return
                        default:
                            print("Warning: Unrecognized response during audio streaming.")
                    }

                    currentStart = currentEnd
                    currentEnd = min(currentStart + chunkSize, audioDataSize)
                }

                // Let the SDK's continuation block know the stream is over.

                continuation.finish()
            }
        }

        return audioStream
    }
```

This function returns an `AsyncThrowingStream<[TranscribeStreamingClientTypes.AudioStream](https://sdk.amazonaws.com/swift/api/awstranscribestreaming/latest/documentation/awstranscribestreaming/transcribestreamingclienttypes/audiostream), Error>`. This is a function that asynchronously generates chunks of audio data, yielding them to the caller, until there's no audio left to process.

The function begins by creating a Foundation `URL` from the path of the audio file. Then it reads the audio into a `Data` object (to support larger audio files, this would need to be changed to load the audio from disk in chunks). The size of each audio chunk to send to the SDK is calculated so it will hold 125 milliseconds of audio, and the total size of the audio file in bytes is obtained.

The audio stream is generated by iterating over the audio data, taking the next chunk of audio and creating a `[TranscribeStreamingClientTypes.AudioStream.audioevent](https://sdk.amazonaws.com/swift/api/awstranscribestreaming/latest/documentation/awstranscribestreaming/transcribestreamingclienttypes/audioevent)` that represents it. The event is sent to the SDK using the continuation object's `yield()` function. The yield result is checked to see if any problems occurred, such as the event being dropped because the event queue is full.

This continues until the last chunk of audio is sent; then the continuation's `finish()` function is executed to let the SDK know the file has been fully transmitted.

### Transcribing audio
<a name="event-streaming-example-transcription"></a>

Transcription is handled by the `transcribe()` function:

```
    /// Run the transcription process.
    ///
    /// - Throws: An error from `TranscribeError`.
    func transcribe(encoding: TranscribeStreamingClientTypes.MediaEncoding) async throws {
        // Create the Transcribe Streaming client.

        let client = TranscribeStreamingClient(
            config: try await TranscribeStreamingClient.TranscribeStreamingClientConfiguration(
                region: region
            )
        )

        // Start the transcription running on the audio stream.

        let output = try await client.startStreamTranscription(
            input: StartStreamTranscriptionInput(
                audioStream: try await createAudioStream(),
                languageCode: TranscribeStreamingClientTypes.LanguageCode(rawValue: lang),
                mediaEncoding: encoding,
                mediaSampleRateHertz: sampleRate
            )
        )

        // Iterate over the events in the returned transcript result stream.
        // Each `transcriptevent` contains a list of result fragments which
        // need to be concatenated together to build the final transcript.
        for try await event in output.transcriptResultStream! {
            switch event {
            case .transcriptevent(let event):
            for result in event.transcript?.results ?? [] {
                guard let transcript = result.alternatives?.first?.transcript else {
                    continue
                }

                // If showing partial results is enabled and the result is
                // partial, show it. Partial results may be incomplete, and
                // may be inaccurate, with upcoming audio making the
                // transcription complete or by giving more context to make
                // transcription make more sense.

                if (result.isPartial && showPartial) {
                    print("[Partial] \(transcript)")
                }

                // When the complete fragment of transcribed text is ready,
                // print it. This could just as easily be used to draw the
                // text as a subtitle over a playing video, though timing
                // would need to be managed.

                if !result.isPartial {
                    if (showPartial) {
                        print("[Final  ] ", terminator: "")
                    }
                    print(transcript)
                }
            }
            default:
                print("Error: Unexpected message from Amazon Transcribe:")
            }
        }
    }
```

This function first looks at the value of the **--format** option passed into the program on the command line and prepares a constant of type `TranscribeStreamingClientTypes.MediaEncoding` that indicates the format of the incoming audio. Then it calls `[client.startStreamTranscription(input:)](https://sdk.amazonaws.com/swift/api/awstranscribestreaming/latest/documentation/awstranscribestreaming/transcribestreamingclient/startstreamtranscription(input:))` to start the transcription process. The audio stream is specified by a function named `createAudioStream()`, which is [described below](#event-streaming-example-createaudiostream).

The event stream returned by `startStreamTranscription(input:)` is monitored using a `for await` loop. Each `transcriptevent` is handled by pulling the first available transcription from the [result stream](https://sdk.amazonaws.com/swift/api/awstranscribestreaming/latest/documentation/awstranscribestreaming/transcribestreamingclienttypes/transcriptresultstream). If the transcript is flagged as partial and the user specified the **--show-partial** option on the command line, the partial output is printed to the console.

If it's a completed transcription of a section of the audio, the transcription is output to the screen. The importance of checking the value of the result's `isPartial` property is simple: as chunks of audio are processed, they may contain partial words that need to be completed by referring to other chunks. Similarly, if a transcription's certainty is low, it might be higher if subsequent chunks provide additional context. For example, if the transcription includes the word "its," the following chunk may help determine if the word should actually be "it's" instead.

### Running the example
<a name="event-streaming-example-running"></a>

If you download and build the [complete example](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift/example_code/transcribe-streaming/transcribe-events), you can run it using the **tsevents** executable. For example, if you have a 44,100Hz audio file named `audio-sample.flac`, you can process it with the command:

```
$ tsevents --path audio-sample.flac --format flac --sample-rate 44100
```

If the language of the audio file isn't US English, you can specify the file's language using the **--lang** option. For example, for modern Arabic, you can use:

```
$ tsevents --path audio-sample.flac --format flac --sample-rate 44100 --lang ar-SA
```

For complete usage information, simply run the command **tsevents --help**.

## Additional information
<a name="additional-information"></a>
+ [Amazon Transcribe Developer Guide](https://docs.aws.amazon.com/transcribe/latest/dg/)
+ [Amazon Transcribe API Reference](https://docs.aws.amazon.com/transcribe/latest/APIReference/Welcome.html)

# Presigned URLs and requests with AWS SDK for Swift
<a name="using-presigning"></a>

## Overview
<a name="presigned-overview"></a>

You can presign certain AWS API operations in advance of their use to let the request be used at a later time without the need to provide credentials. With a presigned URL, the owner of a resource can grant an unauthorized person access to a resource for a limited time by simply sending the other user a presigned URL to use the resource.

## Presigning basics
<a name="presigned-basics"></a>

The AWS SDK for Swift provides functions that create presigned URLs or requests for each of the service actions that support presigning. These functions take as their `input` parameter the same input struct used by the unsigned action, plus an expiration time that restricts how long the presigned request will be valid and usable.

For example, to create a presigned request for the Amazon S3 action `GetObject`:

```
        let getInput = GetObjectInput(
            bucket: bucket,
            key: key
        )

        let presignedRequest: URLRequest
        do {
            presignedRequest = try await s3Client.presignedRequestForGetObject(
                input: getInput,
                expiration: TimeInterval(5 * 60)
            )
        } catch {
            throw TransferError.signingError
        }
```

This first creates a `[GetObjectInput](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/getobjectinput)` struct to identify the object to retrieve, then creates a Foundation `URLRequest` with the presigned request by calling the SDK for Swift function `[presignedRequestForGetObject(input: expiration:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client/presignedrequestforgetobject(input:expiration:))`, specifying the input struct and a five-minute expiration period. The resulting request can then be sent by anyone, up to five minutes after the request was created. The codebase that sends the request can be in a different application, and even written in a different programming language.

## Advanced presigning configuration
<a name="presigned-advanced"></a>

The SDK's input structs used to pass options into presignable operations have two methods you can call to generate presigned requests or URLs. For example, the `PutObjectInput` struct has the methods `[presign(config: expiration:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/putobjectinput/presign(config:expiration:))` and `[presignURL(config: expiration:)](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/putobjectinput/presignurl(config:expiration:))`. These are useful if you need to apply to the operation a configuration other than the one used when initializing the service client.

In this example, the `[AsyncHTTPClient](https://github.com/swift-server/async-http-client)` package from Apple's `swift-server` project is used to create an `HTTPClientRequest`, which in turn is used to send the file to Amazon S3. A custom configuration is created that lets the SDK make up to six attempts to send an object to Amazon S3:

```
        let fileData = try Data(contentsOf: fileURL)
        let dataStream = ByteStream.data(fileData)
        let presignedURL: URL

        // Create a presigned URL representing the `PutObject` request that
        // will upload the file to Amazon S3. If no URL is generated, a
        // `TransferError.signingError` is thrown.

        let putConfig = try await S3Client.S3ClientConfiguration(
            maxAttempts: 6,
            region: region
        )
        
        do {
            let url = try await PutObjectInput(
                body: dataStream,
                bucket: bucket,
                key: fileName
            ).presignURL(
                config: putConfig,
                expiration: TimeInterval(10 * 60)
            )

            guard let url = url else {
                throw TransferError.signingError
            }
            presignedURL = url
        } catch {
            throw TransferError.signingError
        }

        // Send the HTTP request and upload the file to Amazon S3.

        var request = HTTPClientRequest(url: presignedURL.absoluteString)
        request.method = .PUT
        request.body = .bytes(fileData)

        _ = try await HTTPClient.shared.execute(request, timeout: .seconds(5*60))
```

This creates a new `S3ClientConfiguration` struct with the value of `maxAttempts` set to 6, then creates a `[PutObjectInput](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/putobjectinput)` struct which is used to generate an URL that's presigned using the custom configuration. The presigned URL is a standard Foundation `URL` object.

To use the `AsyncHTTPClient` package to send the data to Amazon S3, an `HTTPClientRequest` is created using the presigned URL as the destination URL. The request's method is set to `.PUT`, indicating that it's an upload request. Then the request body is set to the contents of `fileData`, which contains the `Data` to be sent to Amazon S3.

Finally, the request is executed using the shared `HTTPClient` managed by `AsyncHTTPClient`.

# Integrating with Apple platforms using the AWS SDK for Swift
<a name="apple-integration"></a>

Software running on any of Apple's platforms (macOS, iOS, iPadOS, tvOS, visionOS, or watchOS) may wish to integrate into the Apple ecosystem. This may include, for example, letting the user authenticate to access AWS services using [Sign In With Apple](https://developer.apple.com/sign-in-with-apple/).

## Adding the AWS SDK for Swift to an existing Xcode project
<a name="getting-started-existing-xcode"></a>

If you have an existing Xcode project, you can add the SDK for Swift to it. Open your project's main configuration pane and choose the **Swift Packages** tab at the right end of the tab bar. The following image shows how to do this for an Xcode project called "Supergame," which will use Amazon S3 to get game data from a server.

 ****Swift Packages** tab in Xcode** 

![\[The location of the Swift Packages tab in Xcode.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-project-packages-tab.png)


This shows a list of the Swift packages currently in use by your project. If you haven't added any Swift packages, the list will be empty, as shown in the preceding image. To add the AWS SDK for Swift package to your project, choose the **\$1** button under the package list.

 **Find and select packages to import** 

![\[The package selection dialog box in Xcode.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-add-packages-panel.png)


Next, specify the package or packages to add to your project. You can choose from standard Apple-provided packages or enter the URL of a custom package in the search box at the top of the window. Enter the URL of the AWS SDK for Swift as follows: ` [https://github.com/awslabs/aws-sdk-swift.git](https://github.com/awslabs/aws-sdk-swift.git) `.

After you enter the SDK URL, you can configure version requirements and other options for the SDK package import.

 **Configure dependency rules for the SDK for Swift package** 

![\[The Xcode dependency rule configuration panel during package import.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-package-settings.png)


Configure the dependency rule. Make sure that **Add to Project** is set to your project — "Supergame" in this case — and choose **Add Package**. You will see a progress bar while the SDK and all its dependencies are processed and retrieved.

 **Fetching the AWS SDK for Swift package and its product list** 

![\[The Xcode Verifying aws-sdk-swift package window.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-verifying-package.png)


Next, select specific *products* from the AWS SDK for Swift package to include in your project. Each product is generally one AWS API or service. Each package is listed by package name, starting with `AWS` and followed by the shorthand name of the service or toolkit.

For the Supergame project, select `AWSS3`, `AWSDynamoDB`, and `AWSGameLift`. Assign them to the correct target (iOS in this example), and choose **Add Package**.

 **Choose package products for specific AWS services and toolkits** 

![\[The Choose Package Products dialog box in Xcode.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-package-products.png)


Your project is now configured to import the AWS SDK for Swift package and to include the desired APIs in the build for that target. To see a list of the AWS libraries, open the target's **General** tab and scroll down to **Frameworks, Libraries, and Embedded Content**.

 **AWS SDK for Swift libraries in the Xcode target** 

![\[The Xcode target's list of libraries, including AWS libraries.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-libraries-list.png)


If your project is a multi-platform project, you also need to add the AWS libraries to the other targets in your project. For each platform's target, navigate to the **Frameworks, Libraries, and Embedded Content** section under the **General** tab and choose **\$1** to open the library picker window.

Then, you can scroll to find and select all of the needed libraries and choose **Add** to add them all at once. Alternatively, you can search for and select each library, then choose **Add** to add them to the target one at a time.

 **Find and add SDK for Swift libraries using the Xcode library picker window** 

![\[Add SDK for Swift libraries by using the Xcode library picker.\]](http://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/images/xcode-library-picker.png)


You're now ready to import the libraries and any needed dependencies into individual Swift source code files and start using the AWS services in your project. Build your project by using the Xcode **Build** option in the **Product** menu.

## Authenticating using Sign In With Apple
<a name="apple-sign-in"></a>

One convenient way for users to sign into AWS while using your application by adding support for Sign In With Apple. This allows your users to access AWS services using their Apple ID and either TouchID or FaceID.

Adding Sign In With Apple support to your app requires planning and configuration of services:

1. Set up your application in Apple's "[Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list)" dashboard, or [configure Sign In With Apple using Xcode](https://developer.apple.com/documentation/authenticationservices/implementing-user-authentication-with-sign-in-with-apple).

1. Set up the application's security and authentication configuration in the [AWS Management Console](https://console.aws.amazon.com).

1. Add the Sign In With Apple authentication method to your application.

**Note**  
When setting up Sign In With Apple as a way to authenticate to use AWS services, the JWT audience should always be the same as your application's bundle ID as configured in Xcode (or the `Info.plist` file) and the Apple developer portal.

The rest of this section discusses the process of setting up and using Sign In With Apple for AWS authentication. The example authenticates to AWS using Sign In With Apple, then lists the user's Amazon Simple Storage Service (Amazon S3) buckets in a [SwiftUI](https://developer.apple.com/documentation/swiftui/) view. The example works on macOS, iOS, and iPadOS and is shown in part during the walkthrough below. The [complete example is on GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/df606a664bf2f7cfe3abc76c187e024451d0279c/swift/example_code/apple/Buckets).

### Configuring the app for Sign in With Apple
<a name="apple-sign-in-setup"></a>

If you use Xcode, you can [enable Sign In With Apple](https://developer.apple.com/documentation/authenticationservices/implementing-user-authentication-with-sign-in-with-apple) in the options for your application's main target.

1. Verify that your team name and bundle identifier are correct for the application. Be sure to use Apple's standard reverse-URI notation, such as `com.example.buckets`.

1. Click the **\$1 Capability** button to open a capability picker. Add Sign In With Apple to your target's capabilities.

1. Click the **Automatically manage signing** checkbox to enable automatic signing of your application. This also configures your application on the Apple developer portal.

1. Under **App Sandbox**, enable **Outgoing Connections (Client)** to have your signed application request network access permission.

If you don't use Xcode, or you prefer to configure the app by hand, you can do so by following the [instructions on Apple's developer website](https://developer.apple.com/documentation/sign_in_with_apple/configuring_your_environment_for_sign_in_with_apple).

### Configuring AWS services for your application
<a name="apple-sign-in-aws"></a>

Next, open the [IAM Management Console](https://console.aws.amazon.com/iam) to configure IAM to support authentication using the JSON Web Token (JWT) returned by Sign In With Apple after a sign in request.

1. In the sidebar, click **Identity providers**, then look to see if there's already a provider for `appleid.apple.com`. If there is, click on it to add your new application's bundle ID as a new audience. Otherwise, click **Add provider** to create a new identity provider with the following configuration:
   + Set the provider type to **OpenID Connect**.
   + Set the **Provider URL** to `https://appleid.apple.com`.
   + Set the audience to match your application's bundle ID, such as `com.example.buckets`.

1. If you don't already have a permissions policy set up to grant the permissions needed by your application (and no permissions it doesn't need):

   1. Click **Policies** in the [IAM Management Console](https://console.aws.amazon.com/iam) sidebar, then click **Create policy**.

   1. Add the permissions your application needs, or directly edit the JSON. The JSON for this example application looks like this:

------
#### [ JSON ]

****  

      ```
      {
          "Version":"2012-10-17",		 	 	 
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": "s3:ListAllMyBuckets",
                  "Resource": ["arn:aws:s3:::*"]
              }
          ]
      }
      ```

------

      This policy allows your application to get a list of your Amazon S3 buckets.

   1. On the **Review and create** page, set the **Policy name** to be a unique name to identify your policy, such as `example-buckets-app-policy`.

   1. Enter a helpful description for your policy, such as "Application permissions for the Sign In With Apple buckets example".

   1. Click **Create policy**.

1. Create a new role for your application:

   1. Click **Roles** in the [IAM Management Console](https://console.aws.amazon.com/iam) sidebar, then click **Create role**.

   1. Set the trusted entity type to **Web identity**.

   1. Under **Web identity**, select the Apple ID identity provider you created above.

   1. Select your application's bundle ID as the **Audience**.

   1. Under **Permissions policies**, choose the policy you created above.

   1. On the **Name review, and create** page, set a unique name for your new role, such as `example-buckets-app-role`, and enter a description for the role, such as "Permissions for the Buckets example."

   1. Review the **Trust policy** generated by the console. The JSON should resemble the following:

------
#### [ JSON ]

****  

      ```
      {
          "Version":"2012-10-17",		 	 	 
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": "sts:AssumeRoleWithWebIdentity",
                  "Principal": {
                      "Federated": "arn:aws:iam::111122223333:oidc-provider/appleid.apple.com"
                  },
                  "Condition": {
                      "StringEquals": {
                          "appleid.apple.com:aud": [
                              "com.example.buckets"
                          ]
                      }
                  }
              }
          ]
      }
      ```

------

      This indicates that the Apple ID identity provider used to process Sign In With Apple requests should be allowed to use the role if the web token's `aud` property (the audience) matches the application's bundle ID.

### Adding Sign In With Apple support to your application
<a name="apple-sign-in-code"></a>

Once both Apple and AWS know about your application and that Sign In With Apple should be allowed to authenticate the user for the desired services, the next step is to add a Sign In With Apple button and its supporting code to the application.

This section explains how an application can provide a Sign In With Apple option for authentication, using Apple's SwiftUI and Authentication Services libraries.

#### Adding a "Sign In With Apple" button
<a name="apple-sign-in-button"></a>

To include a Sign In With Apple button, the `AuthenticationServices` module needs to be imported along with SwiftUI:

```
import SwiftUI
import AuthenticationServices
```

In your SwiftUI sign-in view, embed a Sign In With Apple button. The `[SignInWithAppleButton](https://developer.apple.com/documentation/authenticationservices/signinwithapplebutton/)` is used to trigger and manage the Sign In With Apple process. This button calls a completion handler with a `Result` object, which indicates whether or not a valid Apple ID was authenticated:

```
                    // Show the "Sign In With Apple" button, using the
                    // `.continue` mode, which allows the user to create
                    // a new ID if they don't already have one. When SIWA
                    // is complete, the view model's `handleSignInResult()`
                    // function is called to turn the JWT token into AWS
                    // credentials.
                    SignInWithAppleButton(.continue) { request in
                        request.requestedScopes = [.email, .fullName ]
                    } onCompletion: { result in
                        Task {
                            do {
                                try await viewModel.handleSignInResult(result)
                            } catch BucketsAppError.signInWithAppleCanceled {
                                // The "error" is actually Sign In With Apple being
                                // canceled by the user, so end the sign in
                                // attempt.
                                return
                            } catch let error as BucketsAppError {
                                // Handle AWS errors.
                                viewModel.error = error
                                return
                            }
                        }
                    }
```

This example `SignInWithAppleButton` uses a function named `handleSignInResult()` as its completion handler. This function is passed a `Result` that contains an `ASAuthorization` object if Sign In With Apple succeeded. If sign in failed or was canceled, the `Result` contains an `Error` instead. If the error actually indicates that sign in was canceled by the user, the sign in attempt is ended. Actual errors are stored for display by a SwiftUI alert sheet.

**Note**  
By default, the token returned by Sign In With Apple expires one day from its creation time.

##### Processing the JSON Web Token
<a name="apple-sign-in-handler"></a>

A number of variables are used by the example application to store information related to the user and their sign-in session:

```
    /// The unique string assigned by Sign In With Apple for this login
    /// session. This ID is valid across application launches until it
    /// is signed out from Sign In With Apple.
    var userID = ""
    
    /// The user's email address.
    ///
    /// This is only returned by SIWA if the user has just created
    /// the app's SIWA account link. Otherwise, it's returned as `nil`
    /// by SIWA and must be retrieved from local storage if needed.
    var email = ""
    
    /// The user's family (last) name.
    ///
    /// This is only returned by SIWA if the user has just created
    /// the app's SIWA account link. Otherwise, it's returned as `nil`
    /// by SIWA and must be retrieved from local storage if needed.
    var familyName = ""
    
    /// The user's given (first) name.
    ///
    /// This is only returned by SIWA if the user has just created
    /// the app's SIWA account link. Otherwise, it's returned as `nil` by SIWA
    /// and must be retrieved from local storage if needed.
    var givenName = ""
    
    /// The AWS account number provided by the user.
    var awsAccountNumber = ""
    
    /// The AWS IAM role name given by the user.
    var awsIAMRoleName = ""
    
    /// The credential identity resolver created by the AWS SDK for
    /// Swift. This resolves temporary credentials using
    /// `AssumeRoleWithWebIdentity`.
    var identityResolver: STSWebIdentityAWSCredentialIdentityResolver? = nil
```

These are used to record the AWS account information (the AWS account number and the name of the IAM role to use, as entered by the user), as well as the user's information returned by Sign In With Apple. The `STSWebIdentityAWSCredentialIdentityResolver` is used to convert the JWT token into valid, temporary AWS credentials when creating a service client object.

The `handleSignInResult()` function looks like this:

```
    /// Called by the Sign In With Apple button when a JWT token has
    /// been returned by the Sign In With Apple service. This function
    /// in turn handles fetching AWS credentials using that token.
    ///
    /// - Parameters:
    ///   - result: The Swift `Result` object passed to the Sign In
    ///     With Apple button's `onCompletion` handler. If the sign
    ///     in request succeeded, this contains an `ASAuthorization`
    ///     object that contains the Apple ID sign in information.
    func handleSignInResult(_ result: Result<ASAuthorization, Error>) async throws {
        switch result {
        case .success(let auth):
            // Sign In With Apple returned a JWT identity token. Gather
            // the information it contains and prepare to convert the
            // token into AWS credentials.
            
            guard let credential = auth.credential as? ASAuthorizationAppleIDCredential,
                  let webToken = credential.identityToken,
                  let tokenString = String(data: webToken, encoding: .utf8)
            else {
                throw BucketsAppError.credentialsIncomplete
            }
            
            userID = credential.user
            
            // If the email field has a value, set the user's recorded email
            // address. Otherwise, keep the existing one.
            email = credential.email ?? self.email
            
            // Similarly, if the name is present in the credentials, use it.
            // Otherwise, the last known name is retained.
            if let name = credential.fullName {
                self.familyName = name.familyName ?? self.familyName
                self.givenName = name.givenName ?? self.givenName
            }
            
            // Use the JWT token to request a set of temporary AWS
            // credentials. Upon successful return, the
            // `credentialsProvider` can be used when configuring
            // any AWS service.
            
            try await authenticate(withWebIdentity: tokenString)
        case .failure(let error as ASAuthorizationError):
            if error.code == .canceled {
                throw BucketsAppError.signInWithAppleCanceled
            } else {
                throw BucketsAppError.signInWithAppleFailed
            }
        case .failure:
            throw BucketsAppError.signInWithAppleFailed
        }
        
        // Successfully signed in. Fetch the bucket list.
        do {
            try await self.getBucketList()
        } catch {
            throw BucketsAppError.bucketListMissing
        }
    }
```

If sign in is successful, the `Result` provides details about the authentication returned by Sign In With Apple. The authentication credential contains a JSON Web Token, as well as a user ID which is unique for the current session.

If the authentication represents a new link between Sign In With Apple and this application, it may contain the user's email address and full name. If it does, this function retrieves and stores that information. The email address and user name *will never be provided again* by Sign In With Apple.

**Note**  
Sign In With Apple only includes personally identifiable information (PII) the when the user first associates their Apple ID with your application. For all subsequent connections, Sign In With Apple only provides a unique user ID. If the application needs any of this PII, it's the app's responsibility to securely save it *locally* and *securely*. The example application stores the information in the Keychain. 

The JWT is converted to a string, which is passed to a function called `authenticate(withWebIdentity: region:)` to actually create the web identity resolver.

##### Creating the web identity credential identity resolver
<a name="apple-sign-in-create-identity-resolver.swift"></a>

The `authenticate(withWebIdentity: region:)` function performs credential identity resolution by first writing the token to a local file, then creating an object of type `STSWebIdentityAWSCredentialIdentityResolver`, specifying the stored web identity token file when doing so. This AWS Security Token Service (AWS STS) credential identity resolver is used when creating service clients.

After authenticating with Sign In With Apple, a function named `saveUserData()` is called to securely store the user's information in the Keychain. This lets the sign in screen automatically fill in the form fields, and lets the application remember the user's email address and name if available.

```
    /// Convert the given JWT identity token string into the temporary
    /// AWS credentials needed to allow this application to operate, as
    /// specified using the Apple Developer portal and the AWS Identity
    /// and Access Management (IAM) service.
    ///
    /// - Parameters:
    ///   - tokenString: The string version of the JWT identity token
    ///     returned by Sign In With Apple.
    ///   - region: An optional string specifying the AWS Region to
    ///     access. If not specified, "us-east-1" is assumed.
    func authenticate(withWebIdentity tokenString: String,
                      region: String = "us-east-1") async throws {
        // If the role is empty, pass `nil` to use the default role for
        // the user.
        
        let roleARN = "arn:aws:iam::\(awsAccountNumber):role/\(awsIAMRoleName)"
        
        // Use the AWS Security Token Service (STS) action
        // `AssumeRoleWithWebIdentity` to convert the JWT token into a
        // set of temporary AWS credentials. The first step: write the token
        // to disk so it can be used by the
        // `STSWebIdentityAWSCredentialIdentityResolver`.
        
        let tokenFileURL = createTokenFileURL()
        let tokenFilePath = tokenFileURL.path
        do {
            try tokenString.write(to: tokenFileURL, atomically: true, encoding: .utf8)
        } catch {
            throw BucketsAppError.tokenFileError()
        }
        
        // Create an identity resolver that uses the JWT token received
        // from Apple to create AWS credentials.
        
        do {
            identityResolver = try STSWebIdentityAWSCredentialIdentityResolver(
                region: region,
                roleArn: roleARN,
                roleSessionName: "BucketsExample",
                tokenFilePath: tokenFilePath
            )
        } catch {
            throw BucketsAppError.assumeRoleFailed
        }
        
        // Save the user's data securely to local storage so it's available
        // in the future.
        //
        // IMPORTANT: Any potential Personally Identifiable Information _must_
        // be saved securely, such as by using the Keychain or an appropriate
        // encrypting technique.
        
        saveUserData()
    }
```

**Important**  
The token is written to disk since the SDK expects to load the token from a file. This token file *must remain in place* until you have finished using the service client. When you're done using the client, delete the token file.

##### Creating a service client using the web identity credential identity resolver
<a name="apple-sign-in-credential-identity-provider.swift"></a>

To access an AWS service, create a client configuration object that includes the `awsCredentialIdentityResolver` property. This property's value should be the web identity credential identity resolver created by the `authorize(withWebIdentity: region:)` function:

```
    /// Fetches a list of the user's Amazon S3 buckets.
    ///
    /// The bucket names are stored in the view model's `bucketList`
    /// property.
    func getBucketList() async throws {
        // If there's no identity resolver yet, return without doing anything.
        guard let identityResolver = identityResolver else {
            return
        }

        // Create an Amazon S3 client configuration that uses the
        // credential identity resolver created from the JWT token
        // returned by Sign In With Apple.
        let config = try await S3Client.S3ClientConfiguration(
            awsCredentialIdentityResolver: identityResolver,
            region: "us-east-1"
        )
        let s3 = S3Client(config: config)
        
        let output = try await s3.listBuckets(
            input: ListBucketsInput()
        )
        
        guard let buckets = output.buckets else {
            throw BucketsAppError.bucketListMissing
        }
        
        // Add the names of all the buckets to `bucketList`. Each
        // name is stored as a new `IDString` for use with the SwiftUI
        // `List`.
        for bucket in buckets {
            self.bucketList.append(IDString(bucket.name ?? "<unknown>"))
        }
    }
```

This function creates an `[S3Client](https://sdk.amazonaws.com/swift/api/awss3/latest/documentation/awss3/s3client)` configured to use our web identity credential identity resolver. That client is then used to fetch a list of Amazon S3 buckets. The buckets' names are then added to the bucket list. The SwiftUI `List` view automatically refreshes to display the newly-added bucket names.