توسعه اپلیکیشن لیست وظایف با SwiftUI و Core Data — از صفر تا صد
در این مقاله قصد داریم یک اپلیکیشن لیست وظایف با SwiftUI و Core Data توسعه دهیم که شرایط زیر را داشته باشد:
- بتوانیم یک وظیفه اضافه کنیم.
- یک وظیفه را تکمیل کنیم.
- یک وظیفه را حذف کنیم.
- در صورت بسته شدن اپلیکیشن، دادهها حفظ شوند.
اپلیکیشن نهایی ما به صورت زیر خواهد بود:
برای مشاهده کد کامل اپلیکیشن میتوانید به این ریپوی گیتهاب (+) مراجعه کنید. اما اگر میخواهید آن را به صورت گام به گام همراه با ما بسازید، در ادامه این نوشته با ما همراه شوید.
1. ایجاد یک اپلیکیشن تک صفحهای iOS
ابتدا یک پروژه جدید Xcode برای یک اپلیکیشن تکصفحهای iOS ایجاد کنید. تیک گزینه استفاده از SwiftUI و همچنین Core Data را بزنید.
2. موجودیت و خصوصیتهای Core Data
نخستین کاری که باید انجام دهیم افزودن یک موجودیت به مدل Core Data است. به این منظور فایل ProjectName.xcdatamodeld را باز کنید. توجه کنید که ProjectName همان نام پروژه شما است که در گام قبلی تعیین کردهاید. سپس روی Add Entity در انتهای پنجره کلیک کنید. نام موجودیت جدید را Task بگذارید. در تصویر زیر جایی که نام در inspector تغییر مییابد را میبینید:
2.1 افزودن خصوصیت به موجودیت Task
در این مرحله باید برای موجودیت Task، خصوصیتهایی تعیین کنیم تا اطلاعات زیر را ذخیره کند:
- id – به صورت یک شناسه یکتا برای هر وظیفه استفاده میشود.
- name – نامی است که کاربر به وظیفه میدهد.
- isComplete – تکمیل شدن یا نشدن وظیفه را تعیین میکند.
- dateAdded – تاریخ اضافه شدن وظیفه را نشان میدهد.
برای افزودن خصوصیتها به Task از کلید + در بخش Attributes استفاده کنید و یک نام و نوع برای خصوصیت تعیین کنید. در تصویر زیر روش انجام کار را میبینید:
این جدول هر خصوصیت و نوع متناظر با خصوصیت را تعیین میکند:
- id: UUID
- name: string
- isComplete: Bool
- dateAdded: date
فایل ProjectName.xcdatamodeld اینک باید به صورت تصویر زیر در آمده باشد:
2.2 افزودن یک فایل جدید سوئیفت
در این بخش یک فایل جدید سوئیفت اضافه میکنیم که موجب میشود Task قابل شناسایی باشد و فراخوانی فهرست وظایف آسانتر باشد. یک فایل جدید سوئیفت اضافه کرده و نام آن را Task+Extensions بگذارید. در این فایل کد زیر را اضافه کنید:
1extension Task: Identifiable {
2}
با افزودن کد فوق، کلاس Task اکنون با کلاس Identifiable سازگار است.
2.3 افزودن Core Data به ContentView.swift
در ادامه باید متغیری اضافه کنیم که به managedObjectContext در فایل ContentView.swift دسترسی داشته باشد. به این منظور فایل ContentView.swift را باز کنید و خط زیر را پیش از متغیر body اضافه کنید:
1@Environment(.\managedObjectContext) var context
به این ترتیب ContentView اینک باید به صورت زیر در آمده باشد:
1struct ContentView: View {
2 @Environment(\.managedObjectContext) var context
3 var body: some View {
4 Text("Hello world!")
5 }
6}
7struct ContentView_Previews: PreviewProvider {
8 static var previews: some View {
9 ContentView()
10 }
11}
در کد فوق context را به صورت یک متغیر محیطی اعلان میکنیم یعنی مقدار آن قرار است از محیط view بیاید. در این مورد مقدار آن از SceneDelegate.swift در خطوط 23 تا 27 ناشی میشود که context اعلان شده و سپس ()ContentView داده میشود.
3. کار روی UI
در این بخش روی رابط کاربری اپلیکیشن خود در ContentView.swift کار میکنیم.
3.1 افزودن یک TextField
کار خود را با اضافه کردن یک TextField به اپلیکیشن آغاز میکنیم. به این منظور در فایل ContentView.swift مقدار Text(HelloWorld) را به خط زیر تغییر دهید:
1TextField(title: StringProtocol, text:Binding<String>)
TextField باید دو مشخصه یکی StringProtocol و دیگری <Binding<String داشته باشد. برای StringProtocol یک مشخصه به نام "Task Name" اضافه میکنیم. زمانی که TextField خالی باشد، نام وظیفه به رنگ خاکستری روشن ظاهر میشود. سپس به یک <Binding<String نیاز داریم. این مشخصه به اندازه قبلی ساده نیست. ما باید یک متغیر برای آن اعلان کنیم. پیش از اعلان متغیر body خط زیر را اضافه کنید:
1@State private var taskName: String = ""
سپس مشخصه دوم TextField را به صورت $taskName تعیین کنید. اینک فایل ContentView.swift باید مانند زیر باشد:
1struct ContentView: View {
2 @Environment(\.managedObjectContext) var context
3 // this is the variable we added
4 @State private var taskName: String = ""
5 var body: some View {
6 // this is the TextField that we added
7 TextField("Task Name", text: $taskName)
8 }
9}
10struct ContentView_Previews: PreviewProvider {
11 static var previews: some View {
12 ContentView()
13 }
14}
از آنجا که ما از SwiftUI استفاده میکنیم، اگر از canvas استفاده کنید، میتوانید بدون الزام به اجرای اپلیکیشن در شبیهساز، UI اپلیکیشن را ببینید. در کد فوق ابتدا یک مشخصه state با استفاده از پوشش مشخصه State@ تعریف کردیم. بنابراین taskName یک مقدار binding است. یک مشخصه state باید مقدار را در taskName ذخیره کند و به ویو امکان میدهد که موارد تغییر مقادیر را مشاهده کرده و بهروزرسانی کند.
3.2 افزودن وظیفه به Core Data
ابتدا باید یک دکمه اضافه کنیم تا وقتی که کاربر، وظیفه را وارد کرد، بتواند با فشردن آن، وظیفه را در لیست خود اضافه کند. به این منظور باید TextField را در یک HStack قرار دهیم و سپس یک ()Button اضافه کنیم. زمانی که دکمه را اضافه میکنیم، اکشن آن باید ()self.addTask باشد و برچسب دکمه نیز به صورت Text("Add Task") است. کد ما در body به صورت زیر است:
1var body: some View {
2 HStack{
3 TextField("Task Name", text: $taskName)
4 Button(action: {
5 self.addTask()
6 }){
7 Text("Add Task")
8 }
9 }
10}
اکنون Xcode خطایی به صورت زیر نمایش میدهد:
1Value of type 'ContentView' has no member 'addTask',
معنای آن این است که باید تابع ()addTask را اضافه کنیم. پس از متغیر body کد زیر را اضافه کنید:
1func addTask() {
2 let newTask = Task(context: context)
3 newTask.id = UUID()
4 newTask.isComplete = false
5 newTask.name = taskName
6 newTask.dateAdded = Date()
7 do {
8 try context.save()
9 } catch {
10 print(error)
11 }
12}
در کد فوق در ()addTask یک شیء جدید Task اضافه کرده و سپس خصوصیتهای متناظر را به مقادیر newTask میدهیم. در ادامه از ()save استفاده میکنیم تا آن را به Core Data اضافه کنیم. اینک UI ما به صورت زیر در آمده است: