SwiftUI框架詳細(xì)解析 (二十二) —— 基于SwiftUI的AWS AppSync框架的使用(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2021.01.12 星期二

前言

今天翻閱蘋果的API文檔,發(fā)現(xiàn)多了一個(gè)框架SwiftUI,這里我們就一起來看一下這個(gè)框架哆姻。感興趣的看下面幾篇文章芙沥。
1. SwiftUI框架詳細(xì)解析 (一) —— 基本概覽(一)
2. SwiftUI框架詳細(xì)解析 (二) —— 基于SwiftUI的閃屏頁的創(chuàng)建(一)
3. SwiftUI框架詳細(xì)解析 (三) —— 基于SwiftUI的閃屏頁的創(chuàng)建(二)
4. SwiftUI框架詳細(xì)解析 (四) —— 使用SwiftUI進(jìn)行蘋果登錄(一)
5. SwiftUI框架詳細(xì)解析 (五) —— 使用SwiftUI進(jìn)行蘋果登錄(二)
6. SwiftUI框架詳細(xì)解析 (六) —— 基于SwiftUI的導(dǎo)航的實(shí)現(xiàn)(一)
7. SwiftUI框架詳細(xì)解析 (七) —— 基于SwiftUI的導(dǎo)航的實(shí)現(xiàn)(二)
8. SwiftUI框架詳細(xì)解析 (八) —— 基于SwiftUI的動(dòng)畫的實(shí)現(xiàn)(一)
9. SwiftUI框架詳細(xì)解析 (九) —— 基于SwiftUI的動(dòng)畫的實(shí)現(xiàn)(二)
10. SwiftUI框架詳細(xì)解析 (十) —— 基于SwiftUI構(gòu)建各種自定義圖表(一)
11. SwiftUI框架詳細(xì)解析 (十一) —— 基于SwiftUI構(gòu)建各種自定義圖表(二)
12. SwiftUI框架詳細(xì)解析 (十二) —— 基于SwiftUI創(chuàng)建Mind-Map UI(一)
13. SwiftUI框架詳細(xì)解析 (十三) —— 基于SwiftUI創(chuàng)建Mind-Map UI(二)
14. SwiftUI框架詳細(xì)解析 (十四) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(一)
15. SwiftUI框架詳細(xì)解析 (十五) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(二)
16. SwiftUI框架詳細(xì)解析 (十六) —— 基于SwiftUI簡單App的Dependency Injection應(yīng)用(一)
17. SwiftUI框架詳細(xì)解析 (十七) —— 基于SwiftUI簡單App的Dependency Injection應(yīng)用(二)
18. SwiftUI框架詳細(xì)解析 (十八) —— Firebase Remote Config教程(一)
19. SwiftUI框架詳細(xì)解析 (十九) —— Firebase Remote Config教程(二)
20. SwiftUI框架詳細(xì)解析 (二十) —— 基于SwiftUI的Document-Based App的創(chuàng)建(一)
21. SwiftUI框架詳細(xì)解析 (二十一) —— 基于SwiftUI的Document-Based App的創(chuàng)建(二)

開始

首先看下主要內(nèi)容:

了解如何使用AWS AppSync框架以簡單且類型安全的方式在SwiftUIiOS應(yīng)用中使用GraphQL API。內(nèi)容來自翻譯京办。

接著看下寫作環(huán)境:

Swift 5, iOS 14, Xcode 12

最后看正文掀序。

如今,大多數(shù)應(yīng)用程序都需要后端才能工作惭婿。需要用戶帳戶嗎?為此财饥,您需要一個(gè)后端。要跨設(shè)備同步數(shù)據(jù)嗎沾瓦?猜猜是什么贯莺,您需要一個(gè)后端宁改。有針對(duì)性的推送通知?后端……您明白了爹耗。

您可能之前曾聽說過縮寫BaaS (Back end as a service)豁鲤。BaaS工具通過使用簡單的API與云存儲(chǔ)服務(wù)集成。配置完成后鲸沮,這些BaaS服務(wù)將以與其他任何API相同的方式運(yùn)行琳骡,而幾乎不需要或不需要任何前端知識(shí)。

在本教程中讼溺,您將使用Amazon的名為AppSyncBaaS產(chǎn)品以及Amplify框架向iOS應(yīng)用添加后端組件楣号。您將學(xué)習(xí)如何:

  • 安裝AWS Amplify及其依賴項(xiàng)
  • 使用GraphQL實(shí)現(xiàn)模型并使用Amplify生成本地文件
  • 對(duì)數(shù)據(jù)執(zhí)行CRUD操作
  • 將您的應(yīng)用程序數(shù)據(jù)保存到AWS AppSync

您將通過使用SwiftUI實(shí)現(xiàn)待辦事項(xiàng)應(yīng)用程序來學(xué)習(xí)所有這些內(nèi)容。該應(yīng)用程序?qū)⒃试S您創(chuàng)建怒坯,刪除和完成待辦事項(xiàng)炫狱,同時(shí)保持應(yīng)用程序的數(shù)據(jù)與AppSync云服務(wù)同步。該應(yīng)用程序無論有沒有互聯(lián)網(wǎng)連接都可以使用剔猿!

