AWS SDK for SwiftのGet Startedを試してS3バケットの一覧を取得してみた

こんにちは、AWS事業本部の荒平(@0Air)です。

Swift(iOS)でAWSの各リソースを触ってみたいなと思うことはありませんか?私は年10回くらい思います。
私はSwiftは2.xの頃に少し触れただけのズブの素人なので、今は何もかも変わってしまっていますが、いい感じのGet Startedがあったので試しに触ってみたいと思います。

以下のドキュメントに沿って確認してみます。

はじめに

  • 執筆時点で、上記ドキュメントはプレビューリリースです。記載する全ての内容は変更される場合があります
  • 実稼働での利用は推奨されていません
  • 筆者環境は、Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5) および、aws-sdk-swift v0.36.1 にて試しました
  • 最小バージョンの要件は Swift 5.7 および Xcode 14 です。

プロジェクトの作成

ドキュメントに従い、以下コマンドにてプロジェクトを作成します。

mkdir ListBuckets
cd ListBuckets
swift package init --type executable
mv Sources/main.swift Sources/entry.swift

ファイル構成は以下のようになります。

$ tree
.
├── Package.swift
└── Sources
    └── entry.swift

Package.swiftおよびentry.swiftはgitにて公開されていますので、そちらからコピーしてもよいと思います。(参考)

Package.swiftは以下のように記述しました。
特に15行目は、ご利用の環境に合わせてバージョンを変更してください。

// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "ListBuckets",
    platforms: [
        .macOS(.v11),
        .iOS(.v13)
    ],
    dependencies: [
        .package(
            url: "https://github.com/awslabs/aws-sdk-swift",
            from: "0.36.1"
        )
    ],
    targets: [
        .executableTarget(
            name: "ListBuckets-Simple",
            dependencies: [
                .product(name: "AWSS3", package: "aws-sdk-swift")
            ],
            path: "Sources")
    ]
)

entry.swiftは以下の通りです。
9行目region:は確認したいリージョンに変更します。

// The Swift Programming Language
// https://docs.swift.org/swift-book

import Foundation
import ClientRuntime
import AWSS3

func getBucketNames() async throws -> [String] {
    let client = try S3Client(region: "ap-northeast-1")
    let output = try await client.listBuckets(
        input: ListBucketsInput()
    )

    var bucketNames: [String] = []

    guard let buckets = output.buckets else {
        return bucketNames
    }
    for bucket in buckets {
        bucketNames.append(bucket.name ?? "<unknown>")
    }

    return bucketNames
}

@main
struct Main {
    static func main() async {
        do {
            let names = try await getBucketNames()

            print("Found \(names.count) buckets:")
            for name in names {
                print("  \(name)")
            }
        } catch let error as ServiceError {
            print("An Amazon S3 service error occurred: \(error.message ?? "No details available")")
        } catch {
            print("An unknown error occurred: \(dump(error))")
        }
    }
}

サンプルコードを実行してみる

それぞれファイルを変更して保存したら、ターミナルからswift buildにてビルドします。

$ swift build
Building for debugging...
[3/3] Linking ListBuckets-Simple
Build complete! (3.46s)

swift runにてコードを実行できますが、この際、ターミナルの認証情報を利用するため注意が必要です。
~/.aws/credentialsに認証情報をセットしていない場合は、一時的に以下のコマンドで設定します。

export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY"

そして実行すると、しっかりS3バケットの一覧が取得できました。

$swift run
Building for debugging...
Build complete! (0.25s)
2024-02-24T02:49:54+0900 info S3Client : [Logging] Request: GET https:443 
 Path: / 
 User-Agent: aws-sdk-swift/1.0 ua/2.0 api/s3#1.0 os/macos#14.2.1 lang/swift#5.9 cfg/retry-mode#legacy, 
X-Amz-Date: 20240223T174954Z, 
Host: s3.ap-northeast-1.amazonaws.com, 
x-amz-content-sha256: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, 
Authorization: AWS4-HMAC-SHA256 Credential=AKIAXXXXXXXXXXXXXXXX/20240223/ap-northeast-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
 Optional([ClientRuntime.SDKURLQueryItem(name: "x-id", value: Optional("ListBuckets"))])
