使用AWS构建后端(一) —— Authentication & API(一)

版本记录

版本号 时间
V1.0 2020.11.13 星期五

前言

使用Amazon Web Services(AWS)为iOS应用构建后端,可以用来学习很多东西。下面我们就一起学习下如何使用Xcode Server

开始

首先我们看下主要内容:

了解如何使用GraphQLAmazon Web Services(AWS)AWS AmplifyCognito一起为iOS应用构建后端。内容来自翻译

接着看下写作环境:

Swift 5, iOS 14, Xcode 12

下面就是正文啦

Amazon Web Services(AWS)是一个云计算平台。为了支持云计算,亚马逊在全球拥有和运营数据中心。它“作为服务”提供各种基础架构和软件产品。例如,您可以使用Amazon EC2Amazon数据中心内保留虚拟服务器。或者,您可以使用Amazon SageMaker快速轻松地构建和部署机器学习模型。 AWS提供了近200种单独的服务,因此无论您对下一个项目有何需求,都可以找到!

在学习本教程的过程中,您将学习如何使用AWS Amplify将身份验证和数据库存储添加到名为Isolation Nation的聊天应用程序中。

这是高级教程。在开始之前,您应该对SwiftSwiftUI有一个很好的了解。您还应该对GraphQLCocoaPods有所了解。如果您需要先完成以上任何一项,请尝试以下教程:

现在开始!

Isolation Nation是一款针对因COVID-19而自我隔离的人的应用程序。 它使他们可以向当地社区的其他人寻求帮助。 Isolation Nation的工作方式是向用户询问其邮政编码(相当于英国的邮政编码),并将其添加到其邮政编码区域(postcode area)的线程thread中。 例如,白金汉宫的完整邮政编码为SW1A 1AA。 邮政编码区域(postcode area)SW1A,它代表此处here显示的区域。

邮政编码在同一区域内的用户被添加到单个线程中。 然后,他们可以发送消息并回复同一区域中的其他人。

打开入门项目。 在Xcode中打开IsolationNation入门项目的工作区(不是项目)。


Introduction to the App

构建并运行项目。 该应用程序显示带有单线程项SW1A的列表。 点击该项目。 该应用程序将导航到该位置的消息列表。

点击任何一条消息,以查看每条消息的答复列表。

该应用程序包含四个主屏幕:Home, Threads, Messages and Replies。 在Xcode中,您可以在“项目”导航器中看到每个屏幕的分组。 您还可以查看每个屏幕的view model。 这些不在他们的组中,因此更容易找到。

看一下该项目:

  • AppDelegate设置登录用户。
  • SceneDelegateRootView设置为SwiftUI视图层次结构的根视图。
  • RootView检查是否存在已登录用户,如果存在,则加载HomeScreen
  • HomeScreen加载ThreadsScreen

Threads, Messages and Replies屏幕均具有相似的结构。每个人都将其view model用作ObservedObject来填充其视图。

打开ThreadsScreenViewModel.swift。视图模型view model包含一个属性threadListState,该属性发布包装在Loading枚举中的ThreadModel对象数组。在初始化程序之后,perform(action :)定义一个API。该API允许视图将请求发送到视图模型以执行操作。这些动作的处理程序如下。

初次检查时,该应用可能看起来已经正常运行。但是请注意,fetchThreads()是如何简单地返回一个硬编码列表的。本教程的目的是构建功能齐全的后端并删除所有硬编码数据。

注意:如果使用多个模拟器,则在Isolation Nation中测试功能将更加容易。我使用Control Room创建一个与每个用户同名的模拟器。

首先,您需要注册一个AWS账户并将一些软件安装到您的计算机上。


Setting Up Your Environment

打开浏览器并转到the AWS Homepage

如果您已经拥有一个AWS账户,请登录。否则,请单击右上角的Create a AWS account,然后注册free tierAWS创建您的帐户后,使用您刚创建的凭证以Root user用户身份登录。

