DevGang
Авторизоваться

Создание ToDo приложения с помощью SwiftUI и Cosmic

В этой статье мы рассмотрим особенно мощную комбинацию технологий - 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. Ниже описаны шаги по настройке новой учетной записи:

  1. Перейдите на официальный сайт компании Cosmic.
  2. Нажмите на кнопку Sign Up ("Зарегистрироваться").
  3. Зарегистрироваться можно с помощью учетной записи GitHub, Google или вручную, введя свои данные.
  4. После регистрации и входа в систему создайте пустой проект и Bucket (наш "контейнер данных").
  5. Сохраните метку Bucket и ключ API для чтения/записи, найденные в разделе Bucket > Settings > API keys.

Теперь вы настроены и готовы приступить к созданию приложения со списком дел!

Обзор SwiftUI

SwiftUI - это современный кроссплатформенный декларативный фреймворк пользовательского интерфейса от компании Apple, представленный в 2019 году. Он используется для разработки пользовательских интерфейсов для платформ Apple - iOS, iPadOS, macOS, watchOS и tvOS. SwiftUI использует такие возможности языка Swift, как дженерики и вывод типов, для обеспечения эффективной работы пользовательского интерфейса. Вот некоторые особенности:

  1. Декларативный синтаксис: SwiftUI использует декларативный синтаксис, то есть вам достаточно указать, что вы хотите видеть в пользовательском интерфейсе, и SwiftUI гарантирует, что ваш интерфейс будет соответствовать этому состоянию.
  2. Инструменты проектирования: SwiftUI был создан с учетом взаимной связи между кодом и дизайном. Он обеспечивает предварительный просмотр пользовательского интерфейса в реальном времени, который обновляется по мере изменения кода. Эта мощная функция позволяет разработчикам видеть изменения в реальном времени при создании пользовательского интерфейса.
  3. Кроссплатформенность: С помощью SwiftUI вы можете один раз разработать и развернуть систему на всех платформах Apple.
  4. Доступность: В SwiftUI встроены такие функции, как голосовое сопровождение, увеличенный шрифт и коэффициент контрастности. Это позволяет разработчикам создавать доступные приложения без излишнего количества шаблонов.

В нашей задаче по созданию приложения для составления списка дел SwiftUI будет обрабатывать все аспекты пользовательского интерфейса, делая дизайн нашего приложения более рациональным и интуитивно понятным.

Cosmic CMS

Cosmic - это облачная система управления содержимым (CMS), позволяющая разработчикам более эффективно управлять содержимым своих приложений. Она позволяет создавать и управлять содержимым приложения простым и интуитивно понятным способом.

RESTful API: Cosmic предоставляет RESTful API, позволяющий легко интегрироваться с любыми веб- или мобильными приложениями. Cosmic SDK для Swift абстрагирует REST API для управления CRUD-операциями в нашем приложении, что мы и будем использовать в нашем приложении.

Для нашего приложения ToDo мы будем использовать Cosmic в качестве бэкенда для выполнения всех CRUD-операций, где будут храниться и управляться наши данные.

Создание приложения ToDo

Настройка Xcode

Начните с создания базового проекта приложения в Xcode.

После завершения работы у вас должно получиться простое окно "Hello, world!". Вы можете обнаружить, что по умолчанию установлена macOS, поскольку мы выбрали проект мультиплатформенного приложения. Поэтому можно просто выбрать цель iPhone из списка Targets.

Интеграция Cosmic

Итак, для начала мы настроим Cosmic, создав модель контента. Эту модель мы также сопоставим с проектом SwiftUI, когда перейдем к нему. Мне нравится сначала настраивать модель в Cosmic, чтобы предварительно заполнить ее некоторыми данными-заполнителями, которые помогут при тестировании откликов и создании пользовательского интерфейса.

Во время начальной настройки вы перейдете к созданию первого типа контента, остановившись на варианте "Multiple", поскольку мы хотим хранить несколько ToDo. Называйте его как хотите, но "ToDo", вероятно, будет наиболее безопасным.

Наша основная структура будет выглядеть следующим образом:

|— title
    [Metadata]
    |— is_completed
    |— due_date
    |— priority

Название каждого объекта будет тем, что мы заполняем при создании нового элемента ToDo, затем у нас будет возможность отметить его как завершенный или нет (по умолчанию - нет, или ложь), установить дату выполнения и, наконец, приоритет (низкий, средний или высокий).

Это дает нам множество приятных вещей, которые мы можем реализовать в нашем пользовательском интерфейсе, чтобы облегчить сортировку и стилизацию.

Давайте создадим пару примеров ToDo, чтобы заполнить их небольшим количеством данных.

Мы даже можем просматривать список дел на панели Cosmic, почти так же, как в нашем приложении.

Получение данных

Мы знаем, что в конечном итоге у нас будет несколько пунктов 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
    )
)

Затем мы объявляем две опубликованные переменные, что означает, что наше приложение слушает изменения от них как часть их наблюдаемости.

  1. Первая - это наш массив объектов, который мы ремапируем в простую переменную items
  2. Вторая - это переменная ошибок, которая позволяет публиковать и перехватывать любые ошибки модели представления, предоставляемые 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 и внесем в него некоторые изменения. Для начала удалим весь шаблонный код и добавим в него следующий код.

  1. Нам необходимо объявить @StateObject, поскольку это представление будет нашим основным источником просмотра и обновления данных
  2. Сделав это, мы можем просмотреть наши данные в цикле внутри списка
  • Попав туда, мы можем выполнять итерации над этими объектами и получать нужные нам данные
  • На данный момент мы просто получим название каждого 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.

Обратите внимание, что порядок отражает то, что вы видите в Cosmic. Попробуйте переписать элементы, а затем снова создать и запустить приложение, оно должно обновиться!

Проектирование пользовательского интерфейса с помощью SwiftUI

Итак, настало время добавить еще кое-что в пользовательский интерфейс и сделать его немного лучше. Мы не будем вечно зацикливаться на внешнем виде и отдадим предпочтение использованию собственных компонентов SwiftUIs.

Однако нам необходимо написать несколько вспомогательных функций. С их помощью мы можем сопоставить нашу строку приоритетов и вернуть две вещи:

  1. Замена приоритетного слова на восклицательный знак
  2. Цветовой оттенок для улучшения видимости
// 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)
            }
        }
    }
}

Теперь при нажатии кнопки "плюс" должна появиться небольшая форма.

Представление наших данных

Теперь, чтобы действительно отправить наши данные, нам нужно сделать еще один вызов 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)

Это выглядит лучше!

Мы также выделим 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, чтобы он был синхронизирован между использованиями.

На данный момент мы сделаем всю ячейку сенсорной целью для завершения, но в будущем вы можете изменить это, если вам понадобится другая функциональность.

  1. Добавьте новое требование в ToDoCell.swift для получения текущего id элемента
  2. Оберните всю ячейку в кнопку, где пользовательский интерфейс является свойством label кнопки
  3. Добавьте в действие кнопки новую функцию, которую мы создадим для обновления нашего ToDo
  4. Добавьте новую переменную 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.

Создание кросс-платформенного приложения

В настоящее время оно не является идеальным, и в 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.

Источник:

Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу