Создание ToDo приложения с помощью SwiftUI и Cosmic
![](/static/storage/24860564344150228573484249524006518512.png)
В этой статье мы рассмотрим особенно мощную комбинацию технологий - SwiftUI и Cosmic CMS - для создания кроссплатформенного приложения составления списка дел.
SwiftUI, детище Apple с 2019 года, - это инновационный и интуитивно понятный набор инструментов пользовательского интерфейса, позволяющий разработчикам создавать пользовательские интерфейсы на одной платформе и обмениваться кодом на всех платформах. Благодаря простоте и эффективности дизайна он открывает новую эру разработки приложений.
Но что такое отличный пользовательский интерфейс, если мы не можем управлять контентом и обеспечивать его бесперебойную работу? Именно здесь на помощь приходит наш следующий инструмент - Cosmic CMS. Cosmic, известный как headless CMS (система управления контентом), позволяет нам с легкостью управлять контентом на различных платформах.
Речь идет о кроссплатформенном приложении ToDo - фундаментальном инструменте для повышения продуктивности, который помогает нам следить за тем, что необходимо сделать. В ходе этого путешествия мы рассмотрим основные возможности и преимущества приложения для работы со списком дел, такие как управление задачами, организация работы и повышение производительности. Это отличный способ понять CRUD-операции (Create, Read, Update, Delete) ((Создать, Прочесть, Обновить, Удалить)) и оценить, как SwiftUI и Cosmic могут обеспечить работу такого приложения.
Это уникальное сочетание кроссплатформенных возможностей SwiftUI и эффективного управления контентом Cosmic позволит нам создать действительно надежное приложение для работы со списками дел. Так что пристегните ремни и давайте вместе наслаждаться поездкой!
Предварительные условия
Прежде чем приступить к созданию приложения, необходимо установить и настроить некоторые необходимые инструменты. Вот шаги, которые необходимо выполнить:
Требования к установке и настройке
1. Xcode:
Xcode - это интегрированная среда разработки (IDE) для написания, компиляции и отладки Swift-кода. Ее можно загрузить непосредственно из Mac App Store. Убедитесь, что установлена самая последняя версия для оптимальной производительности и использования новейших функций.
2. iOS SDK:
iOS SDK (Software Development Kit) поставляется в комплекте с Xcode и позволяет разработчикам создавать, тестировать и отлаживать приложения для iOS. Убедитесь, что пакет iOS SDK обновлен в настройках параметров Xcode.
3. Swift:
Swift - это надежный и интуитивно понятный язык программирования, созданный компанией Apple для разработки под iOS. Установив последнюю версию Xcode, вы неизбежно получите самую последнюю стабильную версию Swift.
4. Cosmic SDK Swift:
Cosmic SDK для Swift - это простой и элегантный способ управления CRUD-операциями из вашего приложения Swift или SwiftUI.
Настройка новой учетной записи Cosmic CMS и создание нового проекта
Cosmic - это CMS, которую мы будем использовать для хранения данных и управления нашим приложением ToDo. Ниже описаны шаги по настройке новой учетной записи:
- Перейдите на официальный сайт компании Cosmic.
- Нажмите на кнопку
Sign Up
("Зарегистрироваться"). - Зарегистрироваться можно с помощью учетной записи GitHub, Google или вручную, введя свои данные.
- После регистрации и входа в систему создайте пустой проект и Bucket (наш "контейнер данных").
- Сохраните метку Bucket и ключ API для чтения/записи, найденные в разделе Bucket > Settings > API keys.
Теперь вы настроены и готовы приступить к созданию приложения со списком дел!
Обзор SwiftUI
SwiftUI - это современный кроссплатформенный декларативный фреймворк пользовательского интерфейса от компании Apple, представленный в 2019 году. Он используется для разработки пользовательских интерфейсов для платформ Apple - iOS, iPadOS, macOS, watchOS и tvOS. SwiftUI использует такие возможности языка Swift, как дженерики и вывод типов, для обеспечения эффективной работы пользовательского интерфейса. Вот некоторые особенности:
- Декларативный синтаксис: SwiftUI использует декларативный синтаксис, то есть вам достаточно указать, что вы хотите видеть в пользовательском интерфейсе, и SwiftUI гарантирует, что ваш интерфейс будет соответствовать этому состоянию.
- Инструменты проектирования: SwiftUI был создан с учетом взаимной связи между кодом и дизайном. Он обеспечивает предварительный просмотр пользовательского интерфейса в реальном времени, который обновляется по мере изменения кода. Эта мощная функция позволяет разработчикам видеть изменения в реальном времени при создании пользовательского интерфейса.
- Кроссплатформенность: С помощью SwiftUI вы можете один раз разработать и развернуть систему на всех платформах Apple.
- Доступность: В SwiftUI встроены такие функции, как голосовое сопровождение, увеличенный шрифт и коэффициент контрастности. Это позволяет разработчикам создавать доступные приложения без излишнего количества шаблонов.
В нашей задаче по созданию приложения для составления списка дел SwiftUI будет обрабатывать все аспекты пользовательского интерфейса, делая дизайн нашего приложения более рациональным и интуитивно понятным.
Cosmic CMS
Cosmic - это облачная система управления содержимым (CMS), позволяющая разработчикам более эффективно управлять содержимым своих приложений. Она позволяет создавать и управлять содержимым приложения простым и интуитивно понятным способом.
RESTful API: Cosmic предоставляет RESTful API, позволяющий легко интегрироваться с любыми веб- или мобильными приложениями. Cosmic SDK для Swift абстрагирует REST API для управления CRUD-операциями в нашем приложении, что мы и будем использовать в нашем приложении.
Для нашего приложения ToDo мы будем использовать Cosmic в качестве бэкенда для выполнения всех CRUD-операций, где будут храниться и управляться наши данные.
Создание приложения ToDo
Настройка Xcode
Начните с создания базового проекта приложения в Xcode.
![](/static/storage/237565151258131488899422437137046642423.png)
![](/static/storage/76250025917569093259777649939194195771.png)
![](/static/storage/175462255771364592821699109813925639657.png)
После завершения работы у вас должно получиться простое окно "Hello, world!". Вы можете обнаружить, что по умолчанию установлена macOS, поскольку мы выбрали проект мультиплатформенного приложения. Поэтому можно просто выбрать цель iPhone из списка Targets.
![](/static/storage/239435450074406822649444911348424652735.png)
Интеграция Cosmic
Итак, для начала мы настроим Cosmic, создав модель контента. Эту модель мы также сопоставим с проектом SwiftUI, когда перейдем к нему. Мне нравится сначала настраивать модель в Cosmic, чтобы предварительно заполнить ее некоторыми данными-заполнителями, которые помогут при тестировании откликов и создании пользовательского интерфейса.
Во время начальной настройки вы перейдете к созданию первого типа контента, остановившись на варианте "Multiple", поскольку мы хотим хранить несколько ToDo. Называйте его как хотите, но "ToDo", вероятно, будет наиболее безопасным.
![](/static/storage/58299984497366316709758259203758056044.png)
Наша основная структура будет выглядеть следующим образом:
|— title
[Metadata]
|— is_completed
|— due_date
|— priority
Название каждого объекта будет тем, что мы заполняем при создании нового элемента ToDo, затем у нас будет возможность отметить его как завершенный или нет (по умолчанию - нет, или ложь), установить дату выполнения и, наконец, приоритет (низкий, средний или высокий).
Это дает нам множество приятных вещей, которые мы можем реализовать в нашем пользовательском интерфейсе, чтобы облегчить сортировку и стилизацию.
Давайте создадим пару примеров ToDo, чтобы заполнить их небольшим количеством данных.
![](/static/storage/256460219916443742712264776921395726472.png)
Мы даже можем просматривать список дел на панели Cosmic, почти так же, как в нашем приложении.
![](/static/storage/122834707458811673540282494818540594253.png)
Получение данных
Мы знаем, что в конечном итоге у нас будет несколько пунктов todo, и поэтому нам нужно будет перебрать их и представить в виде списка. К счастью, Cosmic SDK для Swift и SwiftUI уже настроен таким образом, чтобы справиться с этой задачей.
Теперь мы можем написать наш вызов API для получения данных.
Примечание: я поместил свои учетные данные Cosmic в файл 'Secrets', чтобы не раскрывать их в процессе обучения, но учтите, что такой способ хранения небезопасен и не должен быть зафиксирован на GitHub.
Для начала создадим новый файл ToDoViewModel.swift
. Выберем стандартный Swift-тип, поскольку доступ к SwiftUI нам здесь не нужен.
Теперь необходимо добавить SDK. Начните с меню File и выберите пункт 'Add package dependencies...".
Затем в появившемся окне в строку поиска вставьте url GitHub.
https://github.com/cosmicjs/CosmicSDKSwift
После добавления нам нужно просто импортировать его в файлы, в которых мы работаем. В данном случае мы должны включить его в верхнюю часть нашего файла.
import CosmicSDK
После этого создадим class
для нашей модели и сделаем его наблюдаемым (Observable).
class ToDoViewModel: ObservableObject {
private let BUCKET = CONST_BUCKET
private let READ_KEY = CONST_READ_KEY
private let WRITE_KEY = CONST_WRITE_KEY
private let TYPE = CONST_TYPE
Мы также объявим несколько новых локальных констант, которые будут браться из наших секретных файлов. Это делается для того, чтобы упростить процесс и сохранить конфиденциальность файлов.
Затем мы можем инициировать каждый из наших глобальных файлов непосредственно в методе Cosmic createBucketClient
. Это связано с тем, что нам необходимо инициировать клиент bucket до того, как наши переменные станут доступны приложению.
private let cosmic = CosmicSDKSwift(
.createBucketClient(
bucketSlug: CONST_BUCKET,
readKey: CONST_READ_KEY,
writeKey: CONST_WRITE_KEY
)
)
Затем мы объявляем две опубликованные переменные, что означает, что наше приложение слушает изменения от них как часть их наблюдаемости.
- Первая - это наш массив объектов, который мы ремапируем в простую переменную
items
- Вторая - это переменная ошибок, которая позволяет публиковать и перехватывать любые ошибки модели представления, предоставляемые SDK
@Published var items: [Object] = []
@Published var error: ViewModelError?
enum ViewModelError: Error {
case decodingError(Error)
case cosmicError(Error)
}
Внутри нашей функции fetchData()
мы используем метод find()
для получения наших данных и передачи их в массив items
.
func fetchData() {
cosmic.find(type: TYPE) { results in
switch results {
case .success(let cosmicSDKResult):
DispatchQueue.main.async {
self.items = cosmicSDKResult.objects
}
case .failure(let error):
print(error)
}
}
}
Обратите внимание, что необходимо написать соответствующую обработку ошибок, но я знаю, что мы в безопасности, поэтому оставил всё как есть.
// ToDoViewModel.swift
import Foundation
import CosmicSDK
class ToDoViewModel: ObservableObject {
private let BUCKET = CONST_BUCKET
private let READ_KEY = CONST_READ_KEY
private let WRITE_KEY = CONST_WRITE_KEY
private let TYPE = CONST_TYPE
private let cosmic = CosmicSDKSwift(
.createBucketClient(
bucketSlug: CONST_BUCKET,
readKey: CONST_READ_KEY,
writeKey: CONST_WRITE_KEY
)
)
@Published var items: [Object] = []
@Published var error: ViewModelError?
enum ViewModelError: Error {
case decodingError(Error)
case cosmicError(Error)
}
func fetchData() {
cosmic.find(type: TYPE) { results in
switch results {
case .success(let cosmicSDKResult):
DispatchQueue.main.async {
self.items = cosmicSDKResult.objects
}
case .failure(let error):
print(error)
}
}
}
}
Для того чтобы правильно декодировать данные, нам необходимо расширить предоставляемый SDK Object, включив в него специфические ключи метаданных, чтобы в дальнейшем их было проще использовать в нашем коде. Хотя это необязательный шаг, он позволяет значительно упростить работу с выходными данными.
Внутри файла ToDoViewModel, в самом низу, добавьте следующее расширение. Все, что это делает, - сообщает нашему коду, что мы можем ожидать возврата определенных необязательных метаданных от API, а затем мы сопоставляем их с новыми переменными, которые легче понять и использовать.
// Model
extension Object {
var metadata_due_date: String? {
return self.metadata?["due_date"]?.value as? String
}
var metadata_priority: String? {
return self.metadata?["priority"]?.value as? String
}
var metadata_is_completed: Bool? {
return self.metadata?["is_completed"]?.value as? Bool
}
}
Просмотр в нашем приложении
Давайте перейдем к нашему файлу ContentView.swift
и внесем в него некоторые изменения. Для начала удалим весь шаблонный код и добавим в него следующий код.
- Нам необходимо объявить
@StateObject
, поскольку это представление будет нашим основным источником просмотра и обновления данных - Сделав это, мы можем просмотреть наши данные в цикле внутри списка
- Попав туда, мы можем выполнять итерации над этими объектами и получать нужные нам данные
- На данный момент мы просто получим название каждого todo
// ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ToDoViewModel()
var body: some View {
VStack {
List(viewModel.items, id: \.id) { todo in
Text(todo.title)
}
}
.padding()
.onAppear {
viewModel.fetchData()
}
}
}
#Preview {
ContentView()
}
Если вы соберете и запустите его, то увидите следующее. Это не выглядит ужасно, но и не замечательно. Кроме того, у нас еще нет полноценной CRUD-функциональности, в настоящее время у нас есть только Read
.
![](/static/storage/267021452848201648521779849513872104233.png)
Обратите внимание, что порядок отражает то, что вы видите в Cosmic. Попробуйте переписать элементы, а затем снова создать и запустить приложение, оно должно обновиться!
Проектирование пользовательского интерфейса с помощью SwiftUI
Итак, настало время добавить еще кое-что в пользовательский интерфейс и сделать его немного лучше. Мы не будем вечно зацикливаться на внешнем виде и отдадим предпочтение использованию собственных компонентов SwiftUIs.
Однако нам необходимо написать несколько вспомогательных функций. С их помощью мы можем сопоставить нашу строку приоритетов и вернуть две вещи:
- Замена приоритетного слова на восклицательный знак
- Цветовой оттенок для улучшения видимости
// ContentView.swift
func replaceTextWithValue(priority: String) -> String {
switch priority {
case "Low":
return "!"
case "Medium":
return "!!"
case "High":
return "!!!"
default:
return "?"
}
}
func determineColor(priority: String) -> Color {
switch priority {
case "Low":
return Color.yellow
case "Medium":
return Color.orange
case "High":
return Color.red
default:
return Color.gray
}
}
А вот наш полностью обновленный код ContentView
.
// ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ToDoViewModel()
var body: some View {
NavigationStack {
VStack {
List(viewModel.items, id: \.id) { todo in
VStack {
HStack {
Text(todo.title)
Spacer()
Image(systemName: "checkmark.circle")
.foregroundStyle(.secondary)
}
HStack {
Text(todo.metadata_due_date)
.font(.caption)
.foregroundStyle(.secondary)
Spacer()
Text(replaceTextWithValue(priority: todo.metadata_priority))
.fontWeight(.black)
.foregroundStyle(determineColor(priority: todo.metadata_priority))
}
}
}
}
.navigationTitle("Cosmic ToDos")
.onAppear {
viewModel.fetchData()
}
}
}
func replaceTextWithValue(priority: String) -> String {
switch priority {
case "Low":
return "!"
case "Medium":
return "!!"
case "High":
return "!!!"
default:
return "?"
}
}
func determineColor(priority: String) -> Color {
switch priority {
case "Low":
return Color.yellow
case "Medium":
return Color.orange
case "High":
return Color.red
default:
return Color.gray
}
}
}
#Preview {
ContentView()
}
Операции CRUD с помощью Cosmic
Теперь, когда мы разобрались с этим, можно подумать о реализации других операций CRUD. Начнем с создания нового ToDo.
Операция создания
В верхней части нашего файла сначала нужно объявить новое состояние для отображения листа, которое позволит нам добавить новый todo.
Вставьте @State var showAddNewSheet = false
.
Затем мы можем использовать его для переключения отображения листа, добавив модификатор sheet и привязав его к нашей переменной.
.sheet(isPresented: $showAddNewSheet) {…}
Создайте новый файл SwiftUI с именем AddNewTodoView
.
В этом окне мы будем наблюдать за нашим объектом StateObject, вызывая @ObservedObject
, который мы привяжем к новой переменной.
@ObservedObject var viewModel: ToDoViewModel
В этом новом файле мы инициализируем три переменные, которые будут хранить название, дату выполнения и приоритет. Я не буду вдаваться в подробности того, как все это работает, но это базовая форма, которая позволяет добавить соответствующие элементы, необходимые для работы нашей операции создания.
// AddNewTodoView.swift
struct AddNewTodoView: View {
@ObservedObject var viewModel: ToDoViewModel
@Environment(\.dismiss) private var dismiss
@State private var todoTitle: String = ""
@State private var todoDueDate: Date = Date()
@State private var todoDueDateString: String = ""
enum Priority: String, CaseIterable, Identifiable {
case low, medium, high
var id: Self { self }
}
@State private var selectedToDo: Priority = .low
#if os(iOS)
let update = UINotificationFeedbackGenerator()
#endif
var body: some View {
NavigationStack {
Form {
TextField("Enter a title", text: $todoTitle, axis: .vertical)
DatePicker("Pick a date", selection: $todoDueDate, displayedComponents: .date)
Picker("", selection: $todoPriority, content: {
ForEach(Priority.allCases) { priority in
Text(priority.rawValue.capitalized)
}
})
.pickerStyle(.segmented)
}
.navigationBarTitle("Add new ToDo")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button {
update.notificationOccurred(.success)
self.dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.viewModel.createToDo()
}
} label: {
Text("Add")
.bold()
}
}
ToolbarItem(placement: .cancellationAction) {
Button {
self.dismiss()
} label: {
Text("Close")
}
}
}
.onAppear {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
dateFormatter.locale = Locale.init(identifier: "en_GB")
todoDueDateString = dateFormatter.string(from: todoDueDate)
}
}
}
}
Теперь при нажатии кнопки "плюс" должна появиться небольшая форма.
![](/static/storage/224361313331713807890727015168504133323.png)
Представление наших данных
Теперь, чтобы действительно отправить наши данные, нам нужно сделать еще один вызов API в Cosmic, но на этот раз мы вставляем новый объект.
Начнем с создания новой функции в нашей модели ToDoViewModel
, которая ожидает получения заголовка, даты выполнения и приоритета из источника, в котором она вызывается. Теперь мы можем полагаться на другой метод SDK insertOne()
, который требует только type
и title
, но может принимать и необязательные параметры. В нашем случае мы будем опираться на необязательные metadata
.
// ToDoViewModel.swift
func createToDo(title: String, due_date: String, priority: String) {
cosmic.insertOne(type: TYPE, title: title, metadata: ["due_date": due_date, "priority": priority]) { results in
switch results {
case .success(_):
print("Inserted \(title)")
DispatchQueue.main.async {
self.fetchData()
}
case .failure(let error):
print(error)
}
}
}
Предоставленные сообщения печати не являются обязательными, они просто помогают нам получить обратную связь во время разработки, поскольку мы можем просмотреть консоль, чтобы увидеть, что создание прошло успешно.
Теперь в нашем AddNewTodoView
необходимо обновить вызов createToDo()
, чтобы включить в него заданные нами параметры.
// AddNewToDoView.swift
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.viewModel.createToDo(title: todoTitle, due_date: todoDueDateString, priority: selectedToDo.rawValue.capitalized)
}
Обратите внимание, что нам придется немного потрудиться. Cosmic ожидает, что дата будет строкой в формате UTF8, поэтому мы просто используем функцию dateFormatter
, чтобы присвоить значение String
из значения Date
, которое возвращает нам строковое значение. Для нашего приоритета мы берем текущий выбранный приоритет, получаем его необработанное значение (которое соответствует ожидаемому Cosmic) и пишем его заглавными буквами, чтобы оно соответствовало нашему бэкенду. Если ваш ключ не имеет заглавных букв, вы можете проигнорировать это.
// AddNewToDoView.swift
.onAppear {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
dateFormatter.locale = Locale.init(identifier: "en_GB")
todoDueDateString = dateFormatter.string(from: todoDueDate)
}
Теперь создайте и запустите приложение снова, и все должно работать, как ожидалось, и вы сможете добавить новое Todo! Вы увидите, как оно появится в Cosmic, и увидите его в пользовательском интерфейсе. Это происходит потому, что мы повторно получаем данные из API в главном потоке, используя DispatchQueue
, если создание прошло успешно.
DispatchQueue.main.async {
self.fetchData()
}
Очистка пользовательского интерфейса
Давайте улучшим форматирование даты в списке дел и сделаем его более читабельным. Я написал небольшую вспомогательную функцию, которая делает это за нас. Создайте новый файл с именем Helpers.swift
и добавьте в него эту функцию.
// Helpers.swift
import SwiftUI
func parseDate(from dateString: String) -> String? {
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withFullDate]
if let date = dateFormatter.date(from: dateString) {
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "dd MMM yyyy"
return outputFormatter.string(from: date)
} else {
return nil
}
}
Мне нравится в SwiftUI то, что не нужно импортировать файлы, Xcode достаточно умен, чтобы увидеть, что вы их использовали, и если они есть где-то в вашем проекте, он автоматически импортирует их. Отлично!
Для его использования достаточно вызвать его внутри элемента Text()
и обратиться к свойству description
.
// ContentView.swift
Text(parseDate(from: todo.metadata.due_date)?.description ?? "Invalid Date")
.font(.caption)
.foregroundStyle(.secondary)
![](/static/storage/76552526040461790640704733572774162862.png)
Это выглядит лучше!
Мы также выделим TodoCell в отдельный компонент, чтобы сделать более чистым наш ContentView
.
Во-первых, переместите две функции в ContentView
, которые помогают разобрать нашу информацию о приоритете, в файл Helpers.swift
. Теперь они доступны глобально.
Затем скопируйте индивидуальный код todo в только что созданный файл ToDoCell.swift
SwiftUI View.
// ToDoCell.swift
import SwiftUI
struct ToDoCell: View {
@State var title: String = ""
@State var date: String = ""
@State var priority: String = ""
var body: some View {
VStack {
HStack {
Text(title)
Spacer()
Image(systemName: "checkmark.circle")
.foregroundStyle(.secondary)
}
HStack {
Text(parseDate(from: date)?.description ?? "")
.font(.caption)
.foregroundStyle(.secondary)
Spacer()
Text(replaceTextWithValue(priority: priority))
.fontWeight(.black)
.foregroundStyle(determineColor(priority: priority))
}
}
}
}
Операция обновления
Теперь настало время выполнить обновления. Технически здесь можно обновить что угодно, но мы сосредоточимся на простом выполнении задачи и обновлении объекта Cosmic, чтобы он был синхронизирован между использованиями.
На данный момент мы сделаем всю ячейку сенсорной целью для завершения, но в будущем вы можете изменить это, если вам понадобится другая функциональность.
- Добавьте новое требование в
ToDoCell.swift
для получения текущегоid
элемента - Оберните всю ячейку в кнопку, где пользовательский интерфейс является свойством
label
кнопки - Добавьте в действие кнопки новую функцию, которую мы создадим для обновления нашего ToDo
- Добавьте новую переменную
is_completed
// ToDoCell.swift
import SwiftUI
struct ToDoCell: View {
@State var id: String = ""
@State var title: String = ""
@State var date: String = ""
@State var priority: String = ""
@State var is_completed: Bool = false
var body: some View {
Button {
viewModel.completeToDo(id: id)
} label: {
VStack {
HStack {
Text(title)
Spacer()
Image(systemName: "checkmark.circle")
.foregroundStyle(.secondary)
}
HStack {
Text(parseDate(from: date)?.description ?? "")
.font(.caption)
.foregroundStyle(.secondary)
Spacer()
Text(replaceTextWithValue(priority: priority))
.fontWeight(.black)
.foregroundStyle(determineColor(priority: priority))
}
}
}
.buttonStyle(.plain)
}
}
Теперь обновите файл ContentView.swift
, включив в него следующее:
// ContentView.swift
List(viewModel.items, id: \.id) { todo in
ToDoCell(viewModel: viewModel, id: todo.id ?? "", title: todo.title, date: todo.metadata_due_date ?? "", priority: todo.metadata_priority ?? "", is_completed: todo.metadata_is_completed ?? false)
}
Для того чтобы это работало, мы добавим еще одну функцию в наш файл ToDoViewModel.swift
. Это позволит нам обновить наш переключатель is_completed
до нового состояния. Теперь мы будем использовать метод updateOne()
SDK.
// ToDoViewModel.swift
func completeToDo(id: String, is_completed: Bool) {
cosmic.updateOne(type: TYPE, id: id, metadata: ["is_completed": is_completed]) { results in
switch results {
case .success(_):
print("Updated \(id)")
DispatchQueue.main.async {
self.fetchData()
}
case .failure(let error):
print(error)
}
}
}
Теперь у нас есть это, и мы обновим наш пользовательский интерфейс, чтобы правильно переключать состояние, таким образом мы обновим наш пользовательский интерфейс и наши данные Cosmic и будем синхронизировать их. Здесь мы используем тернарные операторы для изменения внешнего вида флажка в зависимости от состояния завершения.
// ToDoCell.swift
Button {
is_completed.toggle()
viewModel.completeToDo(id: id, is_completed: is_completed)
} label: {
VStack {
HStack {
Text(title)
Spacer()
Image(systemName: is_completed ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundStyle(is_completed ? .green : .secondary)
}
HStack {
Text(parseDate(from: date)?.description ?? "")
.font(.caption)
.foregroundStyle(.secondary)
Spacer()
Text(replaceTextWithValue(priority: priority))
.fontWeight(.black)
.foregroundStyle(determineColor(priority: priority))
}
}
}
.buttonStyle(.plain)
Операция удаления
Отлично, теперь у нас прекрасно работают функции создания, чтения и обновления. Осталось убедиться в том, что мы можем удалить любую запись. К счастью, поскольку мы использовали API List
в SwiftUI, мы можем легко воспользоваться встроенным методом удаления для удаления записи из списка и отправить запрос DELETE
в Cosmic.
Создадим последнюю функцию в нашем файле ToDoViewModel.swift
. На этот раз это будет метод deleteToDo()
.
Вы увидите, что вся наша функция очень похожа на функцию обновления: мы передаем в метод идентификатор и просто сообщаем, что хотим удалить этот идентификатор. В данном случае нам не нужно передавать ничего другого.
// ToDoViewModel.swift
func deleteToDo(id: String) {
cosmic.deleteOne(type: TYPE, id: id) { results in
switch results {
case .success(_):
print("Deleted \(id)")
DispatchQueue.main.async {
self.fetchData()
}
case .failure(let error):
print(error)
}
}
}
Теперь добавим к нашей ячейке функцию swipeAction()
, чтобы включить удаление.
Под стилем кнопки в файле ToDoCell.swift
добавьте действие пролистывания, чтобы включить функцию удаления. Поскольку мы не имеем дело с Core Data или какой-либо другой внутренней библиотекой, мы можем использовать простую кнопку с вызовом метода удаления внутри действия.
// ToDoCell.swift
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
withAnimation {
viewModel.deleteToDo(id: id)
}
} label: {
Label("Delete", systemImage: "trash")
}
}
Теперь, когда вы проводите пальцем справа налево (или слева направо, в зависимости от страны), вы получаете типичную функцию удаления, которая сохраняется при всех перезагрузках, поскольку наши данные всегда поступают из Cosmic.
![](/static/storage/285521384266749157979434913269127266790.png)
Создание кросс-платформенного приложения
В настоящее время оно не является идеальным, и в macOS, скорее всего, будут возникать ошибки, так как мы используем некоторые специфические функции для iOS. Но вы можете создать и запустить его на iPad без проблем. Оно не полностью оптимизировано, и это не идеальный пользовательский интерфейс для большого экрана, но его все равно можно использовать. Теперь можно работать над улучшением пользовательского интерфейса для iPad, поскольку вся функциональность находится здесь.
Заключение
В этом подробном руководстве мы вместе рассмотрели процесс создания кросс-платформенного приложения для составления списка дел с использованием SwiftUI и Cosmic CMS.
Мы начали с обзора SwiftUI и Cosmic CMS, ознакомились с основными возможностями и преимуществами этих современных технологий. Затем мы приступили к разработке приложения, начиная с установки и настройки необходимых условий и заканчивая проектированием пользовательского интерфейса приложения и интеграцией Cosmic для бесперебойного управления данными.
Основной раздел нашего путешествия был посвящен выполнению CRUD-операций с помощью Cosmic, где мы подробно рассмотрели создание, чтение, обновление и удаление задач. Этот практический опыт продемонстрировал универсальность и полезность как SwiftUI, так и Cosmic.
Красота и эффективность использования SwiftUI и Cosmic, надеюсь, были тщательно продемонстрированы в процессе разработки. Простота создания визуально привлекательного интерфейса с помощью SwiftUI и эффективного управления контентом с помощью Cosmic делает эту комбинацию очень рекомендуемой для любого начинающего или опытного разработчика приложений.
Благодаря тому, что мы использовали Cosmic, вы можете даже пойти дальше и разработать веб-интерфейс, используя, например, Next.js, где изменения будут отражаться и в вашем приложении!
Следующие шаги
Я надеюсь, что это руководство было для вас полезным. Зарегистрируйтесь, чтобы начать использовать Cosmic для создания контента для своих сайтов и приложений. Создавайте собственные мобильные приложения с помощью Cosmic Swift SDK.