Статьи

Управление данными в приложениях iOS с помощью SQLite

Эта статья была рецензирована Александром Коко . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Почти все приложения должны будут хранить данные той или иной формы. Возможно, вам нужно сохранить пользовательские настройки, прогресс в игре или данные в автономном режиме, чтобы ваше приложение могло работать без сетевого подключения. Разработчики имеют множество вариантов управления данными в приложениях для iOS, от Core Data до облачного хранилища, но одним из элегантных и надежных локальных хранилищ является SQLite.

В этом уроке я покажу вам, как добавить поддержку SQLite в ваше приложение. Вы можете найти окончательный исходный код на GitHub .

Начиная

Библиотека SQLite написана на C, и все запросы выполняются как вызовы функций C. Это затрудняет использование, так как вы должны знать указатели и типы данных и т. Д. Чтобы помочь, вы можете использовать оболочки Objective-C или Swift в качестве уровня адаптера.

Популярным выбором является FMDB , оболочка Objective-C для SQLite. Его легко использовать, но лично я предпочитаю не использовать жестко запрограммированные команды SQL (Structured Query Language). Для этого урока я буду использовать SQLite.swift для создания основного списка контактов.

Сначала создайте новый проект с одним представлением в Xcode (для SQLite.swift требуется Swift 2 и Xcode 7 или выше). Я создал ViewControllerMain.storyboard, который выглядит следующим образом. Создайте свой собственный подобный макет или загрузите файлы раскадровки здесь .

Предварительный просмотр приложения

Внизу находится TableView

Установка

Вы можете установить SQLite.swift с Carthage , CocoaPods или вручную .

Модель

Создайте новый файл / класс Swift с именем Contact.swift , он содержит три свойства и инициализатора, чтобы упростить его.

 import Foundation

class Contact {
    let id: Int64?
    var name: String
    var phone: String
    var address: String

    init(id: Int64) {
        self.id = id
        name = ""
        phone = ""
        address = ""
    }

    init(id: Int64, name: String, phone: String, address: String) {
        self.id = id
        self.name = name
        self.phone = phone
        self.address = address
    }
}

id

Подключение пользовательского интерфейса

В ViewController.swift класс должен реализовывать протоколы UITableViewDelegateUITableViewSource

 class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
   ...
 }

Соедините следующие IOutlet

 @IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var phoneTextField: UITextField!
@IBOutlet weak var addressTextField: UITextField!

@IBOutlet weak var contactsTableView: UITableView!

Добавить торговые точки

Теперь вам потребуется список контактов и индекс для контакта, выбранного из списка.

 private var contacts = [Contact]()
private var selectedContact: Int?

Свяжите источник данных и делегат UITableViewUIViewController

Добавить источник данных

Или добавив следующие строки в метод viewDidLoad()ViewController.swift .

 contactsTableView.dataSource = self
contactsTableView.delegate = self

Чтобы вставить, обновить и удалить элементы из UITableView

Первый заполнит UITextField Yt сохранит строку, представляющую этот контакт, в таблице.

 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    nameTextField.text = contacts[indexPath.row].name
    phoneTextField.text = contacts[indexPath.row].phone
    addressTextField.text = contacts[indexPath.row].address

    selectedContact = indexPath.row
}

Следующая функция сообщает UITableViewDataSource На данный момент это будет ноль, так как массив пуст.

 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return contacts.count
}

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

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("ContactCell")!
    var label: UILabel
    label = cell.viewWithTag(1) as! UILabel // Name label
    label.text = contacts[indexPath.row].name

    label = cell.viewWithTag(2) as! UILabel // Phone label
    label.text = contacts[indexPath.row].phone

    return cell
}

Приложение теперь может работать, но пока нет возможности добавлять или редактировать контакты. Для этого свяжите следующие IBAction

 @IBAction func addButtonClicked() {
    let name = nameTextField.text ?? ""
    let phone = phoneTextField.text ?? ""
    let address = addressTextField.text ?? ""

    let contact = Contact(id: 0, name: name, phone: phone, address: address)
    contacts.append(contact)
    contactsTableView.insertRowsAtIndexPaths([NSIndexPath(forRow: contacts.count-1, inSection: 0)], withRowAnimation: .Fade)
}

Здесь вы берете значения UITextFieldcontacts id Функция insertRowsAtIndexPaths()

 @IBAction func updateButtonClicked() {
    if selectedContact != nil {
        let id = contacts[selectedContact].id!
        let contact = Contact(
            id: id,
            name: nameTextField.text ?? "",
            phone: phoneTextField.text ?? "",
            address: addressTextField.text ?? "")

            contacts.removeAtIndex(selectedContact!)
                contacts.insert(contact, atIndex: selectedContact!)

        contactsTableView.reloadData()
    } else {
    print("No item selected")
    }
}

В этой функции вы создаете новый Contact В настоящее время функция не проверяет, изменились ли данные.

 @IBAction func deleteButtonClicked() {
    if selectedContact != nil {
        contacts.removeAtIndex(selectedContact)

        contactsTableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: selectedContact, inSection: 0)], withRowAnimation: .Fade)
    } else {
    print("No item selected")
    }
}

