使用【超微型】的AWS AppSync + Amplify来创建iOS聊天应用

首先

这篇文章是and factory Advent Calendar 2020 的第四篇文章。昨天是@ykkd先生的Swiftlint自动修正代码!?

亚马逊云服务 AppSync / Amplify

我认为在移动应用程序的后端中,使用Firebase作为聊天应用程序的后端很常见,但AWS的托管服务有一个重大优点,就是与其他AWS服务的集成很容易。
虽然我自己是iOS工程师,但我对AWS提供的服务和构建很感兴趣,所以我决定这次尝试使用AppSync和Amplify。

AppSync是亚马逊提供的一项托管服务,旨在支持使用GraphQL进行服务开发。通过使用GraphQL中提供的订阅功能,可以相对轻松地创建聊天功能。
此外,Amplify是为客户端访问AppSync而提供的工具。通过在iOS中使用Amplify SDK,SDK将负责处理与AppSync之间的数据传递。

スクリーンショット 2020-12-04 1.26.02.png

这次我们将使用AWS AppSync + Amplify来快速创建一个超级简约的聊天应用程序。

放大器的设置

前提条件 tí

    • AWSアカウントを持っている

 

    • Amplifyのセットアップが完了している

Amplifyを実行するコマンドラインツールのセットアップ、Podのインストールを行います。
詳細はこちらを参照ください。

在Amplify的命令行工具中,执行以下操作。

Amplify的初始化

amplify init

之后会被询问项目名称等初始设置,我会进行回答。这次我回答如下。

? Enter a name for the project 
    -> SampleChatApp
? Enter a name for the environment
    -> dev
? Choose your default editor:
    -> Visual Studio Code
? Choose the type of app that you're building
    -> ios
? Do you want to use an AWS profile?
    -> Yes
? Please choose the profile you want to use
    -> default

✅ 放大设置成功完成。

API的设置

当你完成amplify的设置后,我们将使用命令行工具来设置API。

在项目根目录下执行以下命令,并根据确认内容逐步回答。

amplify add api

再次被問到。接下來是有關API的初設定。這次案例的答案如下所示。

? Please select from one of the below mentioned services: 
    -> GraphQL
? Provide API name:
    -> samplechatapp
? Choose the default authorization type for the API
    -> API key
? Enter a description for the API key:
    -> SampleChatApp's API key.
? After how many days from now the API key should expire (1-365):
    -> 7
? Do you want to configure advanced settings for the GraphQL API
    -> No, I am done.
? Do you have an annotated GraphQL schema?
    -> No
? Choose a schema template:
    -> Single object with fields (e.g., “Todo” with ID, name, description)


GraphQL schema compiled successfully.

? Do you want to edit the schema now?
    -> Yes // Yesとするとエディタが開き、スキーマを編集できる

模型定义

当回答最后一个关于创建API的问题回答为是后,编辑器将打开,以便可以编辑模式。
通过GraphQL,我们根据graphql文件中定义的模式来创建API。
在这里,我们简单地定义了以下消息模型:
它具有文本、创建日期(以毫秒级的纪元时间为基础)和最小限度的UserID属性。

type Message @model {
  id: ID!
  text: String!
  createdAt: String!
  user: String!
}

定义完模式之后,将之前创建的模式以及API的定义文件等本地资源推送到远程。

amplify push

一旦成功推送后,通常会有问题被提出。
基于这个回答,将决定用于访问基于该API创建的封装处理的类型和命名。

? Do you want to generate code for your newly created GraphQL API
    -> Yes
? Enter the file name pattern of graphql queries, mutations and subscriptions
    -> graphql/**/*.graphql
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscripti
ons
    -> Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested]
    -> 2
? Enter the file name for the generated code
    -> API.swift

执行到这一步,您可以使用Amplify的命令行工具创建API,
然后您可以在AWS控制台->服务->AppSync中进行确认。

スクリーンショット 2020-12-03 22.09.29.png

客户端实施

安装

将在amplify init时创建的两个json文件,即amplifyconfiguration.json和awsconfiguration.json,转移到Xcode项目中。

此外,在应用启动时,作为Amplify的设置过程,AppDelegate将执行以下操作。

import UIKit
import Amplify
import AmplifyPlugins

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Setup Amplify
        do {
            try Amplify.add(plugin: AWSAPIPlugin(modelRegistration: AmplifyModels()))
            try Amplify.configure()
        } catch {
            print("An error occurred setting up Amplify: \(error)")
        }

        // 略

        return true
    }
}

实施聊天功能

这是聊天功能的实施。

在viewDidLoad中获取保存在数据源中的消息