打開啟動(dòng)文件夾中的RazeList.xcodeproj视译。

RazeList可以幫助您在所有iOS設(shè)備上保持同步。這是您所有工作的真相之一归敬,更重要的是酷含,您所有的dones!學(xué)完本教程后汪茧,無論使用哪種設(shè)備椅亚,您都將永遠(yuǎn)知道自己在哪里工作。

Xcode中舱污,構(gòu)建并運(yùn)行項(xiàng)目呀舔。

目前,該項(xiàng)目不僅僅是問候惧磺。您將對(duì)此進(jìn)行更改,但是首先需要滿足一些先決條件琳拭。


About GraphQL and AppSync

在編寫任何代碼之前,您首先需要了解GraphQL是什么以及它如何與AppSync一起使用絮缅。

1. What is GraphQL?

GraphQLFacebook2012年開發(fā)耕魄;它是一種查詢語言和服務(wù)器端運(yùn)行時(shí)允扇,用于執(zhí)行旨在與服務(wù)器端API配合使用的查詢。

如果您以前使用過服務(wù)器端API糊治,則可能已經(jīng)熟悉REST井辜。 REST API通過為應(yīng)用程序公開多個(gè)終結(jié)點(diǎn)來工作抑胎,每個(gè)終結(jié)點(diǎn)均針對(duì)特定的數(shù)據(jù)類型而設(shè)計(jì)赃蛛。如今呕臂,大多數(shù)API都被認(rèn)為是RESTful的歧蒋。但是,REST標(biāo)準(zhǔn)的解釋較為寬松阐虚,因此您可能會(huì)在多個(gè)REST API上有不同的經(jīng)驗(yàn)实束。

REST相反构订,GraphQL僅公開一個(gè)端點(diǎn),您可以通過查詢與之交互分尸。使用這種方法箩绍,客戶端僅訪問他們需要的數(shù)據(jù),并最小化通過網(wǎng)絡(luò)發(fā)送的數(shù)據(jù)量。查看GraphQL如何運(yùn)行的最佳方法是一個(gè)示例马绝。

type Todo {
  id: ID!
  name: String!
  description: String
  completed: Boolean!
}

上面是GraphQL模式的示例掷邦,描述了Todo類型,這是構(gòu)建RazeList時(shí)將使用的基本結(jié)構(gòu)宣蔚。 服務(wù)器定義此類型,以便您可以獲取它篷扩。 假設(shè)您的應(yīng)用程序中有一個(gè)屏幕枢冤,其中列出了所有待辦事項(xiàng),但只需要namecompleted字段核蘸。 這是使用GraphQL查詢獲取該屏幕數(shù)據(jù)的方式:

query TodoQuery {
  listTodos {
    items {
      name
      completed
    }
  }
}

GraphQL查詢僅通過指定其關(guān)注的字段來訪問所需的數(shù)據(jù)。 您將此查詢發(fā)送到服務(wù)器徙鱼,并且服務(wù)器使用與您的查詢匹配的數(shù)據(jù)結(jié)構(gòu)進(jìn)行響應(yīng)。 以這種方式添加和刪除字段將需要在使用REST時(shí)對(duì)API進(jìn)行更改绞绒,而在這里蓬衡,您只需在應(yīng)用程序內(nèi)部更改查詢撤蟆,而無需完全修改服務(wù)器盟猖。

2. GraphQL with AWS AppSync

AWS AppSync可以完成后端Web服務(wù)的所有繁重工作。 它充當(dāng)GraphQL和其他AWS服務(wù)(例如數(shù)據(jù)存儲(chǔ)娘汞,緩存和實(shí)時(shí)更新)之間的橋梁惊豺。

AppSync為您的項(xiàng)目提供了一個(gè)儀表板,您可以在其中查看和查詢數(shù)據(jù)烹俗,以及通過自定義功能添加自定義功能。

您的應(yīng)用將在后臺(tái)通過GraphQLAppSync進(jìn)行通信; 但是置吓,您將使用AppSync iOS框架來消除很多這種復(fù)雜性。 完成一些配置后戴质,您只會(huì)以一種類型安全的方式談?wù)撃暮蠖恕?/p>


Installing the Amplify Framework

您將從安裝項(xiàng)目依賴項(xiàng)開始。 您可能已經(jīng)安裝了其中的一些(或全部)后专。 在這種情況下,您可以跳到相關(guān)部分型凳。

1. Installing npm

Node Package Manager (npm)是一個(gè)軟件包管理器和CLI (command line interface),用于管理Node.js軟件包蓄氧。 在此項(xiàng)目中准给,您將使用npm安裝Amplify CLI祖灰。

如果不確定是否已安裝npm叁扫,請打開“終端”并鍵入npm -v畴蒲,然后按Enter模燥。 如果已安裝,則應(yīng)該在“終端”窗口中看到打印的版本號(hào)掩宜。