注意:您需要在注册时提供信用卡。 通过遵循本教程,您应该保持在免费限制之内。 但是,AWS可能会对您的产品使用收取少量费用。 完成本教程后,应删除创建的所有资源,以免产生任何其他费用。

现在该安装必备软件了。 打开终端。

首先,请确保您已安装Git。 它已经预装在每个现代macOS上,因此应该如此。 在您的终端中,键入以下内容:

git --version 

确保您的Git版本是2.14.1或更高版本。 如果没有,您可以在这里here安装。

接下来,通过在终端中运行以下命令来检查是否安装了Node v10或更高版本:

node --version

如果尚未安装,则可以在此处here安装。

最后,如果还没有,请在终端中运行以下命令来安装CocoaPods

sudo gem install cocoapods

接下来,您将安装并配置Amplify


AWS Amplify

Amplify包含三个独立但相关的产品:

  • 首先,有一个Command Line Interface (CLI),用于代表您的项目以编程方式创建和保留AWS资源。 AWS功能强大,但也很复杂。 Amplify使这个变的容易!
  • 其次,Amplify提供了针对几种流行编程环境(包括iOS)的库libraries。 这些库为常见的应用程序开发用例提供了简化的API。
  • 最后,Amplify提供了一组有限的UI components,用于快速建立常见的用户流,例如身份验证。 这些组件当前不适用于iOS。

1. Installing and Configuring Amplify

要开始使用Amplify,请在终端中键入以下内容以安装CLI

npm install -g @aws-amplify/cli amplify-app

-g标志表示CLI将在您的计算机上全局安装,而不仅仅是单个项目。

注意:这可能需要几分钟才能完成,有时似乎会挂起。 请耐心等待,它最终会完成。

安装CLI后,必须对其进行配置,以使其链接到您的AWS账户。 在终端中运行以下命令:

amplify configure

终端屏幕将要求您登录到您的AWS账户,然后将打开一个新的浏览器选项卡。 根据CLI的要求,按Enter键继续。 按Enter选择默认的AWS区域并为Amplify IAM用户键入用户名:

另一个浏览器选项卡将自动打开。 单击Next: Permissions ▸ Next: Tags ▸ Next: Review ▸ Create user

注意:在成功屏幕上,复制您的Access Key IDSecret access key,并将它们保存在安全的地方。 这很重要,因为关闭此选项卡后无法检索它们! 这些键将使CLI充当您最近创建的角色。

单击Close

返回终端,按照说明添加access key IDsecret access key

最后,创建一个配置文件名称:

恭喜你! 现在在您的计算机上设置了Amplify。 接下来,将其添加到您的应用中。

2. Adding Amplify to Your App

在Xcode中,打开Podfile。 在#Pods for IsolationHelp注释和文件末尾end之间,添加以下新依赖项:

pod 'Amplify'
pod 'Amplify/Tools'

接下来,在您的终端中,导航到项目目录的根目录。 键入以下内容以在项目中安装依赖项:

pod install --repo-update

Amplify是主要依赖项。 它为您的应用提供了对所有Amplify API的访问权限。 Amplify ToolsXcode的构建过程增加了各种自动化,从而使Amplify的工作更加轻松。

接下来,单击项目工作区中的IsolationNation项目,然后单击IsolationNation target

Build Phases选项卡中,单击plus按钮以添加另一个阶段。 选择New Run Script Phase

通过单击Run script标题,将阶段命名为Amplify Tools。 单击并将其拖动到Compile Sources阶段上方。

Shell脚本更新为以下内容:

"${PODS_ROOT}/AmplifyTools/amplify-tools.sh"

为确保所有Amplify CLI工具正常运行,请在终端中输入以下命令:

npm i --package-lock-only

注意:如果您使用nvm来管理节点版本,则前面的命令可能是不必要的。

建立您的专案。 构建完成后,项目导航器将具有一个名为AmplifyConfig的新组。 该文件夹包含包含Amplify的配置和资源标识符的文件。