Последняя функция удаляет выбранный контакт и обновляет таблицу.

В этот момент приложение работает, но потеряет все изменения при перезапуске.

Создание базы данных

Теперь время для управления базой данных. Создайте новый файл / класс swift с именем StephencelisDB.swift и импортируйте библиотеку SQLite.

 import SQLite

class StephencelisDB {
}

Сначала инициализируйте экземпляр класса, используя шаблон «Singleton». Затем объявите объект типа Connection

 static let instance = StephencelisDB()
private let db: Connection?

Другие объявления — это таблица контактов и ее столбец с определенным типом.

 private let contacts = Table("contacts")
private let id = Expression<Int64>("id")
private let name = Expression<String?>("name")
private let phone = Expression<String>("phone")
private let address = Expression<String>("address")

Конструктор пытается открыть соединение с базой данных, которая имеет указанное имя и путь к данным приложения, а затем создает таблицы.

 private init() {
    let path = NSSearchPathForDirectoriesInDomains(
        .DocumentDirectory, .UserDomainMask, true
        ).first!

    do {
        db = try Connection("\(path)/Stephencelis.sqlite3")
    } catch {
        db = nil
        print ("Unable to open database")
    }

    createTable()
}

func createTable() {
    do {
        try db!.run(contacts.create(ifNotExists: true) { table in
        table.column(id, primaryKey: true)
        table.column(name)
        table.column(phone, unique: true)
        table.column(address)
        })
    } catch {
        print("Unable to create table")
    }
}

Обратите внимание, что нет кода SQL для создания таблицы и столбцов. Это сила используемой оболочки. С несколькими строками кода у вас есть готовая база данных.

CRUD Операции

Для тех, кто не знаком с этим термином, «CRUD» является аббревиатурой от Create-Read-Update-Delete. Затем добавьте четыре метода в класс базы данных, которые выполняют эти операции.

 func addContact(cname: String, cphone: String, caddress: String) -> Int64? {
    do {
        let insert = contacts.insert(name <- cname, phone <- cphone, address <- caddress)
        let id = try db!.run(insert)

        return id
    } catch {
        print("Insert failed")
        return -1
    }
}

Оператор <- Метод run id

Добавьте print(insert.asSQL())

 INSERT INTO "contacts" ("name", "phone", "address") VALUES ('Deivi Taka', '+355 6X XXX XXXX', 'Tirana, Albania')

Если вы хотите провести дальнейшую отладку, вы можете вместо этого использовать метод. Метод prepare Вы перебираете эти строки и создаете массив объектов Contact Если эта операция завершается неудачно, возвращается пустой список.

 func getContacts() -> [Contact] {
    var contacts = [Contact]()

    do {
        for contact in try db!.prepare(self.contacts) {
            contacts.append(Contact(
            id: contact[id],
            name: contact[name]!,
            phone: contact[phone],
            address: contact[address]))
        }
    } catch {
        print("Select failed")
    }

    return contacts
}

Для удаления элементов найдите элемент с заданным id

 func deleteContact(cid: Int64) -> Bool {
    do {
        let contact = contacts.filter(id == cid)
        try db!.run(contact.delete())
        return true
    } catch {
        print("Delete failed")
    }
    return false
}

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

Обновление имеет похожую логику.

 func updateContact(cid:Int64, newContact: Contact) -> Bool {
    let contact = contacts.filter(id == cid)
    do {
        let update = contact.update([
            name <- newContact.name,
            phone <- newContact.phone,
            address <- newContact.address
            ])
        if try db!.run(update) > 0 {
            return true
        }
    } catch {
        print("Update failed: \(error)")
    }

    return false
}

Финальные изменения

После настройки класса управления базой данных в Viewcontroller.swift необходимо внести некоторые изменения.

Во-первых, при загрузке просмотра получить ранее сохраненные контакты.

 contacts = StephencelisDB.instance.getContacts()

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

Внутри addButtonClicked Затем обновите представление таблицы, только если метод вернул правильный id

 if let id = StephencelisDB.instance.addContact(name, cphone: phone, caddress: address) {
    // Add contact in the tableview
    ...
}

Аналогичным образом вызовите эти методы внутри updateButtonClickeddeleteButtonClicked

 ...
StephencelisDB.instance.updateContact(id, newContact: contact)
...
StephencelisDB.instance.deleteContact(contacts[selectedContact].id!)
...

Запустите приложение и попробуйте выполнить некоторые действия. Ниже приведены два скриншота того, как это должно выглядеть. Чтобы обновить или удалить контакт, его нужно сначала выбрать.

1

2

Любые вопросы?

SQLite является хорошим выбором для работы с локальными данными и используется многими приложениями и играми. Оболочки, такие как SQLite.swift, упрощают реализацию, избегая использования жестко закодированных SQL-запросов. Если вам нужно хранить данные в вашем приложении и вам не нужно обрабатывать более сложные параметры, тогда SQLite стоит рассмотреть.

Пусть кодекс будет с вами!