npmNode.js一起安裝蔫骂。 要同時(shí)安裝Node.jsnpm,請導(dǎo)航到node.js website網(wǎng)站牺汤,然后單擊標(biāo)有LTS的下載鏈接辽旋。 在撰寫本文時(shí)戴已,當(dāng)前的LTS版本是14.15.1 LTS每瞒。

下載完成后挤庇,打開.pkg文件掷酗,您應(yīng)該看到以下內(nèi)容:

單擊繼續(xù)阿蝶,然后按照步驟進(jìn)行粤蝎。 安裝完成后,重新啟動(dòng)Terminal并鍵入npm -v并按Enter。 現(xiàn)在盅粪,您應(yīng)該看到版本號(hào)含鳞。

2. Installing Amplify CLI

通過命令行安裝Amplify桅狠。 在終端內(nèi)陨仅,鍵入以下內(nèi)容颖侄,然后按Enter。

sudo npm install -g @aws-amplify/cli

根據(jù)需要輸入系統(tǒng)密碼。 npm會(huì)在終端窗口中看到很多活動(dòng)械媒。 安裝Amplify后,您應(yīng)該會(huì)看到類似以下的內(nèi)容:

----------------------------------------
Successfully installed the Amplify CLI
----------------------------------------

此過程完成后停巷,輸入以下命令始鱼,然后按Enter

amplify configure

該命令將在新的瀏覽器窗口中打開AWS登錄頁面纸厉。 如果您還沒有AWS帳戶,則需要先注冊一個(gè)帳戶朝抖,然后才能完成此步驟。 注冊非常容易胧卤。 有關(guān)如何執(zhí)行此操作的說明扔水,請參閱 at the AWS knowledge center朝氓。 完成后魔市,請務(wù)必從此開始繼續(xù)本教程主届。

在瀏覽器窗口中登錄到您的AWS賬戶。 登錄后待德,返回“終端”窗口君丁,然后按Enter

接下來将宪,您需要指定您的區(qū)域绘闷。 使用箭頭鍵選擇最能代表您的位置的區(qū)域,然后按Enter较坛。

接下來輸入新用戶的用戶名印蔗,然后按Enter。 這可以是您想要的任何東西丑勤。

這會(huì)將您定向到AWS控制臺(tái)以完成設(shè)置华嘹。 使用底部的按鈕單擊整個(gè)設(shè)置過程,并確保在權(quán)限屏幕上選中了AdministratorAccess法竞。

在用戶設(shè)置成功屏幕上耙厚,將Access key IDSecret access key復(fù)制到安全位置-以后將需要它們。 復(fù)制Secret access key時(shí)岔霸,請確保單擊Show薛躬。

返回“終端”窗口,然后按Enter呆细。

當(dāng)終端提示時(shí)型宝,輸入您的Access Key IDSecret Access Key

最后侦鹏,在要求輸入Profile Name時(shí)诡曙,最后一次按Enter鍵。 這會(huì)將您的配置文件設(shè)置為default略水。

3. Installing CocoaPods

您將使用CocoaPodsAppSync框架添加到您的項(xiàng)目中价卤。 如果您不熟悉CocoaPods,可以在我們的 CocoaPods Tutorial for Swift教程中了解

CocoaPods通過Ruby安裝渊涝,而Ruby已經(jīng)安裝在Mac上慎璧。 打開終端,鍵入以下命令跨释,然后按Enter胸私。

sudo gem install cocoapods

短暫的延遲后,您應(yīng)該在終端窗口中看到Successfully installed cocoapods-VERSION鳖谈。


Adding Amplify to the Project

現(xiàn)在你已經(jīng)設(shè)置好了依賴項(xiàng)岁疼,你可以繼續(xù)在Xcode中設(shè)置項(xiàng)目了。確保你在下一步關(guān)閉Xcode缆娃,是的捷绒,你沒聽錯(cuò)!

打開一個(gè)終端屏幕瑰排,并使用cd導(dǎo)航到starter項(xiàng)目目錄。然后暖侨,在終端窗口中輸入以下內(nèi)容:

pod init

命令執(zhí)行完畢后椭住,您將注意到項(xiàng)目目錄中出現(xiàn)了一個(gè)名為Podfile的新文件。在一個(gè)文本編輯器中打開Podfile字逗,添加如下# Pods for RazeList:

pod 'Amplify'
pod 'Amplify/Tools'
pod 'AmplifyPlugins/AWSAPIPlugin'
pod 'AmplifyPlugins/AWSDataStorePlugin'

回到終端窗口并輸入以下命令:

pod install

幾秒鐘后京郑,您應(yīng)該看到以下消息:

Pod installation complete! There are 4 dependencies from the Podfile and 12 total pods installed.

瞧!就像這樣,您的包已經(jīng)安裝并包含在您的項(xiàng)目中葫掉。

Finder中打開項(xiàng)目目錄些举,您將注意到現(xiàn)在有一個(gè)CocoaPods創(chuàng)建的名為RazeList.xcworkspace的工作空間文件。雙擊這個(gè)文件俭厚,你的項(xiàng)目就會(huì)在Xcode中打開金拒。從現(xiàn)在開始使用這個(gè)文件來打開你的項(xiàng)目,而不是RazeList.xcodeproj套腹,因?yàn)樗行枰囊蕾図?xiàng)。

1. Adding AppSync Script

你快過終點(diǎn)線了资铡。在編寫任何代碼之前电禀,你需要做的最后一件事是在Xcode的Build Phases選項(xiàng)卡中添加一個(gè)Run Script。這個(gè)腳本執(zhí)行一些需要在項(xiàng)目中使用AppSync的任務(wù)笤休。

Xcode中選擇RazeList項(xiàng)目尖飞。在project explorer中,單擊Build Phases店雅。單擊+按鈕并選擇New Run Script Phase政基。

您將注意到列表底部有一個(gè)新的Run Script條目。單擊箭頭以展開它闹啦。

在頂部的代碼編輯器中沮明,添加以下代碼:

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

現(xiàn)在構(gòu)建并運(yùn)行項(xiàng)目。這次的構(gòu)建時(shí)間會(huì)稍長一些窍奋,因?yàn)?code>Xcode將運(yùn)行腳本(Run Script)作為構(gòu)建過程的一部分執(zhí)行荐健。當(dāng)構(gòu)建完成時(shí),你的項(xiàng)目中會(huì)有更多的文件琳袄;您將在下一節(jié)中使用這些江场。在進(jìn)入下一階段之前等待項(xiàng)目構(gòu)建是很重要的。

2. Initializing Amplify

一旦構(gòu)建過程完成窖逗,您將需要在項(xiàng)目中初始化amplify址否。當(dāng)您在項(xiàng)目導(dǎo)航器中看到一個(gè)名為AmplifyConfig的新文件夾時(shí),您就會(huì)知道構(gòu)建已經(jīng)完成了它的工作碎紊。

確保您在終端的項(xiàng)目目錄中佑附,并輸入以下命令:

amplify init

根據(jù)界面提示輸入如下信息:

? Enter a name for the environment
    Press Enter

? Choose your default editor
    None

? Do you want to use an AWS profile?
    Y

? Please choose the profile you want to use
    Press Enter for default

在同一個(gè)終端窗口中樊诺,輸入以下命令,然后按Enter帮匾。

amplify add api

根據(jù)界面提示輸入如下信息啄骇。其他步驟按enter,使用默認(rèn)設(shè)置瘟斜。

? Please select from one of the below mentioned services:
    GraphQL

? Provide API name:
    Press Enter to set this to your directory name.

接下來輸入以下命令缸夹。

amplify push

根據(jù)界面提示輸入如下信息。

? Are you sure you want to continue? 
    Y

? Do you want to generate code for your newly created GraphQL API
    N

這可能看起來有很多設(shè)置螺句,但Amplify已經(jīng)為您做了很多虽惭。您已經(jīng)創(chuàng)建了一個(gè)用戶,設(shè)置了一個(gè)應(yīng)用程序并將其添加到AWS儀表板蛇尚,創(chuàng)建了一個(gè)GraphQL API并將其發(fā)布到AWS芽唇。一切都準(zhǔn)備好了!


Creating Models Using GraphQL

在處理后端服務(wù)時(shí),您可能希望將數(shù)據(jù)類型表示為模型取劫。Amplify讓你省去了自己打印它們的麻煩匆笤。那不是很好嗎?

但是,您仍然需要告訴Amplify要生成什么谱邪,這將使用GraphQL完成!

打開AmplifyConfig組內(nèi)的schema.graphql炮捧。

替換該文件的內(nèi)容如下:

type Todo @model {
  id: ID!
  name: String!
  description: String
  completed: Boolean!
}

接下來,打開同一個(gè)目錄中的amplifytools.xcconfig惦银。將pushmodelgen更改為true咆课。

構(gòu)建并運(yùn)行項(xiàng)目。構(gòu)建完成后扯俱,項(xiàng)目導(dǎo)航器中會(huì)有一個(gè)名為AmplifyModels的新目錄书蚪。在配置中更改上面的行告訴AmplifyGraphQL schema模式為您生成模型文件,并更新AWS上的配置迅栅。擴(kuò)大AmplifyModels殊校,看看周圍系馆。您將看到Todo.swift包含您的模型和一些幫助文件式矫。

1. Using Amplify in the App

在左側(cè)的項(xiàng)目導(dǎo)航器中孵稽,打開AppMain.swift并添加以下導(dǎo)入:

import Amplify
import AmplifyPlugins

AppDelegate類中诞挨,在application(_:didFinishLaunchingWithOptions:)函數(shù)返回true之前添加以下代碼:

let apiPlugin = AWSAPIPlugin(modelRegistration: AmplifyModels())
let dataStorePlugin = AWSDataStorePlugin(modelRegistration: AmplifyModels())
do {
  try Amplify.add(plugin: apiPlugin)
  try Amplify.add(plugin: dataStorePlugin)
  try Amplify.configure()
  print("Initialized Amplify")
} catch {
  print("Could not initialize Amplify: \(error)")
}

構(gòu)建并運(yùn)行項(xiàng)目镰禾。

沒有任何視覺上的變化州叠,但是您已經(jīng)完全配置了項(xiàng)目以與AppSyncAmplify一起使用弊决。


Building the To Do List UI

安裝了庫仅政,設(shè)置了CocoaPods并生成了模型之后拜英,就該使RazeList變得栩栩如生了静汤。

本教程的某些SwiftUI編碼已為您完成,但您仍需要構(gòu)建主要任務(wù)清單。 這就是本節(jié)中要執(zhí)行的操作虫给。

1. Adding Rows to the To Do List

首先定義行藤抡。 右鍵單擊Views組,然后選擇New File選項(xiàng)抹估。 選擇SwiftUI View缠黍,然后單擊Next。 將文件命名為TodoRowView.swift并創(chuàng)建它药蜻。

打開該文件瓷式,然后在TodoRowView聲明下方添加以下內(nèi)容。

// 1
let todoItem: Todo
// 2
let onToggleCompleted: (Todo) -> Void

待辦事項(xiàng)行定義了兩個(gè)要求语泽。

  • 1) 用于渲染的Todo模型贸典。
  • 2) 當(dāng)用戶切換完成狀態(tài)時(shí)調(diào)用的閉包。

這將導(dǎo)致錯(cuò)誤踱卵,因?yàn)轭A(yù)覽沒有傳遞這些依賴項(xiàng)廊驼。 用以下內(nèi)容替換TodoRowView_Previews的全部內(nèi)容:

struct TodoRowView_Previews: PreviewProvider {
  static var previews: some View {
    TodoRowView(
      todoItem: Todo(
      id: UUID().uuidString,
      name: "Build this cool app",
      description: "I need to finish building this awesome todo list app :]",
      completed: false)) { _ in }
  }
}

接下來,您將定義一個(gè)方法惋砂,當(dāng)用戶切換待辦事項(xiàng)時(shí)妒挎。 將以下方法添加到TodoRowView中:

func toggleCompleted() {
  withAnimation {
    onToggleCompleted(todoItem)
  }
}

此函數(shù)僅將onToggle Completed封裝在動(dòng)畫塊中,該動(dòng)畫塊將使各節(jié)之間的行移動(dòng)具有動(dòng)畫效果西饵。

接下來饥漫,將整個(gè)body替換為以下內(nèi)容:

var body: some View {
  // 1
  VStack(alignment: .leading, spacing: 8) {
    HStack(spacing: 10) {
      // 2
      Button(action: { onToggleCompleted(todoItem) }) {
        Image(systemName: todoItem.completed ? "checkmark.square" : "square")
          .imageScale(.large)
          .foregroundColor(todoItem.completed ? .pink : .primary)
      }
      // 3
      Text(todoItem.name)
        .font(.system(size: 18, weight: .semibold))
    }
    // 4
    if let description = todoItem.description {
      Text(description)
        .font(.system(size: 14, weight: .medium))
        .padding(.leading, 32)
        .padding(.trailing, 10)
        .foregroundColor(.gray)
    }
  }
}

上面的代碼是這樣的:

  • 1) 定義一個(gè)VStack容器。
  • 2) 定義一個(gè)HStack罗标,其中包含帶有復(fù)選框圖像的按鈕。 根據(jù)待執(zhí)行模型的completed屬性的狀態(tài)來選擇是否選中該圖像积蜻。 點(diǎn)擊按鈕將調(diào)用onToggleCompleted(_ :)闯割。
  • 3) 堆棧stack中的第二項(xiàng)是包含要執(zhí)行的操作的名稱的Text視圖。
  • 4) 如果待辦事項(xiàng)包含描述竿拆,請?jiān)?code>Text視圖中呈現(xiàn)它宙拉。

2. Setting up Your Data

打開TodoListViewModel.swift。 在類實(shí)現(xiàn)內(nèi)添加以下代碼:

@Published var todos: [Todo] = []
@Published var completedTodos: [Todo] = []

TodoListViewModel符合ObservableObject丙笋。 遵循此協(xié)議谢澈,允許對(duì)象在狀態(tài)更改時(shí)發(fā)布更新。 使用@Published屬性包裝器告訴對(duì)象通過其發(fā)布者將更改廣播給正在收聽的任何人御板。 當(dāng)對(duì)象更新時(shí)锥忿,SwiftUI使用它來重繪UI。

如果您想了解有關(guān)ObservableObject的更多信息怠肋,請查看Combine: Asynchronous Programming with Swift敬鬓。

接下來,打開TodoListView.swift并在視圖實(shí)現(xiàn)中添加以下代碼:

@ObservedObject var viewModel = TodoListViewModel()

