I tried the AWS SDK for Swift for one of the projects I was working on. Here are some notes to supplement with the minimal documentation the project has.

The latest version of the SDK as of this guide is 0.2.5. This guide should work for both iOS and macOS since the AWS libraries are said to be platform agnostic.

Adding the SDK to a Swift project

To add the package to the project in XCode, use the Add Packages option in the File menu. In the window that appears, enter the GitHub URL of the project to discover AWS packages.

https://github.com/awslabs/aws-sdk-swift

When prompt appears asking to choose the libraries to add, take your pick. For this example, we will be using the “CloudWatch Logs” client.

Ensure build targets have dependencies

I created a universal app and the dependencies were added only to the iOS app. It would be good to open the project window on XCode and check that the dependencies are listed for each of the targets.

My project has macOS and iOS as targets and I’ve ensured that the dependencies are in both. This section in the latest version of XCode is called “Frameworks, Libraries and Embedded Content”. If the libraries are missing, click on the plus sign and add them.

Import the libraries

As you type in class names, XCode would automatically suggest the libraries to import. So do not bother much about finding these manually for the libraries that you use. These are the XCode suggested imports for my code (and they work as expected 😃).

import AWSCloudWatchLogs
import AWSClientRuntime

Create a Credentials Provider config

All clients for the AWS Services in the SDK require a “credentials provider” object to be passed as input. This is just a fancy label for any kind of AWS credentials.

The most common credentials are the AWS Access Key and AWS Secret Key. the below snippet creates a new object with the relevant credentials.

let aws_config = AWSCredentialsProviderStaticConfig(
    accessKey: "REPLACE_THIS",
    secret: "REPLACE_THIS"
)

Create a client for the AWS Service

The next bunch of steps

  1. Create a config for the client we need (CloudWatch Logs in this case).
  2. Use the config to create a client for the AWS Service.
let client_config = try CloudWatchLogsClient.CloudWatchLogsClientConfiguration(
    region: "us-west-2",
    credentialsProvider: AWSCredentialsProvider.fromStatic(aws_config)
)

let client = CloudWatchLogsClient(config: client_config)

Make a request to fetch log groups

Payload for requests to AWS services are organized as input structs for different operations. For this operation, our input struct is DescribeLogGroupsInput. We do not have any special options to pass, so this will be an empty struct.

Our helper method is actually describeLogGroups according to the docs. If we did that the result would be available as response.logGroups. The snippet below has a bit more magic.

The “Paginated” helper

Most AWS SDKs now have auto-pagination. If we used describeLogGroups and there was more than one page of values, then the API response would include the first page of values, along with a pageToken as a reference to the net page to query.

If we used the describeLogGroupsPaginated function, then the SDK makes requests to fetch all values across pages in a loop (on-demand, you’ll see below). This is implemented with Swift’s new AsyncSequence protocol (WWDC 2021 video)

let response = client.describeLogGroupsPaginated(
  input: DescribeLogGroupsInput()
)

To print the responses, we could use a loop.

// Each item in the response would be a page of values.
// So we loop again to print each log event.
//
for try await page in response {
    if let logGroups = page.logGroups {
        for logGroup in logGroups {
            print("--- Received log line ---")
            print(logGroup)
        }
    }
}

AFAIK, any helper function in the AWS SDK that lists values/paginages would support another helper with the “Paginated” suffix like above.

The final piece

As our code has a few areas that might throw errors, we wrap all of it in a do-catch block for now.

do {
    let client_config = try CloudWatchLogsClient.CloudWatchLogsClientConfiguration(
        region: "us-west-2",
        credentialsProvider: AWSCredentialsProvider.fromStatic(aws_config)
    )
    let client = CloudWatchLogsClient(config: client_config)

    let response = client.describeLogGroupsPaginated(
      input: DescribeLogGroupsInput()
    )
    
    print("Printing output...")
    for try await page in response {
        if let logGroups = page.logGroups {
            for logGroup in logGroups {
                print("--- Received log line ---")
                print(logGroup)
            }
        }
    }
    print("-#-#- Call to AWS complete")
} catch {
    print("AWS init error")
}

Next steps

I have not yet tried to call a helper function that creates/destroys resources. So this post does not have any examples on how to handle the response for that. Will update this post in the future.