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簡(jiǎn)單App的Dependency Injection應(yīng)用(一)
17. SwiftUI框架詳細(xì)解析 (十七) —— 基于SwiftUI簡(jiǎn)單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)建(二)
22. SwiftUI框架詳細(xì)解析 (二十二) —— 基于SwiftUI的AWS AppSync框架的使用(一)

源碼

1. Swift

首先看下工程組織結(jié)構(gòu)

接著就是源碼啦

1. AmplifyModels.swift
// swiftlint:disable all
import Amplify
import Foundation

// Contains the set of classes that conforms to the `Model` protocol. 

final public class AmplifyModels: AmplifyModelRegistration {
  public let version: String = "e7b3132e780634896f2769f900665475"

  public func registerModels(registry: ModelRegistry.Type) {
    ModelRegistry.register(modelType: Todo.self)
  }
}
2. Todo.swift
// swiftlint:disable all
import Amplify
import Foundation

public struct Todo: Model {
  public let id: String
  public var name: String
  public var description: String?
  public var completed: Bool

  public init(id: String = UUID().uuidString,
      name: String,
      description: String? = nil,
      completed: Bool) {
      self.id = id
      self.name = name
      self.description = description
      self.completed = completed
  }
}
3. Todo+Schema.swift
// swiftlint:disable all
import Amplify
import Foundation

extension Todo {
  // MARK: - CodingKeys 
   public enum CodingKeys: String, ModelKey {
    case id
    case name
    case description
    case completed
  }

  public static let keys = CodingKeys.self
  //  MARK: - ModelSchema 

  public static let schema = defineSchema { model in
    let todo = Todo.keys

    model.pluralName = "Todos"

    model.fields(
      .id(),
      .field(todo.name, is: .required, ofType: .string),
      .field(todo.description, is: .optional, ofType: .string),
      .field(todo.completed, is: .required, ofType: .bool)
    )
    }
}
4. TodoListView.swift
import SwiftUI

struct TodoListView: View {
  @ObservedObject var viewModel = TodoListViewModel()
  @State var addNewTodoPresented: Bool = false

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

  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 {
              viewModel.toggleComplete(todo)
            }
          }
          .padding(.vertical, 6)
        }
        .onDelete(perform: viewModel.deleteCompletedTodos)
      }
    }
  }

  var body: some View {
    List {
      Section(header: Text("Todo")) {
        todoSection
      }
      Section(header: Text("Completed")) {
        completedTodoSection
      }
    }
    .listStyle(GroupedListStyle())
    .navigationBarItems(
      trailing: Button(action: { addNewTodoPresented.toggle() }) {
        Image(systemName: "plus")
          .imageScale(.large)
      }
    )
    .sheet(isPresented: $addNewTodoPresented) {
      AddTodoView { name, description in
        // add todo
        viewModel.createTodo(name: name, description: description)
        addNewTodoPresented.toggle()
      }
    }
    .onAppear {
      viewModel.loadToDos()
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    TodoListView()
  }
}
5. TodoListViewModel.swift
import Foundation
import Amplify

class TodoListViewModel: ObservableObject {
  @Published var todos: [Todo] = []
  @Published var completedTodos: [Todo] = []

  func createTodo(name: String, description: String?) {
    let item = Todo(name: name, description: description, completed: false)
    todos.append(item)

    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)")
      }
    }
  }

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

    todoList.remove(atOffsets: offsets)
  }

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

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

  func saveTodoItem(name: String, description: String?) {}

  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)")
      }
    }
  }

  func toggleComplete(_ todo: Todo) {
    var updatedTodo = todo
    updatedTodo.completed.toggle()

    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)")
      }
    }

    if updatedTodo.completed {
      if let index = todos.firstIndex(where: { $0.id == todo.id }) {
        todos.remove(at: index)
        completedTodos.insert(updatedTodo, at: 0)
      }
    } else {
      if let index = completedTodos.firstIndex(where: { $0.id == todo.id }) {
        completedTodos.remove(at: index)
        todos.insert(updatedTodo, at: 0)
      }
    }
  }

  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)")
      }
    }
  }
}
6. AddTodoView.swift
import SwiftUI

struct AddTodoView: View {
  @State var name: String = ""
  @State var description: String = ""
  @State var showNameRequiredWarning: Bool = false

  let todoSaved: (String, String?) -> Void

  var dismissIndicator: some View {
    RoundedRectangle(cornerRadius: 3)
      .frame(width: 90, height: 6)
      .foregroundColor(Color(UIColor.lightGray))
  }

  var titleView: some View {
    HStack {
      Text("Add New")
        .font(.largeTitle).fontWeight(.semibold)
      Spacer()
    }
    .padding(.horizontal)
  }

  var todoInputFields: some View {
    List {
      Section(header: Text("Name")) {
        TextField("", text: $name)
          .frame(height: 40)
      }
      Section(header: Text("Description")) {
        TextEditor(text: $description)
          .frame(height: 200)
      }
    }
    .listStyle(GroupedListStyle())
  }

  var saveButton: some View {
    Button(action: saveButtionPressed) {
      ZStack {
        RoundedRectangle(cornerRadius: 10)
          .foregroundColor(.blue)
        Text("Add Todo")
          .font(.system(size: 22, weight: .semibold))
          .foregroundColor(.white)
      }
      .frame(height: 44)
      .padding(.horizontal, 20)
    }
  }

  var body: some View {
    Group {
      VStack {
        dismissIndicator
        titleView
        todoInputFields
        Spacer()
        saveButton
      }
      .padding(.vertical, 30)
      .alert(isPresented: $showNameRequiredWarning) {
        Alert(
          title: Text("Name Required"),
          message: Text("A Todo should have a name"),
          dismissButton: .destructive(Text("OK")))
      }
    }
    .background(Color(UIColor.systemGroupedBackground))
    .edgesIgnoringSafeArea(.all)
  }

  func saveButtionPressed() {
    guard !name.isEmpty else {
      showNameRequiredWarning.toggle()
      return
    }
    todoSaved(name, description)
  }
}

struct AddTodoView_Previews: PreviewProvider {
  static var previews: some View {
    AddTodoView { _, _ in }
  }
}
7. TodoRowView.swift
import SwiftUI

struct TodoRowView: View {
  let todoItem: Todo
  let onToggleCompleted: (Todo) -> Void

  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      HStack(spacing: 10) {
        Button(action: { onToggleCompleted(todoItem) }) {
          Image(systemName: todoItem.completed ? "checkmark.square" : "square")
            .imageScale(.large)
            .foregroundColor(todoItem.completed ? .pink : .primary)
        }

        Text(todoItem.name)
          .font(.system(size: 18, weight: .semibold))
      }

      if let description = todoItem.description {
        Text(description)
          .font(.system(size: 14, weight: .medium))
          .padding(.leading, 32)
          .padding(.trailing, 10)
          .foregroundColor(.gray)
      }
    }
  }

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

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 }
  }
}
8. AppMain.swift
import SwiftUI
import Amplify
import AmplifyPlugins

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
    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)")
    }
    return true
  }
}

@main
struct AppMain: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  var body: some Scene {
    WindowGroup {
      NavigationView {
        TodoListView()
          .navigationBarTitle(Text("Razelist"))
      }
    }
  }
}

后記

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容