使用GraphQL的REST API的Get请求由query负责。利用Amplify SDK,我们可以执行Amplify.API.query(request:)来获取Message的数组。此外,通过结合指定limit和包含下一个值的nextToken,还可以执行分页操作。


    @IBOutlet private weak var tableView: UITableView!

    var messages: [Message] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        self.fetchMessage()
    }

    func fetchMessage() {
        // Amplify SDK経由でqueryオペレーションを実行しMessageの配列を取得
        Amplify.API.query(request: .list(Message.self, where: nil)) { event in
            switch event {
            case .success(let result):
                // GraphQLの場合、Query失敗時のerrorもレスポンスに含まれる
                switch result {
                case .success(let messages):
                    self.messages = messages
                    DispatchQueue.main.async {
                        // tableViewを更新
                        self.tableView.reloadData()
                    }
                case .failure(let error):
                    // サーバーから返されるエラーはこっち
                }
            case .failure(let error):
                // 通信エラー等の場合はこっち
            }
        }
    }

通过点击发送按钮来提交数据。

在GraphQL中,创建、修改等数据写入操作由mutate执行。


    @IBAction func tappedSendButton() {
        // キーボード閉じる
        self.textField.resignFirstResponder()

        // メッセージ内容
        guard let text = self.textField.text, !text.isEmpty else {
            return
        }
        // 送信時間を取得
        let createdAt = String(Date().timeIntervalSince1970)
        // 別管理しているUserID
        let user = UserIdRepositoryProvider.provide().getUserId()
        let message = Message(text: text, ts: ts, user: user!)

        // mutateで新規メッセージを作成
        Amplify.API.mutate(request: .create(message)) { event in
            switch event {
            case .success(let result):
                switch result {
                case .success(let message):
                    print("Successfully created the message: \(message)")
                case .failure(let graphQLError):
                    // サーバーからのエラーの場合はこっち
                    print("Failed to create graphql \(graphQLError)")
                }
            case .failure(let apiError):
                // 通信まわりなどのErrorになった場合はこっち
                print("Failed to create a message", apiError)
            }
        }

        // 初期化しておく
        self.textField.text = ""
    }

订阅数据源

最后,我们将实现实时结果的反映。
通过使用GraphQL的订阅功能,可以实现双向的套接字通信。
在Amplify中,通过执行Amplify.API.subscribe(),可以使数据源的更改能够响应式地反映出来。


    override func viewDidLoad() {
        super.viewDidLoad()
        self.fetchMessage()
        self.subscribeMessage()
    }

    func subscribeMessage() {
        // 新たなメッセージの作成を購読する
        subscription = Amplify.API.subscribe(request: .subscription(of: Message.self, type: .onCreate), valueListener: { (subscriptionEvent) in
            // 購読したイベント内容をチェック
            switch subscriptionEvent {

            // サブスクリプションの接続状態の変更を検知
            case .connection(let subscriptionConnectionState):
                print("Subscription connect state is \(subscriptionConnectionState)")

            // データの更新を検知
            case .data(let result):
                switch result {
                case .success(let createdMessage):
                    self.messages.append(createdMessage)
                    DispatchQueue.main.async {
                        // テーブル更新
                        self.tableView.reloadData()

                        // 最新のメッセージまでスクロール
                        let indexPath = IndexPath(row: self.messages.count - 1, section: 0)
                        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
                    }
                case .failure(let error):
                    print("Got failed result with \(error.errorDescription)")
                }
            }
        }) { result in
            switch result {
            case .success:
                print("Subscription has been closed successfully")
            case .failure(let apiError):
                print("Subscription has terminated with \(apiError)")
            }
        }
    }

暂时完成

好像做得很不错啊~

undefined

做不完的事情

虽然给人的感觉似乎有一些进展,但实际上这次排序并未成功。
(通过订阅可以按时间顺序获取内容,所以可以应付,但通过查询获取的内容会按照Key中的Message ID排序,导致顺序混乱)

Simulator Screen Shot - iPhone 11 - 2020-12-03 at 23.54.10.png

如果是在聊天中,自然希望按照时间顺序排列,但由于超简约的原因,请谅解。
(虽然可以通过修改schema.graphql并将createdAt指定为排序键,但对于通过Amplify SDK间接调用该语句,我不清楚应该如何调用它。如果有人有相关经验,请告诉我。)

最后

本文中完全没有提到,但是关于聊天功能

在本文中没有提到的是,关于聊天功能

    • 認証情報との紐付け

 

    • 送信開始時点で送信中というステータスがユーザーに伝わるようにする

 

    • 送信に失敗したときにユーザーに通知して再送信を促す

 

    • 送信中にアプリを落としても送信が行われるようにする

 

    • 画像や動画などコンテンツの拡充

 

    データの永続化をしてユーザービリティを高める

考虑到这些事情,越来越多的审查项目浮现出来,
我觉得要将多少责任交给SDK真是一个困难的问题。
虽然如此,关于Amplify iOS SDK,目前仍然处于调查阶段,
我还希望能够通过进一步的调试来加深我的理解。

非常感谢您一直坚持观看直到最后?‍♂️

bannerAds