在這里,您將使用@ObservedObject屬性包裝器創(chuàng)建對(duì)TodoListViewModel的引用钉答。 以這種方式創(chuàng)建屬性會(huì)告訴SwiftUI您關(guān)心該對(duì)象的狀態(tài)础芍,并且它應(yīng)該響應(yīng)更改。

3. Adding Sections

接下來数尿,您將定義兩個(gè)部分仑性,一個(gè)用于待辦事項(xiàng),一個(gè)用于完成待辦事項(xiàng)右蹦。 一般來說诊杆,您希望保持body輕點(diǎn)。 考慮到這一點(diǎn)嫩实,您將把這兩個(gè)部分定義為計(jì)算屬性刽辙。

將第一部分添加到TodoListView中:

var todoSection: some View {
  // 1
  Group {
    // 2
    if viewModel.todos.isEmpty {
      Text("Nothing to do!")
    } else {
      // 3
      ForEach(viewModel.todos, id: \.id) { todo in
        // 4
        TodoRowView(todoItem: todo) { todo in
          withAnimation {
            // Toggle complete
          }
        }
        .padding(.vertical, 6)
      }
      .onDelete(perform: viewModel.deleteTodos)
    }
  }
}

一點(diǎn)一點(diǎn)地:

  • 1) 您無法選擇返回Text視圖或ForEach視圖,因此它們被包裝在Group中甲献。
  • 2) 如果列表中沒有待辦事項(xiàng)宰缤,請返回一個(gè)反映此情況的Text視圖。
  • 3) 如果有待辦事項(xiàng)晃洒,請遍歷每個(gè)步驟以在ForEach中執(zhí)行慨灭。
  • 4) 對(duì)于列表中的每個(gè)操作,生成一個(gè)TodoRowView并傳遞當(dāng)前操作球及。

接下來氧骤,您將完成已完成的任務(wù)。 在todoSection屬性下方吃引,添加以下內(nèi)容:

var completedTodoSection: some View {
  Group {
    if viewModel.completedTodos.isEmpty {
      Text("Completed Tasks Appear Here")
    } else {
      ForEach(viewModel.completedTodos, id: \.id) { todo in
        TodoRowView(todoItem: todo) { todo in
          withAnimation {
            // Toggle complete
          }
        }
        .padding(.vertical, 6)
      }
      .onDelete(perform: viewModel.deleteCompletedTodos)
    }
  }
}

唯一的區(qū)別是您已將對(duì)viewModel.todos的引用替換為viewModel.completedTodos筹陵。

現(xiàn)在,您已經(jīng)定義了兩個(gè)列表部分镊尺,現(xiàn)在該看看它們的實(shí)際效果了朦佩!

body內(nèi)容替換為以下內(nèi)容:

// 1
List {
  // 2
  Section(header: Text("Todo")) {
    todoSection
  }
  // 3
  Section(header: Text("Completed")) {
    completedTodoSection
  }
}
// 4
.listStyle(GroupedListStyle())

上面的代碼執(zhí)行以下操作:

  • 1) 創(chuàng)建一個(gè)列表以包含您先前創(chuàng)建的部分。
  • 2) 將“待辦事項(xiàng)”部分嵌入Section視圖中庐氮。
  • 3) 將完成的待辦事項(xiàng)部分嵌入到Section視圖中语稠。
  • 4) 為列表提供分組樣式。 這將分隔各節(jié)并應(yīng)用一些默認(rèn)樣式弄砍。

構(gòu)建并運(yùn)行以查看結(jié)果仙畦。

您終于擺脫了hello world應(yīng)用! 真好

4. Adding a To Do

在本部分的最后部分音婶,您將集成“添加待辦事項(xiàng)”屏幕慨畸。 用戶界面已經(jīng)構(gòu)建,因此這是一個(gè)相當(dāng)簡單的步驟衣式。

轉(zhuǎn)到TodoListView.swift并在視圖實(shí)現(xiàn)內(nèi)添加一個(gè)新屬性:

@State var addNewTodoPresented: Bool = false

這將負(fù)責(zé)呈現(xiàn)和關(guān)閉添加視圖先口。

body的底部型奥,.listStyle(GroupedListStyle())之后的行上,添加以下視圖修飾符:

// 1
.navigationBarItems(
  trailing: Button(action: { addNewTodoPresented.toggle() }) {
    Image(systemName: "plus")
      .imageScale(.large)
  }
)
// 2
.sheet(isPresented: $addNewTodoPresented) {
  AddTodoView { name, description in
    // add todo
    addNewTodoPresented.toggle()
  }
}

這看起來有點(diǎn)復(fù)雜碉京,但實(shí)際上相當(dāng)簡單:

  • 1) navigationBarItems(trailing :)視圖修飾符將導(dǎo)航項(xiàng)添加到封閉的NavigationView的導(dǎo)航欄中厢汹。 您在此處添加了一個(gè)按鈕,點(diǎn)擊該按鈕即可切換addNewTodoPresented谐宙。
  • 2) 當(dāng)isPresented狀態(tài)為true時(shí)烫葬,sheet(isPresented:content :)視圖修飾符將顯示一個(gè)模型。 閉包返回要顯示的視圖凡蜻。 在這種情況下搭综,您將返回AddTodoView

構(gòu)建并運(yùn)行以查看結(jié)果划栓。

您現(xiàn)在在導(dǎo)航欄中有一個(gè)添加按鈕兑巾,并有一個(gè)屏幕來添加新的待辦事項(xiàng)!


Creating and Editing To Dos

所有人都已設(shè)置完畢忠荞,并具有正常運(yùn)行的用戶界面蒋歌。 您需要做的最后一件事就是將所有東西連接起來!

打開TodoListViewModel.swift并添加一個(gè)新的導(dǎo)入委煤。

import Amplify

1. Adding To Dos

接下來堂油,添加以下方法:

func createTodo(name: String, description: String?) {
  // 1
  let item = Todo(name: name, description: description, completed: false)
  // 2
  todos.append(item)
  // 3
  Amplify.DataStore.save(item) { result in
    switch result {
    case .success(let savedItem):
      print("Saved item: \(savedItem.name)")
    case .failure(let error):
      print("Could not save item with error: \(error)")
    }
  }
}

使用前面步驟中的所有配置,這就是將數(shù)據(jù)保存到本地和云數(shù)據(jù)存儲(chǔ)中所需要的全部碧绞。 這是正在發(fā)生的事情:

  • 1) 使用傳入的變量創(chuàng)建新的待辦事項(xiàng)府框。
  • 2) 將其添加到本地待辦事項(xiàng)todos數(shù)組。
  • 3) 使用Amplify框架讥邻,將to do添加到您的數(shù)據(jù)存儲(chǔ)中迫靖。

接下來打開TodoListView.swift,然后向下滾動(dòng)到body末尾的.sheet修改器兴使。 在addNewTodoPresented.toggle()上方一行的閉包中袜香,添加對(duì)createTodo(name:description :)函數(shù)的調(diào)用。

viewModel.createTodo(name: name, description: description)

您現(xiàn)在可以保存待辦事項(xiàng)鲫惶,但是除非您可以加載它們,否則這樣做是沒有用的实抡!

返回TodoListViewModel.swift欠母,將loadToDos()替換為以下內(nèi)容。

func loadToDos() {
  Amplify.DataStore.query(Todo.self) { result in
    switch result {
    case .success(let todos):
      self.todos = todos.filter { !$0.completed }
      completedTodos = todos.filter { $0.completed }
    case .failure(let error):
      print("Could not query DataStore: \(error)")
    }
  }
}

現(xiàn)在吆寨,在TodoListView.swift中赏淌,在.sheet下添加一個(gè)新的視圖修改器。

.onAppear {
  viewModel.loadToDos()
}

構(gòu)建并運(yùn)行項(xiàng)目以添加您的第一個(gè)待辦事項(xiàng)啄清!

2. Completing To Dos

到目前為止六水,該應(yīng)用程序非常適合向您顯示您需要做的事情-但并不擅長讓您完成這些任務(wù)俺孙。

打開TodoListViewModel。 滾動(dòng)到底部掷贾,然后在loadTodos()之后添加以下新方法:

func toggleComplete(_ todo: Todo) {
  // 1
  var updatedTodo = todo
  updatedTodo.completed.toggle()
  
  // 2
  Amplify.DataStore.save(updatedTodo) { result in
    switch result {
    case .success(let savedTodo):
      print("Updated item: \(savedTodo.name )")
    case .failure(let error):
      print("Could not update data with error: \(error)")
    }
  }
  // 3
  if updatedTodo.completed {
    if let index = todos.firstIndex(where: { $0.id == todo.id }) {
      todos.remove(at: index)
      completedTodos.insert(updatedTodo, at: 0)
    }
  // 4
  } else {
    if let index = completedTodos.firstIndex(where: { $0.id == todo.id }) {
      completedTodos.remove(at: index)
      todos.insert(updatedTodo, at: 0)
    }
  }
}

好的睛榄,這是相當(dāng)一部分代碼。 它的作用是:

  • 1) 制作一個(gè)可變的副本即可進(jìn)行修改想帅,然后切換完成的值场靴。
  • 2) 使用Amplify,將的操作保存回您的數(shù)據(jù)存儲(chǔ)中港准。
  • 3) 如果待辦事項(xiàng)已完成旨剥,請將其從待辦事項(xiàng)todos中刪除并將其添加到completedTodos
  • 4) 如果待辦事項(xiàng)尚未完成浅缸,請將其從completedTodos中刪除并將其添加到待辦事項(xiàng)中轨帜。

打開TodoListView.swift并導(dǎo)航到頂部的兩個(gè)屬性。 在todoSectioncompletedTodoSection中衩椒,您會(huì)注意到兩個(gè)占位符注釋// Toggle complete蚌父。 用以下兩個(gè)地方替換該注釋:

viewModel.toggleComplete(todo)