接下来,在您的终端中,键入以下内容:

amplify init

Enter接受默认项目名称,然后选择None作为默认编辑器。 当询问您是否要使用配置文件时,键入Y,然后选择默认default配置文件。

这将需要一些时间来完成,因为CLI会为您创建AWS资源。

接下来,在您的终端中输入以下内容:

amplify console

这将在浏览器中打开Amplify Console。 如果您收到错误消息,指出您的项目不存在,请确保已选择北弗吉尼亚州(N. Virginia)地区。

此时,您可能需要环顾控制台以熟悉它。 不过,由于您尚未向应用程序添加任何服务,因此目前看不到什么。

但是,您将把Isolation Nation变成一个真正的应用程序! 第一步是增加对用户创建帐户和登录的支持。Amazon为此提供了一项名为Cognito的服务。 Cognito有一个User Pool,该池用作所有用户的目录。 您可以配置User Pool,以允许用户使用用户名和密码,社交身份提供商(例如GoogleFacebook)或企业安全系统(例如SAML)登录。


Configuring AWS Cognito

首先,在Xcode中打开Podfile,并在两个现有依赖项之后添加以下依赖项:

pod 'AmplifyPlugins/AWSCognitoAuthPlugin'

接下来,通过在终端中运行以下命令来安装依赖项:

pod install --repo-update

最后,使用Amplify CLI为您的项目配置Cognito。 在项目根目录的终端窗口中键入以下命令:

amplify add auth 

CLI提示您时,选择Default configuration ▸ Username ▸ No, I am done(每种情况下都是默认选项),然后等待Amplify CLI完成。

请务必注意,Amplify CLI现在已在本地为您的项目配置了Cognito,但尚未将该配置保存到云中。 您可以通过在终端中键入以下内容来确认这一点:

amplify status

这告诉您需要使用给定名称在Auth类别中创建资源。 在终端中输入以下内容,并在出现提示时进行确认:

amplify push

当询问您是否要为新API生成代码时,输入N

随着Amplify为您创建AWS资源,这可能需要几分钟的时间才能完成。

完成后,请返回浏览器中的Amplify Console。 选择您的应用,然后选择Backend environments选项卡。

现在,Authentication类别将出现在您的后端中。

单击Authentication链接。 然后,在Users部分中,单击View in Cognito按钮以查看Cognito User Pool

注意:如果AWS在屏幕上显示“Region not supported”,请单击Switch to US East (Virginia)

接下来,在左侧菜单中选择App client settings。 复制客户端ID并将其保存在某处。 稍后您将需要它。

现在,您的所有设置均已完成。 接下来,是时候将代码添加到您的应用中以处理身份验证了。


Adding Authentication With Amazon Cognito

打开AppDelegate.swift。 导入UIKit之后,在文件顶部,为Amplify添加导入:

import Amplify
import AmplifyPlugins

删除将userSession.loggedInUser设置为“ Lizzie”的行。

初始化authenticationService后,立即添加以下内容:

do {
  try Amplify.add(plugin: AWSCognitoAuthPlugin())
  try Amplify.configure()

  #if DEBUG
  Amplify.Logging.logLevel = .debug
  #else
  Amplify.Logging.logLevel = .error
  #endif
} catch {
  print("Error initializing Amplify. \(error)")
}

此代码使用Cognito身份验证插件配置Amplify库。 然后,它为Amplify设置适当的日志级别。

构建并运行。

不好了! 现在,该应用程序显示一个永无休止的旋转器! 显然,您尚未完成。

1. Completing the Authentication Service

打开AuthenticationService.swift。 在// MARK: Public API标记中,您会看到存根函数,其名称类似于signIn(as:identifiedBy :)checkAuthSession()。 现在是时候编写一些使用您的Cognito后端的代码了。

首先,在文件顶部添加一个新的导入:

import Amplify

接下来,找到空的checkAuthSession()并添加以下实现:

// 1
_ = Amplify.Auth.fetchAuthSession { [self] result in
  switch result {
  // 2
  case .failure(let error):
    logger.logError(error)
    signOut()

  // 3
  case .success(let session):
    if !session.isSignedIn {
      setUserSessionData(nil)
      return
    }

    // 4
    guard let authUser = Amplify.Auth.getCurrentUser() else {
      let authError = IsolationNationError.unexpctedAuthResponse
      logger.logError(authError)
      signOut()
      return
    }
    setUserSessionData(authUser.username)
  }
}

此代码的作用如下:

  • 1) 向Amplify请求当前身份验证会话。
  • 2) 如果有错误,请注销用户。
  • 3) 成功后,确认用户已登录。
  • 4) 如果用户已登录,则获取当前用户并在用户会话上设置详细信息。

构建并运行。 现在,加载旋转器已替换为登录屏幕。

接下来,添加登录代码。 删除signIn(as:identifiedBy:)中的所有代码,并将其替换为以下内容:

return Future { promise in
  // 1
  _ = Amplify.Auth
    .signIn(username: username, password: password) { [self] result in
    switch result {
    // 2
    case .failure(let error):
      logger.logError(error.localizedDescription)
      promise(.failure(error))
    // 3
    case .success:
      guard let authUser = Amplify.Auth.getCurrentUser() else {
        let authError = IsolationNationError.unexpctedAuthResponse
        logger.logError(authError)
        signOut()
        promise(.failure(authError))
        return
      }
      // 4
      setUserSessionData(authUser.username)
    }
  }
}

这是您正在做的:

  • 1) 调用Amplify登录API,并传递用户名和密码。
  • 2) 检查并处理失败。
  • 3) 成功后,获取当前登录用户。
  • 4) 像以前一样,在用户会话中设置用户的详细信息。

通过此设置,用户可以登录到您的应用程序!

但是,只有一个问题:您没有任何现有用户,并且仍然没有注册的方法。 是时候解决这个问题了。

用以下内容替换signUp(as:identifiedBy:with :)的正文:

return Future { promise in
  // 1
  let userAttributes = [AuthUserAttribute(.email, value: email)]
  let options = AuthSignUpRequest.Options(userAttributes: userAttributes)
  // 2
  _ = Amplify.Auth.signUp(
    username: username, 
    password: password, 
    options: options
  ) { [self] result in
    DispatchQueue.main.async {
      switch result {
      case .failure(let error):
        logger.logError(error.localizedDescription)
        promise(.failure(error))
      case .success(let amplifyResult):
        // 3
        if case .confirmUser = amplifyResult.nextStep {
          promise(.success(.awaitingConfirmation(username, password)))
        } else {
          let error = IsolationNationError.unexpctedAuthResponse
          logger.logError(error.localizedDescription)
          promise(.failure(error))
        }
      }
    }
  }
}

在此代码中,您将执行以下操作:

  • 1) 配置注册请求以期望通过电子邮件注册。
  • 2) 使用Amplify执行注册。 您可以像前面的示例一样处理结果。
  • 3) 如果注册成功,则返回awaitingConfirmation状态。 Amplify将通过电子邮件向用户发送代码,以确认所提供地址的所有权。

接下来,您需要允许用户确认其电子邮件地址。 用以下内容替换confirmSignUp(for:with:confirmedBy :)的内容:

return Future { promise in
  // 1
  _ = Amplify.Auth.confirmSignUp(
    for: username, 
    confirmationCode: confirmationCode
  ) { [self] result in
    switch result {
    case .failure(let error):
      logger.logError(error.localizedDescription)
      promise(.failure(error))
    case .success:
      // 2
      _ = Amplify.Auth.signIn(
        username: username, 
        password: password
      ) { result in
        switch result {
        case .failure(let error):
          logger.logError(error.localizedDescription)
          promise(.failure(error))
        case .success:
          // 3
          checkAuthSession()
        }
      }
    }
  }
}

