版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.01.12 星期二 |
前言
今天翻阅苹果的API文档,发现多了一个框架SwiftUI,这里我们就一起来看一下这个框架。感兴趣的看下面几篇文章。
1. SwiftUI框架详细解析 (一) —— 基本概览(一)
2. SwiftUI框架详细解析 (二) —— 基于SwiftUI的闪屏页的创建(一)
3. SwiftUI框架详细解析 (三) —— 基于SwiftUI的闪屏页的创建(二)
4. SwiftUI框架详细解析 (四) —— 使用SwiftUI进行苹果登录(一)
5. SwiftUI框架详细解析 (五) —— 使用SwiftUI进行苹果登录(二)
6. SwiftUI框架详细解析 (六) —— 基于SwiftUI的导航的实现(一)
7. SwiftUI框架详细解析 (七) —— 基于SwiftUI的导航的实现(二)
8. SwiftUI框架详细解析 (八) —— 基于SwiftUI的动画的实现(一)
9. SwiftUI框架详细解析 (九) —— 基于SwiftUI的动画的实现(二)
10. SwiftUI框架详细解析 (十) —— 基于SwiftUI构建各种自定义图表(一)
11. SwiftUI框架详细解析 (十一) —— 基于SwiftUI构建各种自定义图表(二)
12. SwiftUI框架详细解析 (十二) —— 基于SwiftUI创建Mind-Map UI(一)
13. SwiftUI框架详细解析 (十三) —— 基于SwiftUI创建Mind-Map UI(二)
14. SwiftUI框架详细解析 (十四) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(一)
15. SwiftUI框架详细解析 (十五) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(二)
16. SwiftUI框架详细解析 (十六) —— 基于SwiftUI简单App的Dependency Injection应用(一)
17. SwiftUI框架详细解析 (十七) —— 基于SwiftUI简单App的Dependency Injection应用(二)
18. SwiftUI框架详细解析 (十八) —— Firebase Remote Config教程(一)
19. SwiftUI框架详细解析 (十九) —— Firebase Remote Config教程(二)
20. SwiftUI框架详细解析 (二十) —— 基于SwiftUI的Document-Based App的创建(一)
21. SwiftUI框架详细解析 (二十一) —— 基于SwiftUI的Document-Based App的创建(二)
22. SwiftUI框架详细解析 (二十二) —— 基于SwiftUI的AWS AppSync框架的使用(一)
源码
1. Swift
首先看下工程组织结构
接着就是源码啦
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"))
}
}
}
}
后记
本篇主要讲述了基于基于
SwiftUI
的AWS AppSync
框架的使用,感兴趣的给个赞或者关注~~~