構(gòu)建并運(yùn)行該應(yīng)用程序。 現(xiàn)在烟具,您可以輕按任一列表中的每個(gè)待辦事項(xiàng)梢什,并以很酷的動(dòng)畫更改完成狀態(tài)!

3. Deleting To Dos

您需要添加的最后一件事是刪除行的方法朝聋。 UI中已經(jīng)存在“滑動(dòng)刪除”功能嗡午,因此您只需將其連接起來即可。

打開TodoListViewModel.swift冀痕,您會(huì)在頂部注意到三種刪除方法荔睹。 這些將充當(dāng)幫助方法,以從其各自的列表中刪除待辦事項(xiàng)言蛇。

添加以下方法:

func delete(todo: Todo) {
  Amplify.DataStore.delete(todo) { result in
    switch result {
    case .success:
      print("Deleted item: \(todo.name)")
    case .failure(let error):
      print("Could not update data with error: \(error)")
    }
  }
}

此方法通過從Amplify框架調(diào)用delete(_ :)從數(shù)據(jù)存儲(chǔ)中刪除模型僻他。

接下來,將以下三種刪除方法替換為:

// 1
func deleteItems(at offsets: IndexSet, from todoList: inout [Todo]) {
  for index in offsets {
    let todo = todoList[index]
    delete(todo: todo)
  }

  todoList.remove(atOffsets: offsets)
}

// 2
func deleteTodos(at offsets: IndexSet) {
  deleteItems(at: offsets, from: &todos)
}

// 3
func deleteCompletedTodos(at offsets: IndexSet) {
  deleteItems(at: offsets, from: &completedTodos)
}

這是您所做的:

  • 1) 第一個(gè)刪除方法調(diào)用剛添加的delete(at:from :)腊尚。
  • 2) 此方法使用todos數(shù)組路由要?jiǎng)h除的調(diào)用吨拗。
  • 3) 此方法使用completedTodos數(shù)組路由要?jiǎng)h除的呼叫。

構(gòu)建并運(yùn)行項(xiàng)目婿斥。 您現(xiàn)在可以滑動(dòng)以刪除待辦事項(xiàng)劝篷!

現(xiàn)在,您有一個(gè)待辦事項(xiàng)列表民宿,可用于添加娇妓,編輯和刪除待辦事項(xiàng)。 它可以脫機(jī)工作活鹰,并與AWS后端保持同步哈恰。

現(xiàn)在只估,您已經(jīng)知道在iOS應(yīng)用中集成和使用AppSyncAmplify的基礎(chǔ)知識(shí)-但是還有很多東西要學(xué)習(xí)! GraphQL可以做的比您在這里介紹的要多得多着绷。

當(dāng)您準(zhǔn)備好使用AWSAmplify進(jìn)行下一步時(shí)蛔钙,請查看 Using AWS as a Back End: Authentication & APIUsing AWS as a Back End: The Data Store API

查看我們的教程GraphQL Using the Apollo Framework: Getting Started蓬戚,以查看更多實(shí)際示例夸楣。 您也可以在GraphQL官方網(wǎng)站official GraphQL website.上了解有關(guān)GraphQL的更多信息。

您還應(yīng)該查看亞馬遜為進(jìn)一步學(xué)習(xí)而制作的官方AWS AppSync教程official AWS AppSync tutorials子漩。

后記

本篇主要講述了基于基于SwiftUIAWS AppSync框架的使用豫喧,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幢泼,隨后出現(xiàn)的幾起案子紧显,更是在濱河造成了極大的恐慌,老刑警劉巖缕棵,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孵班,死亡現(xiàn)場離奇詭異,居然都是意外死亡招驴,警方通過查閱死者的電腦和手機(jī)篙程,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來别厘,“玉大人虱饿,你說我怎么就攤上這事〈ヅ浚” “怎么了氮发?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冗懦。 經(jīng)常有香客問我爽冕,道長,這世上最難降的妖魔是什么披蕉? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任颈畸,我火速辦了婚禮,結(jié)果婚禮上没讲,老公的妹妹穿的比我還像新娘眯娱。我一直安慰自己,他們只是感情好食零,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寂屏,像睡著了一般贰谣。 火紅的嫁衣襯著肌膚如雪娜搂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天吱抚,我揣著相機(jī)與錄音百宇,去河邊找鬼。 笑死秘豹,一個(gè)胖子當(dāng)著我的面吹牛携御,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播既绕,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼啄刹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凄贩?” 一聲冷哼從身側(cè)響起誓军,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疲扎,沒想到半個(gè)月后昵时,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡椒丧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年壹甥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壶熏。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡句柠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出久橙,到底是詐尸還是另有隱情俄占,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布淆衷,位于F島的核電站缸榄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祝拯。R本人自食惡果不足惜甚带,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望佳头。 院中可真熱鬧鹰贵,春花似錦、人聲如沸康嘉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亭珍。三九已至敷钾,卻和暖如春枝哄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阻荒。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工挠锥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侨赡。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓蓖租,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羊壹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蓖宦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容