在此代码中,您验证用户:

  • 1) 使用Amplify确认注册并以通常的方式处理响应。
  • 2) 成功后,请登录用户。
  • 3) 调用checkAuthSession(),它设置用户会话。

更新signOut()以使用户退出Cognito并清除其用户会话。 将用户会话(user session)设置为nil后,添加以下代码:

_ = Amplify.Auth.signOut { [self] result in
  switch result {
  case .failure(let error):
    logger.logError(error)
  default:
    break
  }
}

最后,打开AppDelegate.swift。 在return true之前,将以下内容添加到application(_:didFinishLaunchingWithOptions :)的底部:

// Listen to auth changes
_ = Amplify.Hub.listen(to: .auth) { payload in
  switch payload.eventName {
  case HubPayload.EventName.Auth.sessionExpired:
    authenticationService.checkAuthSession()
  default:
    break
  }
}

默认情况下,从Cognito返回的身份验证令牌会在一个小时后过期。 但是您可以扩展它而无需要求用户再次登录。 这就是这里发生的事情。

构建并运行。 这次,点击Sign Up按钮,然后使用用户名,电子邮件地址和密码进行注册。

注意:大多数电子邮件提供商都会允许您为每个电子邮件地址添加无限数量的后缀。 例如,如果您的电子邮件地址为example @ gmail.comexample + lizzie @ gmail.com也将路由到您的收件箱。 这是生成测试电子邮件地址的好方法。

注意:默认的Cognito用户池设置的密码策略为8个字符或更多。

AWS将通过电子邮件将确认码发送到您提供的地址。 到达后,输入并点击Confirm

恭喜你! 您已成功登录该应用程序。现在,使用同一用户注销并重新登录,以验证您的代码是否正常运行。

接下来,确认您的用户现在出现在云中。 在浏览器中,转到以前的Cognito选项卡。 单击左侧菜单中的Users and groups选项。 从列表中选择您的用户(一个用户)。

找到sub field并将其保存在某处。稍后您将需要它。


GraphQL, AppSync and DynamoDB

在您的应用程序中提供登录功能是一个好的开始。但是大多数产品还需要用户修改和保存数据的方法。对于许多现代应用程序,此数据需要其他用户访问。解决此问题的常见方法是将数据持久存储在云中某处服务器上存储的数据库中。

AWS提供了许多不同的database services,每种服务都有其自身的优势和取舍。在本教程中,您将使用文档数据库DynamoDB。使用DynamoDB,您无需预先定义架构。此功能可帮助您快速为您的应用制作原型或迭代新想法。

许多移动应用程序使用一种通用的架构模式,其中业务和安全逻辑驻留在后端服务器中。该应用程序通过GraphQLREST等网络API与服务器通信。服务器将保存,检索或更新数据库中的记录。

AppSync是一项AWS服务,可使用您的GraphQL schema自动生成数据库和后端。您在GraphQL schema中定义模型对象,然后AppSync为后端生成代码。然后,它部署运行后端所需的服务。并创建保存数据所需的DynamoDB表。


Adding a GraphQL API

返回Xcode,打开Podfile并添加以下依赖项:

pod 'AmplifyPlugins/AWSAPIPlugin'

AWSAPIPlugin将对AppSyncAmplify库添加了支持。 通过从命令行运行以下命令来更新工作空间:

pod install --repo-update

您要添加的第一个模型对象是一个User对象。 通常,Cognito处理用户数据。 但是Cognito的数据是个人专有的。 并且,在Isolation Nation应用程序中,用户必须能够看到彼此的数据,例如用户名。 因此,此应用需要用户模型以及Cognito数据。

1. Defining Your Schema

Xcode中,打开AmplifyConfig组中的schema.graphql。 将内容替换为以下内容:

# 1
type User
  # 4
  @model
{
  # 2
  id: ID!
  username: String!
  # 3
  sub: String!
  postcode: String
  createdAt: AWSDateTime!
}

这是您正在做的事情:

  • 1) 您声明一个User类型。
  • 2) 您可以将用户类型的各种字段定义为code: Type元组。例如,id字段的类型为ID,而postcode字段的类型为String运算符表示必须输入类型。
  • 3) sub字段将包含Cognito的sub记录。这是用户的唯一标识符。
  • 4) 您使用@model指令注释user类型。

如果您熟悉GraphQL,则其中的大部分看起来都很简单。但是,AppSync有一些独特之处。

AppSync使用directives来提供声明性API,该API可让您指定希望AppSync配置每种类型或字段的方式。在此示例中,您指定您的用户类型是model。这向AppSync指示应为此类型创建DynamoDB表。

注意:AppSync大量使用 GraphQL Directives。您可以在此处找到指令的完整列表。

早先在项目中添加Amplify Tools作为依赖项时,Amplify代表您创建了一个API。但是,这是在向项目添加身份验证之前。您的API对您刚创建的Cognito用户池一无所知。在继续之前,您需要修复该问题。

2. Generating the Database

在您的终端中,运行以下命令:

amplify api update

出现提示时,选择GraphQL ▸ Update auth settings ▸ Amazon Cognito User Pool,然后选择N

接下来,打开amplifytools.xcconfig。 通过添加代码来覆盖选项:

push=true
modelgen=true
profile=default
envName=dev

在这里,您指示Amplify Tools脚本采取以下操作:

  • 在运行时将更改推送到云。
  • Swift中为您的@model类型生成模型。
  • 使用默认的AWS配置文件和之前创建的开发环境。

在Xcode中构建您的项目。 如果您打开Report导航器,则会看到Amplify Tools脚本现在正在代表您生成Swift模型。

此构建将花费很长时间! 这是因为脚本还正在运行amplify push,以在云中生成所有必需的资源。

构建完成后,打开“项目”导航器。 确认已将新组添加到您的项目中,并且该组包含为User模型生成的模型文件。

在浏览器中,返回到Amplify Console并重新加载页面。 选择Backend environments,然后单击API链接。

在“ API”页面上,请注意“数据源”部分下添加了UserTable。 单击View in AppSync按钮。

AppSync页面上,打开Run a query,然后单击Run a query按钮。

这将打开一个GraphQL Playground,允许您针对GraphQL API运行实时查询和变异。 单击Login with User Pools按钮,然后使用您先前复制的Client ID和为用户创建的凭据登录。 按Login

3. Writing Data

接下来,您将创建一个User模型。 首先,如果Explorer窗格已打开,请单击“ X”将其关闭。现在,在最左侧的窗格中,添加以下代码,将用户名用作username,并将先前复制的sub用作idsub字段:

mutation CreateUserMutation {
  createUser(input:{
    id: "389b0b66-f0e3-4907-9b4e-01ac4146bb0b"
    username: "Lizzie",
    sub: "389b0b66-f0e3-4907-9b4e-01ac4146bb0b",
  }) {
    id
    username
    sub
    createdAt
  }
}

不幸的是,您无法在GraphQL代码中添加注释。 以下行号是指以下屏幕截图中的行号。

  • 在第1行,您定义了一个名为CreateUserMutation的命名突变。
  • 在第2-5行中,您运行GraphQL schemaAppSync生成的createUser变异。 变异采用一个称为input的单个参数。 此参数包含您要创建的username and sub
  • 6-9行定义了对改变的响应。 您指定idusername
    subcreatedAt应该在响应中返回。 这些是已创建用户的所有字段。

现在,按下橙色的Play按钮以运行改变并创建您的用户。

您的改变将发送到您的AppSync GraphQL服务器,并且响应将显示在中间列中。

接下来,在左侧菜单中选择Data Sources。 然后为您的UserTable选择DynamoDB Resource