2024-02-24T02:49:55+0900 info URLSessionHTTPClient : [Logging] urlSession(_:dataTask:didReceive:) called
2024-02-24T02:49:55+0900 info URLSessionHTTPClient : [Logging] urlSession(_:dataTask:didReceive:) called with 1936 bytes
2024-02-24T02:49:55+0900 info URLSessionHTTPClient : [Logging] urlSession(_:task:didCompleteWithError:) called. Success
Found 15 buckets:
  amazon-connect-xxxxxxx
  aws-glue-assets-xxxxxxx-ap-northeast-1
  backup-data-arap
  (省略)

(参考)既存プロジェクトに対して導入する場合

既存のプロジェクトに対してAWS SDK for Swiftパッケージをプロジェクトに追加します。

`https://github.com/awslabs/aws-sdk-swift` を検索欄に入力し、 "Add to Project"にて追加先のプロジェクトを指定、 "Add Package"にてパッケージをインポートします。

AWS SDK for Swiftのインポートが始まります。

少し待機するとサービス一覧が表示されるので、利用するサービス(例えば、AWSS3)をプロジェクトに追加指定します。

チュートリアルはここまで

・・・なのですが、やはりiOSの画面でも表示させたいなぁと思ったので、作ってみました。

コード例

以下、作成したサンプルコードです。3ファイルに分割しています。

Main.swift

import Foundation
import ClientRuntime
import AWSS3
import SwiftUI

func getBucketNames() async throws -> [String] {
    let client = try S3Client(region: "ap-northeast-1")
    let output = try await client.listBuckets(
        input: ListBucketsInput()
    )

    var bucketNames: [String] = []
    guard let buckets = output.buckets else {
        return bucketNames
    }
    for bucket in buckets {
        bucketNames.append(bucket.name ?? "<unknown>")
    }
    return bucketNames
}

@main
struct awssdkforswift: App {
    var body: some Scene {
        WindowGroup {
            BucketListView()
        }
    }
}

BucketListView.swift

import SwiftUI

struct BucketListView: View {
    @StateObject private var viewModel = BucketListViewModel()

    var body: some View {
        NavigationView {
            List(viewModel.bucketNames, id: \.self) { name in
                Text(name)
            }
            .navigationTitle("S3 Buckets")
            .onAppear {
                viewModel.fetchBucketNames()
            }
        }
    }
}

BucketListViewModel.swift

import SwiftUI
import Combine

class BucketListViewModel: ObservableObject {
    @Published var bucketNames: [String] = []
    @Published var isLoading = false
    @Published var errorMessage: String?

    func fetchBucketNames() {
        isLoading = true
        Task {
            do {
                let names = try await getBucketNames()
                DispatchQueue.main.async {
                    self.bucketNames = names
                    self.isLoading = false
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = error.localizedDescription
                    self.isLoading = false
                }
            }
        }
    }
}

最後に、適当なXcodeプロジェクトを新規作成し、作成したコードをプロジェクトにAddします。
(デバイスは、特にこだわりはありませんが、iPhone 15 Proを指定しました)

認証情報の設定

iOSで表示させるためには、XcodeにAWS操作用の認証情報を設定してあげる必要があります。
※ ここでは、検証のために環境変数で設定しますが、プロダクションでは非推奨です

Scheme > Edit Scheme から、スキーマ操作画面に移動します。

Runの環境変数を設定します。設定するのは以下の3変数です。

  • AWS_REGION
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

S3バケットの一覧が表示できた!

実行してみると、アプリ上からAmazon S3のバケット一覧を取得することができました。
辿り着くまで紆余曲折あったのですが、Xcodeはエラーメッセージが優しい(諸説あり)ので、なんとかなりました。

おわりに

実施内容は基本的ですが、AWS SDK for Swiftを利用すると色々できそうなことが分かりました。
今回は環境変数にアクセスキーを直で入れましたが、本来は別の認証方法を利用すべきなので、要改善です。

2021年終盤に登場し、まだプレビューリリースではありますが、直近でもかなり活発にコミットされていそうなので、幅広く遊べそうです。

このエントリが誰かの助けになれば幸いです。

それでは、AWS事業本部 コンサルティング部の荒平(@0Air)がお送りしました!

参考