Оглавление
Сохранение настроек в UserDefaults
Data Persistence – чтение и запись файлов
Создание файла модели Data Model
Добавление поддержки Core Data в существующий проект
Введение
В любом реальном приложении, даже самом простом, возникает необходимость хранения данных. Представим приложение Stocks, предназначенное для инвестиций в акции. Наше приложение должно отслеживать курс интересующих нас акций, а для этого необходимо где то хранить список акций, которые выбрал пользователь. Каждый раз, когда пользователь запускает приложение Stocks, оно отображает список акций, которые пользователь выбрал ранее. Если пользователь в процессе работы с приложением, удалит или добавит акции из списка отслеживаемых, приложение должно обновить сохраненный список акций, чтобы при следующем запуске отобразить актуальный список.
Другие приложения могут также сохранять различные настройки, которые позволяют индивидуально настроить внешний вид приложения, такие как цвет фона или звуки, которые проигрываются при получении сообщения или звук уведомления при получении голосового сообщения.
Cохранение или извлечение данных между запусками приложения, известно как Data Persistence(сохранение данных). Мне не нравится дословный перевод – хранение или сохранение данных. Поэтому в дальнейшем я буду использовать термин Data Persistence. Мы рассмотрим 3 варианта для хранения и извлечения данных в iOs приложении:
- UserDefaults
- Reading and writing files
- Core Data
Каждый метод предоставляет как свои преимущества, так и недостатки, в зависимости от вида тех данных, какие мы хотим сохранять. Именно на основании поставленной задачи – с какими именно данными наше приложение должно работать, мы и выбираем способ хранения данных, который мы должны использовать в приложении.
UserDefaults в основном используется для хранения небольших данных, такие как пользовательские настройки. Этот метод использует структуру данных типа словарь и хранит данные в .plist файле, похожем на Info.plist, который использует каждый XCode проект. Это простейший метод для хранения общих типов данных, такие как строки, числа, даты и структуры данных, такие как массивы словарей и лучше всего подходит для небольшого объема данных.
Если Вы хотите хранить большой объем данных, Вы можете использовать API для хранения и записи данных в файл. Он лучше подходит для хранения медиа файлов или неупорядоченных строк, например большого текста. Используя API работы с файлами, Вы должны не забывать про то, что поиск специфичных данных в большом файле может быть весьма продолжительным по времени.
CoreData позволяет вам сохранять различные типы данных в группах, называемых сущностями. Сущности похожи на записи в таблицах базы данных. Если Вам нужно сохранить большое количество структурированных данных, лучше использовать CoreData, чем предыдущие два типа хранения данных.
Сохранение настроек в UserDefaults
UserDefaults – предназначен для хранения небольших данных, таких как числа, тип Boolean или строки. Поэтому использование UserDefaults прекрасно подходит для сохранения настроек приложения, таких как цвет фона или наименование используемого шрифта в чатах вашего приложения. Использование UserDefaults включает в себя:
- Сохранение данных в UserDefaults
- Извлечение данных из UserDefaults
Для сохранения данных в UserDefaults, необходимо определить ключ и данные, которые Вы будете ассоциировать с данным ключом. Это очень похоже по своему смыслу и назначению на сохранение настроек в SharedPreferences Android SDK. Если мы хотим сохранить данные dataToSave, которые будут ассоциированы с ключом “keyString“, все что нам необходимо сделать:
UserDafaults.standart.set(dataToSave, forKey: "keyString")
Команда set сохраняет ключ и ассоциированные с ним данные. Для извлечения данных, которые Вы ранее сохранили, Вы должны знать наименование ключа и тип извлекаемых Вами данных, например Integer, Boolean или Double. Зная тип данных, который вы будете извлекать, Вы можете использовать один из следующих участков кода:
- integer(forKey:”keyString”) – возвращает целочисленное число, если ключ keyString существует, или 0 в противном случае
- bool(forKey:”keyString”) – возвращает логическое значение, если ключ существует или false в противном случае
- float(forKey:”keyString”) – возвращает число с плавающей точностью типа float, если ключ существует или 0.0 в противном случае
- string(forKey:”keyString”) – возвращает значение типа String если ключ существует или nil в противном случае
- double(forKey:”keyString”) – возвращает плавающее число с двойной точностью, типа double если ключ существует или 0.0 в противном случае
- object(forKey:”keyString”) – возвращает тип AnyObject? который Вам надо привести с условного типа к требуемому, или возвращает nil если ключ не присутствует в хранилище
- url(forKey:”keyString”) – возвращает тип URL если ключ присутствует в хранилище данных или nil в противном случае
Посмотрим как сохранять данные в UserDefaults на практике:
- Создадим новый проект Chapter6 для iOs приложения и убедимся что мы выбрали тип интерфейса SwiftUI
- Откроем исходный файл ContentView в панели Навигатора
- Добавим 3 State переменные:
@State private var myText = "" @State private var myToggle = true @State private var mySlider = 0.0
- Добавим контейнер VStack в тело var body: Some View. Также добавим отступ .padding у компонента VStack и определим отступаемое пространство в 25:
var body: some View { VStack(spacing: 25) { } .padding() }
- Добавим UI компоненты TextField, Toggle и Slider внутри контейнера VStack. Эти компоненты будут отвечать за изменение значений и сохранение их в UserDefaults:
var body: some View { VStack(spacing: 25) { TextField("Change text here", text: $myText) Toggle(isOn: $myToggle, label: { Text("Toggle here") }) Slider(value: $mySlider) } .padding() }
- Под компонентом Slider добавим 3 кнопки. Первая кнопка сохраняет данные, вторая кнопка возвращает пользовательский интерфейс к первоначальному состоянию, а третья извлекает сохраненные данные.
Button(action: { UserDefaults.standard.set(myText, forKey: "Text") UserDefaults.standard.set(myToggle, forKey: "Toggle") UserDefaults.standard.set(mySlider,forKey: "Slider") }){ Text("Save data") } Button(action: { myText = "" myToggle = true mySlider = 0.0 }){ Text("Clear data") } Button(action: { myText = UserDefaults.standard.string(forKey: "Text") ?? "" myToggle = UserDefaults.standard.bool(forKey: "Toggle") mySlider = UserDefaults.standard.double(forKey: "Slider") }){ Text("Retrieve data") }
Обратите внимание, что первая кнопка использует команду UserDefaults.standard.set для сохранения данных. А третья кнопка использует команды UserDefaults.standard.string, .bool или .double для извлечения данных, сохраненных при нажатии на первую кнопку.
- Нажмем на кнопку предварительного просмотра на панели Canvas
- В компоненте TextView введем любой набор слов или букв
- Нажмем на Toggle
- Переместим слайдер вправо
- Нажмем на кнопку Save Data для сохранения текущих значений переменных в компонентах TextField, Toggle и Slider
- Теперь нажмем на кнопку Clear Button для сброса состояния всех компонентов UI к первоначальному
- Нажмем на кнопку Retrieve Data для восстановления значений переменных и связанными с ними компонентами UI
Полный исходный код файла ContentView:
import SwiftUI struct ContentView: View { @State private var myText = "" @State private var myToggle = true @State private var mySlider = 0.0 var body: some View { VStack(spacing: 25) { TextField("Change text here", text: $myText) Toggle(isOn: $myToggle, label: { Text("Toggle here") }) Slider(value: $mySlider) Button(action: { UserDefaults.standard.set(myText, forKey: "Text") UserDefaults.standard.set(myToggle, forKey: "Toggle") UserDefaults.standard.set(mySlider,forKey: "Slider") }){ Text("Save data") } Button(action: { myText = "" myToggle = true mySlider = 0.0 }){ Text("Clear data") } Button(action: { myText = UserDefaults.standard.string(forKey: "Text") ?? "" myToggle = UserDefaults.standard.bool(forKey: "Toggle") mySlider = UserDefaults.standard.double(forKey: "Slider") }){ Text("Retrieve data") } } .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }Исходный код проекта Вы можете скачать с нашего GitHub’a
Data Persistence – чтение и запись файлов
В обычных компьютерах, например в Macintosh или под управлением ОС Linux/Windows мы ежедневно выполняем сотни операций с файлами, даже не задумываясь об этом. Конечно такие же возможности нам дает и iOs. Мы точно также можем создавать файлы, читать и записывать данные.
И хотя iOs сильно инкапсулирует от пользователя работу с файлами, это не так уж и сложно в исполнении. Во первых при работе с файлами мы должны создать объект FileManager:
let fm = FileManager.default
Далее мы должны определить местоположение файл, в нашем случае это папка с документами в домашней директории
let urls = fm.urls(for: .documentDirectory, in: .userDomainMask)
И наконец мы создаем файл, который называется file.txt для хранения данных
let url = urls.last?.appendingPathComponent("file.txt")
После того как мы сохраним текст в файле, мы можем также его извлечь используя все тот же FileManager снова и найти сторонними приложениями наш файл в папке document домашней директории. Теперь посмотрим на практике, как сохранять и читать данные в файл в ОС iOs:
- Создадим новый проект – Chapter6ReadAndWriteFile
- Откроем для редактирования файл с исходным кодом ContentView в панели Навигатора
- Добавим 2 State переменные
@State var createText = "" @State var displayText = ""
- Внутри компонента VStack разместим 2 компонента TextEditor, разделенные пустым контейнером HStack:
var body: some View { VStack { TextEditor(text: $createText) HStack{ }.padding() TextEditor(text: $displayText) } }
- И добавим внутрь нашего пока еще пустого контейнера HStack 2 кнопки, разделенные пустым элементом Spacer:
Button(action: { let fm = FileManager.default let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) let url = urls.last?.appendingPathComponent("file.txt") do { try createText.write(to: url!, atomically: true, encoding: String.Encoding.utf8) }catch{ print("File writing error") } }){ Text("Write file") } Spacer() Button(action: { let fm = FileManager.default let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) let url = urls.last?.appendingPathComponent("file.txt") do{ let fileContent = try String(contentsOf: url!, encoding: String.Encoding.utf8) displayText = fileContent }catch{ print("File reading error") } }){ Text("Read file") }
Итоговый исходный файл выглядит следующим образом:
import SwiftUI struct ContentView: View { @State var createText = "" @State var displayText = "" var body: some View { VStack { TextEditor(text: $createText) HStack{ Button(action: { let fm = FileManager.default let urls = fm.urls(for: .documentDirectory, in: .userDomainMask ) let url = urls.last?.appendingPathComponent("file.txt") do { try createText.write(to: url!, atomically: true, encoding: String.Encoding.utf8) }catch{ print("File writing error") } }){ Text("Write file") } Spacer() Button(action: { let fm = FileManager.default let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) let url = urls.last?.appendingPathComponent("file.txt") do{ let fileContent = try String(contentsOf: url!, encoding: String.Encoding.utf8) displayText = fileContent }catch{ print("File reading error") } }){ Text("Read file") } }.padding() TextEditor(text: $displayText) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
- Нажмите на иконку Preview на панели Canvas
- В верхнем UI элементе TextEditor введите любой текст
- Нажмите на кнопку WriteFile, после этого текст сохранится в файле
- Теперь нажмите на кнопку Read File. После этого во втором TextEditor появится текст, прочитанный из файла file.txt
Проект Вы можете посмотреть или скачать по адресу: https://github.com/timur-gaysin/Chapter6ReadAndWrireFile
Использование Core Data
Для хранения небольших данных, идеально подходит использование UserDefaults. Если требуется хранить большие объемы неструктурированных данных, можно хранить их в файле. Однако если у Вас стоит задача хранения больших массивов данных, лучшим решением будет использовать Core Data. По своей архитектуре это сродне работе с SQLData в AndroidSDK.
Core Data это фреймворк, который помогает выполнять весь спектр задач по хранению и извлечению данных в приложении. Он позволяет легко организовывать доступ к структурированным данным и отношениям внутри этих данных, без заучивания непростых SQL запросов.
Core Data организовывает хранилище, опираясь на термины сущностей и аттрибутов. Атрибуты определяют одиночную запись в таблице, такие как ее имя, адрес, возраст, пол, email и телефонный номер. Сущность представляет все эти атрибуты как определение единицы данных, такой как например персона. Проще всего представлять сущность Core Data как запись в таблице базы данных, а аттрибут как поле БД, для понимания мы представили рисунок 6.1
Рисунок 6.1 Core Data хранит данные в атрибутах. сгруппированных вместе в единой сущности
Для того чтобы использовать Core Data необходимо выполнение 2-х условий:
- Создание сущностей и определение атрибутов в редакторе XCode данных модели
- Написание Swift кода для работы с данными
Есть 2 пути для добавления Core Data в проект. Первый – мы добавляем CoreData после создания проекта. Второй – мы добавляем Core Data во время создания проекта. Теперь рассмотрим подробнее оба варианта.
Создание файла модели Data Model
Файл Core Data – это модель данных, которая описывает структуру данных и атрибуты, представляющие единицы объекта, такие как имя, номер телефона, адрес и т.д. Мы можем вручную добавить файл с данными модели в любой проект или позволить XCode добавить модель данный когда создаете новый проект.
Посмотрим как добавить файл Core Data при создании проекта:
- Создадим новый iOs проект в среде XCode. Назовем проект CoreDataApp. При создании обратите внимание чтобы CheckBox был снят напротив Core Data.
- Создадим новый файл
- При создании файла выбираем iOs категорию
- Среди шаблонов выбираем Data Model в секции Core Data
- Нажмем кнопку Next
- Выберем имя файла DataModel и нажмем кнопку Create. XCode отобразит Ваш созданный Core Data файл в панели Навигатор
После создания файла Core Data, следующий шаг это определение одной или нескольких сущностей. Сущность содержит множество атрибутов, такие как имя, цена, возраст или телефонный номер. Думайте о сущности, как о записи в базе данных и атрибутах, какие как поля, как указано на Рисунке 6.3
Рисунок 6.3 Сущность может содержать множество атрибутов
Если Вы перед началом работы с приложением уже знаете, что будете использовать Core Data для хранения данных приложения, в этом случае при создании проекта, XCode автоматически сгенерирует файл DataModel и код для доступа к данным. Посмотрим как это выглядит на практике:
- Создадим новый iOs проект
- Дадим ему имя CoreDataProject
- Убедимся, что Checkbox установлен напротив надписи Use Core Data
- Нажимаем Next и далее Create
- После создания проекта, откроем файл Persistence в панели Навигатора. Когда Вы создаете новый проект с Core Data, XCode всегда генерирует следующий код:
// // Persistence.swift // CoreDataProject // // Created by Timur on 20.03.2023. // import CoreData struct PersistenceController { static let shared = PersistenceController() static var preview: PersistenceController = { let result = PersistenceController(inMemory: true) let viewContext = result.container.viewContext for _ in 0..<10 { let newItem = Item(context: viewContext) newItem.timestamp = Date() } do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } return result }() let container: NSPersistentContainer init(inMemory: Bool = false) { container = NSPersistentContainer(name: "CoreDataProject") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. /* Typical reasons for an error here include: * The parent directory does not exist, cannot be created, or disallows writing. * The persistent store is not accessible, due to permissions or data protection when the device is locked. * The device is out of space. * The store could not be migrated to the current model version. Check the error message to determine what the actual problem was. */ fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.automaticallyMergesChangesFromParent = true } }
Обратите внимание, строка container = NSPersistentContainer(name: “CoreDataProject”) относится к имени файла модели данных, который определяет сущности и атрибуты
Добавление поддержки Core Data в существующий проект
Вы можете добавить поддержку Core Data при создании проекта. Но иногда Вы понимаете, что лучше организовать систему хранения данных через использование Core Data уже во время разработки Вашего приложения. В нашем приложении будет весьма простой пользовательский интерфейс который содержит два поля для ввода текста, две кнопки и две текстовые метки. Поля для ввода текста позволяют нам вводить данные, которые мы собираемся сохранить, две текстовые метки будут отображать сохраненный нами текст и две кнопки для удаления или добавления данных.
Давайте разберемся на практике, как добавить поддержку Core Data в уже существующий проект.
- Создадим новый iOs проект. Дадим ему имя Chapter6CoreDataUI. Обязательно!!! проследите, чтобы Вы не включали поддержку Core Data в наш создаваемый проект(см. Рисунок 6.4)
- Создадим новый файл Data Model и дадим ему имя DataModel
- Выберем созданный нами файл DataModel в панели Навигатора. XCode отобразит редактор данных, как показано на рис. 6.5
- Нажмем на кнопку Add Entity. XCode отобразит созданную сущность Entity в раздели Entities
- Теперь выберем нашу сущность и нажмем Enter для изменения ее имени
- Введем Item и снова нажмем Enter. Имена сущностей должны начинаться с заглавной буквы, например Item, Person, Customer и т.д.
После того, как мы создали одну или более сущностей, нам необходимо добавить атрибуты сущности. Атрибут включает в себя описательное имя(прописными буквами) и тип содержащихся данных, такие как string, integer или date.
Чтобы добавить атрибуты в созданную сущность, проделаем следующее:
- Откроем созданный нами файл Core Data – DataModel. XCode отобразит редактор модели(см. Рис 6.5)
- Выберем сущность, которую мы хотим изменить. XCode предоставляет нам 2 варианта добавления атрибутов:
- Нажмем на кнопку Add attribute и увидим следуюшее:
- Введем имя атрибута – name. Не забывайте что имена атрибутов должны начинаться с маленькой буквы
- Если нажать на всплывающее меня type – то мы увидим на рисунке 6.8 список доступных нам типов.
- Мы выберем тип string
- Добавим еще один атрибут
- Введем его имя – price
- В выпадающем меню выберем тип данных String. Получившиеся 2 атрибута нашей сущности Item вы можете увидеть на рисунке 6.9
Теперь, после того как мы добавили модель Core Data в наш проект и определили модель данных с нашей сущностью, пришло время добавить отдельный Swift файл для добавления, извлечения и удаления данных из Core Data следуя инструкции:
- Создайте сами новую сущность с именем Animal и двумя атрибутами breed и name, типа String, как указано на рисунке 6.10
- Создадим новый Swift файл:
- Дадим ему имя CoreDataManager
- После создания файла, откроем его
- Добавим следующий импорт для работы с фреймворком CoreData
import CoreData
- Добавим следующий код в класс CoreDataManager:
class CoreDataManager{ let persistenceContainer: NSPersistentContainer init(){ persistenceContainer = NSPersistentContainer(name: "DateModel") persistenceContainer.loadPersistentStores {(description, error) in if let error = error{ fatalError("Core Data failed to initialize \(error.localizedDescription)") } } } }
Этот простой класс загружает файл Core Data. В нашем случае файл это Core Data файл DataModel. Если же Вы дали файлу другое имя, необходимо заменить “DataModel” на другое название. Мы написали простой код для загрузки Core Data, но мы также должны добавить функции для добавления, удаления и доступа к данным.
- Теперь добавим следующую функцию для сохранения данных в модели Core Data
func savePet(name: String, breed: String){ let pet = Animal(context: persistenceContainer.viewContext) pet.name = name pet.breed = breed do{ try persistenceContainer.viewContext.save() print("Pet saved!") }catch{ print("Failed to save movie \(error)") } }
Эта функция принимает 2 входных параметра name и breed(это 2 атрибута определенные в сущности Animal) и сохраняет их уже в виде сущности Animal
- Добавим следующую функцию для извлечения данных
func getAllPets() -> [Animal]{ let fetchRequest: NSFetchRequest<Animal> = Animal.fetchRequest() do{ return try persistenceContainer.viewContext.fetch(fetchRequest) }catch{ return[] } }
Эта функция извлекается все данные из сущности Animal, определенные в файле модели Core Data
- Добавим следующую функцию для удаления данных:
func deletePet(animal: Animal){ persistenceContainer.viewContext.delete(animal) do{ try persistenceContainer.viewContext.save() }catch{ persistenceContainer.viewContext.rollback() print("Failed to save context \(error.localizedDescription)") } }
Эта функция принимает единицу сущности, которую мы хотим убрать из хранилища и затем удаляет ее из Core Data. Итоговый код файла CoreDataManager выглядит следующим образом:
// // CoreDataManager.swift // Chapter6CoreDataUI // // Created by Timur on 20.03.2023. // import Foundation import CoreData class CoreDataManager{ let persistenceContainer: NSPersistentContainer init(){ persistenceContainer = NSPersistentContainer(name: "DataModel") persistenceContainer.loadPersistentStores {(description, error) in if let error = error{ fatalError("Core Data failed to initialize \(error.localizedDescription)") } } } func savePet(name: String, breed: String){ let pet = Animal(context: persistenceContainer.viewContext) pet.name = name pet.breed = breed do{ try persistenceContainer.viewContext.save() print("Pet saved!") }catch{ print("Failed to save movie \(error)") } } func getAllPets() -> [Animal]{ let fetchRequest: NSFetchRequest<Animal> = Animal.fetchRequest() do{ return try persistenceContainer.viewContext.fetch(fetchRequest) }catch{ return[] } } func deletePet(animal: Animal){ persistenceContainer.viewContext.delete(animal) do{ try persistenceContainer.viewContext.save() }catch{ persistenceContainer.viewContext.rollback() print("Failed to save context \(error.localizedDescription)") } } }
Следующий этап у нас – это разработка пользовательского интерфейса, который позволяет пользователям ввести 2 строки(наши поля name и breed), сохранение данных, отображение данных списком и удаление выбранных нами данных. Для этого выполним следующее:
- Откроем исходный файл ContentView в панели Навигатора
- Добавим 3 State переменные и определим константу, определяющую созданный нами выше CoreDataManager. Первые 2 переменные это строки, отвечающие за значения имени и породы(name and breed), третья переменная отвечает за массив сущностей Animal, определенных в файле модели Core Data. Все это мы добавляем после struct ContentView: View {
let coreDM: CoreDataManager @State var petName = "" @State var petBreed = "" @State var petArray = [Animal]()
- Теперь добавим 2 текстовых поля и кнопки внутри контейнера VStack:
VStack { TextField("Enter pet name", text: $petName).textFieldStyle(RoundedBorderTextFieldStyle()) TextField("Enter pet breed", text: $petBreed).textFieldStyle(RoundedBorderTextFieldStyle()) Button("Save") { coreDM.savePet(name: petName, breed: petBreed) displayPets() petName = "" petBreed = "" } }.padding() .onAppear(perform:{ displayPets() })
Не обращайте внимание на ошибку с отсутствием функции displayPets() – к ней мы вернемся уже в следующем пункте. Мы видим 2 текстовых поля с именем и породой. И кнопка, которая сохраняет эти данные в Core Data. Также мы добавили в onAppear() отображение питомцев. onAppear – срабатывает когда UI появляется на экране девайса.
- Теперь добавим функцию displayPets() сразу после последней закрывающей скобки блока кода var body: some View
func displayPets(){ petArray = coreDM.getAllPets() }
Эта функция извлекает все данные из Core Data и передает их переменной petArray
- Теперь добавим следующий код сразу после блока кола кнопки Save
List{ ForEach(petArray, id: \.self){pet in VStack{ Text(pet.name ?? "") Text(pet.breed ?? "") } }.onDelete(perform: { indexSet in indexSet.forEach { index in let pet = petArray[index] coreDM.deletePet(animal: pet) displayPets() } }) } Spacer()
Этот код создает List, в котором в теле цикла ForEach извлекаются данные из Core Data и затем отображаются в отдельном контейнера VStack. И обратите внимание на модификатор .onDelete – он позволяет свайпнуть влево элемент отображаемой сущности и удалить из Core Data выбранный элемент данных и потом снова отобразить актуальный список сущностей. Элемент Spacer() добавлен для того, чтобы наш список всегда “прижимался” к кнопке save
- Последний штрих – необходимо дать превью доступ к CoreDataManager, для этого чуть подкорректируем блок struct ContentView_Previews: PreviewProvider {
struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(coreDM: CoreDataManager()) } }
Итоговый код ContentView:
// // ContentView.swift // Chapter6CoreDataUI // // Created by Timur on 20.03.2023. // import SwiftUI struct ContentView: View { let coreDM: CoreDataManager @State var petName = "" @State var petBreed = "" @State var petArray = [Animal]() var body: some View { VStack { TextField("Enter pet name", text: $petName).textFieldStyle(RoundedBorderTextFieldStyle()) TextField("Enter pet breed", text: $petBreed).textFieldStyle(RoundedBorderTextFieldStyle()) Button("Save") { coreDM.savePet(name: petName, breed: petBreed) displayPets() petName = "" petBreed = "" } List{ ForEach(petArray, id: \.self){pet in VStack{ Text(pet.name ?? "") Text(pet.breed ?? "") } }.onDelete(perform: { indexSet in indexSet.forEach { index in let pet = petArray[index] coreDM.deletePet(animal: pet) displayPets() } }) } Spacer() }.padding() .onAppear(perform:{ displayPets() }) } func displayPets(){ petArray = coreDM.getAllPets() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(coreDM: CoreDataManager()) } }
Этот код предельно простой. Мы видим 2 текстовых поля, где вводим имя и породу питомца. А также кнопку Save – которая сохраняет введенные нами данные о питомце в наш Core Data. После сохранения данных, они тут же отображаются в списке, расположенном под кнопкой Save. Также мы добавили функционал по удалению добавленных нами домашних питомцев: достаточно свайпнуть влево питомца и появится кнопка Delete, которая при нажатии удалит выбранную сущность из Core Data и обновит пользовательский интерфейс.Посмотрим как работает наш проект:
- Нажмем на иконку Preview
- В верхнем текстовом поле введем имя Fedel
- Во втором текстовом поле введем породу Teriere
- Нажмем на кнопку Save – обратим внимание что наша порода появилась теперь под кнопкой Save
- Добавим еще одно домашнее животное, чтобы мы получили скриншот, похожий на Рис. 6.12
- Попробуем свайпнуть влево второе домашнее животное – Nick и у нас появится красная кнопка для удаления питомца из Core Data, как на рисунке 6.13
- Нажмите на кнопку Delete и обратите внимание что интерфейс тут же обновится
- Попробуйте резко свайпнуть влево оставшегося питомца – наше приложение удалит без предупреждения нашего терьера по имени Fedel из Core Data
Заключение
В любом более или менее серьезном приложении Вы столкнетесь с необходимостью сохранения данных. Для хранения простых настроек приложения или небольших данных, лучше использовать UserDefaults – этот подход отличается простотой и легкостью в исполнении.
Для хранения больших данных, лучше выбрать решение по хранению их в файлах. Файлы могут хранить большие объемы текста или медийной информации(музыка, фильмы – но к этой теме мы вернемся только через несколько глав). Только необходимо помнить, что поиск нужной текстовой информации в большом файле может быть весьма продолжительным по времени.
Если же Вы хотите хранить большие объемы структурированных данных, например список клиентов, домашних питомцев, календарь дел и т.д. – Вам лучше использовать возможности фреймворка Core Data. Когда Вы храните ваши данные в любом из описанных нами способов – UserDefaults, файлы или Core Data, Вы можете легко извлекать Ваши данные и отображать их в Вашем UI автоматически без необходимости ручного управления кодом для отображения загруженной информации.