您的浏览器将打开一个新tab。 您将看到新的DynamoDB数据库表。 选择Items tab

Items tab显示单个记录。 这是您刚创建的用户。 现在,单击记录ID链接以为您的用户打开数据库记录。

恭喜你! 您刚刚在DynamoDB中成功创建了第一个数据库记录。


Reading and Writing AppSync Data From Swift

Playground中运行GraphQL改变很有趣-但没有从您的应用程序中运行它们有趣!

切换回Xcode并打开AppDelegate.swift。 在调用Amplify.configure()之前,添加以下行:

try Amplify.add(plugin: AWSAPIPlugin(modelRegistration: AmplifyModels()))

这告诉Amplify库通过API插件添加对AppSync的支持,并注册从GraphQL模式创建的模型。 当前,这只是您的User模型。


Reading Data From AppSync

此时,UserSession对象只是代表用户名的字符串。 在下一部分中,您将更新应用,以从User模型中检索用户数据。 您将使用AppSync读取DynamoDB数据库。 这将需要大量的重构,因此在本节介绍的过程中,如果您看到Xcode错误,请不要担心。

打开UserSession.swift并更新loggingInUser的两个类型声明。 从String?更改为User?

public final class UserSession: ObservableObject {
  @Published var loaded = false
  // Here
  @Published var loggedInUser: User? {
    didSet {
      loaded = true
    }
  }

  init() {}

  // Here
  init(loggedInUser: User?) {
    self.loggedInUser = loggedInUser
  }
}

接下来,打开AuthenticationService.swift。 在setUserSessionData(_ :)之后添加以下方法:

private func fetchUserModel(id: String) -> Future<User, Error> {
  // 1
  return Future { promise in
    // 2
    _ = Amplify.API.query(request: .get(User.self, byId: id)) { [self] event in
      // 3
      switch event {
      case .failure(let error):
        logger.logError(error.localizedDescription)
        promise(.failure(error))
        return
      case .success(let result):
        // 4
        switch result {
        case .failure(let resultError):
          logger.logError(resultError.localizedDescription)
          promise(.failure(resultError))
          return
        case .success(let user):
          // 5
          guard let user = user else {
            let error = IsolationNationError.unexpectedGraphQLData
            logger.logError(error.localizedDescription)
            promise(.failure(error))
            return
          }
          promise(.success(user))
        }
      }
    }
  }
}

乍一看可能看起来有些吓人,但实际上并没有太多:

  • 1) 首先,此函数返回一个Future,它向成功完成回调中提供User
  • 2) 您使用Amplify API来运行查询。 该查询将通过其ID检索User对象。
  • 3) 该API将事件监听器关闭作为其最终参数。 您使用网络请求的结果发出调用,该请求可以成功也可以失败。 失败时,在返回失败之前记录错误。
  • 4) 如果网络请求成功,则检查基础的GraphQL结果类型。 这仍然可能导致失败,例如无效请求,因此必须再次检查错误。
  • 5) 如果一切成功,则确认您已收到ID的有效用户。 如果是这样,则将其退回。

现在,更新setUserSessionData(_ :)以使用一个User而不是一个String

private func setUserSessionData(_ user: User?) {
  DispatchQueue.main.async {
    if let user = user {
      self.userSession.loggedInUser = user
    } else {
      self.userSession.loggedInUser = nil
    }
  }
}

然后,在checkAuthSession()中,将对setUserSessionData(authUser.username)的调用替换为以下内容:

let sub = authUser.userId
cancellable = fetchUserModel(id: sub)
  .sink(receiveCompletion: { completion in
    switch completion {
    case .failure(let error):
      logger.logError(error)
      signOut()
    case .finished: ()
    }
  }, receiveValue: { user in
    setUserSessionData(user)
  })

此代码调用您刚刚编写的fetchUserModel(id :)方法。 成功后,它将设置与用户的用户会话。

同样,在signIn(as:identifiedBy :)中,将对setUserSessionData(_ :)的调用替换为以下内容:

cancellable = self.fetchUserModel(id: authUser.userId)
  .sink(receiveCompletion: { completion in
    switch completion {
    case .failure(let error):
      signOut()
      promise(.failure(error))
    case .finished: ()
    }
  }, receiveValue: { user in
    setUserSessionData(user)
    promise(.success(.signedIn))
  })

最后,打开RootView.swift。 在第62行中更新HomeScreenViewModel初始化程序以使用新的UserModel

model: HomeScreenViewModel(
  userID: loggedInUser.sub, 
  username: loggedInUser.username)

构建并运行。 如果您尚未登录,请立即登录。 确认该应用程序仍将您带到Locations屏幕。

用户界面中没有任何变化。 但是您的应用程序现在正在使用AppSyncDynamoDB数据库中查询正确的用户User模型!

注意:如果您感到好奇,可以通过嗅探应用程序中的HTTP流量来证明您正在从数据库中检索用户。 您可以使用像 Charles Proxy这样的工具。


Creating Data in DynamoDB

之前,您通过在GraphQL playground上运行一个改变在DynamoDB中创建了一个新的User记录。 您为您的一位用户硬编码了信息。 显然,这不是一个好的长期解决方案! 相反,您应该使用Amplify.API。 您现在就进行更改。

打开AuthenticationService.swift并在confirmSignUp(for:with:confirmedBy :)中找到成功处理程序。 删除对checkAuthSession()的调用,并将其替换为以下内容:

// 1         
guard let authUser = Amplify.Auth.getCurrentUser() else { 
  let authError = IsolationNationError.unexpctedAuthResponse         
  logger.logError(authError)        
  promise(.failure(IsolationNationError.unexpctedAuthResponse))      
  signOut()         
  return         
}        
// 2         
let sub = authUser.userId        
let user = User(         
  id: sub,        
  username: username,         
  sub: sub,       
  postcode: nil,      
  createdAt: Temporal.DateTime.now()      
)        
// 3         
_ = Amplify.API.mutate(request: .create(user)) { event in        
  switch event {         
  // 4       
  case .failure(let error):      
    signOut()       
    promise(.failure(error))         
  case .success(let result):         
    switch result {      
    case .failure(let error):      
      signOut()         
      promise(.failure(error))       
    case .success(let user):         
      // 5       
      setUserSessionData(user)      
      promise(.success(.signedIn))
    }                
  }      
}    

这是您的代码的作用:

  • 1) 首先,您可以从Amplify Auth API获取当前用户。 如果没有用户登录,则返回错误并退出。
  • 2) 您创建一个新的User模型对象,为您的用户设置username。 您可以将Cognitoidsub都设置为userId
  • 3) 然后,通过使用create请求类型调用Amplify.API.mutate API,将此用户模型记录写入DynamoDB
  • 4) 如前面的示例一样,您可以从网络层处理失败,然后从GraphQL层处理故障。
  • 5) 最后,将用户会话设置为新创建的用户并返回成功的登录。

在不同的模拟器上构建并运行该应用程序。 注册为新用户。 通过刷新浏览器的DynamoDB选项卡中的表,确认新用户记录出现在DynamoDB中。

您可以参考Amplify Framework Documentation,以了解有关通过Amplify可用的AWS服务的更多信息。 或查看本教程的第2部分, Using AWS as a Back End: The Data Store & Analytics。 在其中,您将学习如何使用DataStore API通过实时更新和用户分析来构建其余的Isolation Nation聊天应用程序。

注意:不要忘记删除您创建的AWS资源以避免收费。

后记

本篇主要讲述了Xcode Server的安装和配置,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,135评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,317评论 3 397
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,596评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,481评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,492评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,153评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,737评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,657评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,193评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,276评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,420评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,093评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,783评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,262评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,787评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,427评论 2 359

推荐阅读更多精彩内容