# [3] 快速學會 SwiftUI 基礎入門

### 0. 完成成品

這一篇要用 SwiftUI 來製作一個簡單的待辦清單。

本篇文章是一個步驟指導，建議可以搭配影片一起學習：

%[https://youtu.be/irw4WJZIxQs]

### 1. 開啟 SwiftUI 專案

用 Xcode 新建一個 SwiftUI 的專案

### 2. 建立一個列表

List 可用來呈現列表，但內容必須要自己定義，這邊用 ForEach 來產生 10 個 Text 的內容：  
```swift
struct ContentView: View {
    var body: some View {
        
        List {
            ForEach(1...10, id: \.self) { item in
                Text("\(item)")
            }
        }
    }
}
```

List 的外觀，也可以變動：
```swift
List {
    ForEach(1...10, id: \.self) { item in
        Text("\(item)")
    }
}
.listStyle(PlainListStyle())
```

### 3. 建立 Navigation View

這邊新增 Navigation View，並且在右上角新增一個按鈕
```swift
struct ContentView: View {
    var body: some View {
        
        NavigationView {
            List {
                ForEach(1...100, id:\.self) { item in
                    Text("\(item)")
                }
            }
            .navigationTitle("Title")
            .toolbar(content: {
                ToolbarItem(placement: .navigationBarTrailing, content: {
                    Button("Add", action: {
                    })
                })
            })
        }
    }
}
```

### 4. 跳轉第二頁

新增一個 PageView2.swift 檔案：
```swift
struct PageView2: View {
    var body: some View {
        Text("Page2")
    }
}
```

原本的 Button 修改為使用 NavigationLink：
```swift
NavigationView {
    List {
        ForEach(1...100, id:\.self) { item in
            Text("\(item)")
        }
    }
    .navigationTitle("Title")
    .toolbar(content: {
        ToolbarItem(placement: .navigationBarTrailing, content: {
            NavigationLink("Add", destination: {
                PageView2()
            })
        })
    })
}
```

### 5. 第二頁的內容

在第二頁的內容，將會以可以編輯個人的基本資料為主：
```swift
struct PageView2: View {
    
    @State var text = ""
    
    var body: some View {
        
        VStack {
            Form {
                TextField("To do...", text: $text)
            }
            
            Button("Add", action: {
            })
        }
    }
}
```

### 6. 建立資料庫

新增一個 Data Model 檔案，並且在此檔案內新增一個名為 Todo 的  ENTITIES，裡面包含了一個欄位：text，型別為 String

![截圖 2022-07-08 下午8.13.23.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1657282411046/veyZXBcug.png align="left")

### 7. 讀取資料庫

新增一個 Persistence.swift 檔案，用這段程式碼來去開啟 Model：
```swift
import CoreData

struct Persistence {
    
    static let share = Persistence()
    
    let container: NSPersistentContainer
    
    init() {
        container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as? NSError {
                print(error)
            }
        })
    }
}
```

### 8. 修改主程式

在 App 一開始啟動時，就直接開啟資料庫，並且使用 Environment 傳入，修改如下：
```swift
@main
struct TestSwiftUIApp: App {
    
    let persistence = Persistence.share
    
    var body: some Scene {
        WindowGroup {
            ContentView().environment(\.managedObjectContext, persistence.container.viewContext)
        }
    }
}
```

### 9. 將資料寫入到資料庫

在第二頁的內容當中，要將 TextField 所輸入的資料寫入到資料庫中，修改如下：
```swift
struct SwiftUIView: View {
    
    @Environment(\.managedObjectContext) var viewContext
    @State var text = ""
    
    var body: some View {
        
        VStack {
            Form {
                TextField("To do...", text: $text)
            }
            
            Button("Add", action: {
                let todo = Todo(context: self.viewContext)
                todo.text = self.text
                
                do {
                    try self.viewContext.save()
                } catch {
                    let error = error as? Error
                }
            })
        }
    }
}
```

### 10. 回到上一頁

按下按鈕後，除了儲存資料外，也要自動跳轉回到上一頁
```swift
@Environment(\.presentationMode) var presentationMode
self.presentationMode.wrappedValue.dismiss()
```

完整程式碼修改如下：
```swift
struct SwiftUIView: View {
    
    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var viewContext

    @State var text = ""
    
    var body: some View {
        
        VStack {
            Form {
                TextField("To do...", text: $text)
            }
            
            Button("Add", action: {
                let todo = Todo(context: self.viewContext)
                todo.text = self.text
                
                do {
                    try self.viewContext.save()
                } catch {
                    let error = error as? Error
                }
                
                self.presentationMode.wrappedValue.dismiss()
            })
        }
    }
}
```

### 11. 讀取資料庫

在第一頁中，使用 FetchRequest 方式將資料讀取出來：
```swift
struct ContentView: View {
    
    @FetchRequest(entity: Todo.entity(), sortDescriptors: [])
    var todos: FetchedResults<Todo>
    
    var body: some View {
        
        NavigationView {
            List {
                ForEach(todos) { item in
                    Text(item.text ?? "")
                }
            }
            .navigationTitle("To Do Lists")
            .toolbar(content: {
                ToolbarItem(placement: .navigationBarTrailing, content: {
                    NavigationLink("Add", destination: {
                        PageView2()
                    })
                })
            })
        }
        
    }
}
```

### 12. 左滑刪除

在 List 之中，可以新增左滑刪除的功能：
```swift
List {
    ForEach(todos) { item in
        Text(item.text ?? "")
    }
    .onDelete(perform: delete)
}
```

所以這邊就必須要新增 delete 的函式：
```swift
func delete(offsets: IndexSet) {
    if let index = offsets.first {
        let todo = todos[index]
        viewContext.delete(todo)
            
        do {
            try self.viewContext.save()
        } catch {
            let error = error as? Error
        }
    }
}
```
