32
Data Storage Mobile Application Development in iOS School of EECS Washington State University Instructor: Larry Holder Mobile Application Development in iOS 1

Data Storage - eecs.wsu.edueecs.wsu.edu/~holder/courses/MAD/slides/11-Storage.pdf · –Couchbase Lite ( •Cross pla=orm document store (NoSQL) –Firebase (firebase.google.com)

Embed Size (px)

Citation preview

Data StorageMobile Application Development in iOS

School of EECS

Washington State University

Instructor: Larry Holder

Mobile Application Development in iOS 1

Data Storage

• Already seen: UserDefaults, iCloud

• File I/O

• Database support: CoreData, SQLite

• Data backup to iCloud

• Data protection

Mobile Application Development in iOS 2

File I/O

• FileManager

– .default: shared file manager for app

– Numerous methods for manipulating files

• FileManagerDelegate

– Constraint checking and error handling

Mobile Application Development in iOS 3

File I/O

• Get URL to directory

• Append file name to URL

• Convert data to String

• Use String.write(to: URL, encoding: .utf8) to write

• Use String(contentsOf: URL , encoding: .utf8) to read

• Both throw errors

Mobile Application Development in iOS 4

File I/O: Write

Mobile Application Development in iOS 5

let fileName = "myDataFile"

func writeData(_ str: String) -> Bool {if let directoryURL = FileManager.default.urls(for: .documentDirectory,

in: .userDomainMask).first {let fileURL = directoryURL.appendingPathComponent(fileName)do {

try str.write(to: fileURL, atomically: true, encoding: .utf8)return true

} catch {print("\(error)")

}}return false

}

File I/O: Read

Mobile Application Development in iOS 6

func readData() -> String? {if let directoryURL = FileManager.default.urls(for: .documentDirectory,

in: .userDomainMask).first {let fileURL = directoryURL.appendingPathComponent(fileName)do {

let str = try String(contentsOf: fileURL, encoding: .utf8)return str

} catch {print("\(error)")

}} else {

print("Error accessing document directory.")}return nil

}

File I/O: Codable

Problem: Convert everything to a • String

Solu:on: • Codable (new in Swi= 4)

Can be encoded/decoded to common formats, e.g., JSON–

JSON data easily converted to/from – String

All basic types and containers are • Codable

Classes consis:ng of • Codable proper:es are Codable

Mobile Application Development in iOS 7

File I/O: Codable

Mobile Application Development in iOS 8

class Player: Codable {var name: Stringvar health: Int

init(_ name: String, _ health: Int) {self.name = nameself.health = health

}}

File I/O: Codable

Mobile Applica3on Development in iOS 9

func writePlayer(_ player: Player) -> Bool {let jsonEncoder = JSONEncoder()do {

let jsonData = try jsonEncoder.encode(player)if let jsonStr = String(data: jsonData, encoding: .utf8) {

if writeData(jsonStr) {return true

}}

} catch {print("\(error)")

}return false

}

File I/O: Codable

Mobile Application Development in iOS 10

func readPlayer() -> Player? {if let str = readData() {

if let jsonData = str.data(using: .utf8) {let jsonDecoder = JSONDecoder()do {

let player = try jsonDecoder.decode(Player.self,from: jsonData)

return player} catch {

print("\(error)")}

}}return nil

}

Database SupportCore Data•

iOS specific object store–

SQLite (• www.sqlite.org)

Cross– -pla=orm table store (already available in iOS)

Third• -party opEons

Realm (– realm.io) Cross• -pla=orm object store

Couchbase Lite (– www.couchbase.com/products/lite) Cross pla=orm document store (NoSQL)•

Firebase (– firebase.google.com) Cross• -pla=orm document store (NoSQL)

Mobile Application Development in iOS 11

Core Data

• Check “Use Core Data”

for New Project

• Includes empty data

model

• Includes boilerplate

code to create database

Mobile Application Development in iOS 12

Core Data: Model

Create Managed Object Model (Schema)•

Schema consists of • en;;es, their a=ributes,

and rela;onships

Mobile Application Development in iOS 13

Core Data: Stack

• Handles interactions with data store

– Persistent container (NSPersistentContainer)

• Obtained from UIApplication.shared.delegate

– Managed object context (NSManagedObjectContext)

• Obtained from NSPersistentContainer.viewContext

Mobile Application Development in iOS 14

Core Data: Insert

Methods•

NSEn3tyDescrip3on– .insertNewObject(forEn3tyName:

String, into: NSManagedObjectContext) ->

NSManagedObject

NSManagedObject– .setValue(value: Any?, forKey: String)

NSManagedObjectContext– .save()

Mobile Application Development in iOS 15

Core Data: Insert

Mobile Application Development in iOS 16

import CoreData

class ViewController: UIViewController {

var managedObjectContext: NSManagedObjectContext!var appDelegate: AppDelegate!

override func viewDidLoad() {super.viewDidLoad()self.appDelegate = UIApplication.shared.delegate as! AppDelegateself.managedObjectContext =

appDelegate.persistentContainer.viewContext}

func addPlayer() {let player = NSEntityDescription.insertNewObject(forEntityName:

"Player", into: self.managedObjectContext)player.setValue("Mario", forKey: "name")player.setValue(100, forKey: "health")self.appDelegate.saveContext() // In AppDelegate.swift

}}

Core Data: Fetch

Methods•

NSFetchRequest– <NSManagedObject>(en=tyName:

String) -> NSFetchRequest<NSManagedObject>

NSFetchRequest– <NSManagedObject>.predicate =

NSPredicate(format: String, args...)

NSManagedObjectContext– .fetch(request:

NSFetchRequest<NSManagedObject>) throws

Mobile Application Development in iOS 17

Core Data: Fetch

Mobile Application Development in iOS 18

func getPlayers() {let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Player")var players: [NSManagedObject]!do {

players = try self.managedObjectContext.fetch(fetchRequest)} catch {

print("getPlayers error: \(error)")}print("Found \(players.count) players")for player in players {

let name = player.value(forKey: "name") as! Stringlet health = player.value(forKey: "health") as! Intprint(" Found player \(name) with health \(health)")

}}

Core Data: Delete

Methods•

NSManagedObjectContext– .delete(object:

NSManagedObject)

NSManagedObjectContext– .save()

Mobile Application Development in iOS 19

Core Data: Delete

Mobile Application Development in iOS 20

func removePlayers() {let playerName = "Mario"let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Player")fetchRequest.predicate = NSPredicate(format: "name == %@", playerName)var players: [NSManagedObject]!do {

players = try self.managedObjectContext.fetch(fetchRequest)} catch {

print("removePlayers error: \(error)")}for player in players {

self.managedObjectContext.delete(player)}self.appDelegate.saveContext() // In AppDelegate.swift

}

SQLite

C API to SQL database•

Swi2 wrappers provided by SQLite.swi2•

github.com/stephencelis/SQLite.swi2–

Install via CocoaPods (see Miscellaneous Topics)–

Mobile Application Development in iOS 21

SQLite.swift: Create Database

• Connection(pathToDB: String) throws ->

Connection

• Table(name: String)

• Expression<Type>(name: String)

• Connection.run(Table.create() { t in

t.column(Expression<Type>)}) throws

Mobile Application Development in iOS 22

SQLite.swift: Create Database

Mobile Applica8on Development in iOS 23

import SQLiteclass ViewController: UIViewController {

var db: Connection!var playersTable: Table!var nameExp: Expression<String>!var healthExp: Expression<Int>!

func createDatabase() {// Connect to writable database in app’s Documents directory.let directoryURL = FileManager.default.urls(for: .documentDirectory,

in: .userDomainMask).first!let path = directoryURL.pathdb = try? Connection("\(path)/db.sqlite3")// Create Players tableplayersTable = Table("Players")nameExp = Expression<String>("name")healthExp = Expression<Int>("health")do {

try db?.run(playersTable.create(ifNotExists: true) { t int.column(nameExp)t.column(healthExp)})

} catch {print("error creating table")

}}

}

SQLite.swift: Insert

• Connection.run(Table.insert(Expression<Type>

<- Any?, …) throws

Mobile Application Development in iOS 24

SQLite.swift: Insert

Mobile Applica8on Development in iOS 25

func addPlayers() {do {

try db.run(playersTable.insert(nameExp <- "Mario", healthExp <- 100))try db.run(playersTable.insert(nameExp <- "Yoshi", healthExp <- 200))

} catch {print("insert error")

}}

SQLite.swift: Fetch

• Connection.scalar(Table.count) throws -> Int

• Connection.prepare(Table) throws

Mobile Application Development in iOS 26

SQLite.swift: Fetch

Mobile Applica7on Development in iOS 27

func getPlayers() {if let count = try? db.scalar(playersTable.count) {

print("Found \(count) players")if let players = try? db.prepare(playersTable) {

for player in players {print(" Found player \(player[nameExp]) with health \(player[healthExp])")

}}

}}

SQLite.swift: Delete

• Table.Filter(Expression<Type> == Any?)

• Connection.run(Filter.delete()) throws

Mobile Application Development in iOS 28

SQLite.swift: Delete

Mobile Applica6on Development in iOS 29

func removePlayers() {let marioFilter = playersTable.filter(nameExp == "Mario")do {

try db.run(marioFilter.delete())} catch {

print("delete error")}

}

Data Backup to iCloud

• Available with iCloud capability

Mobile Application Development in iOS 30

Data Protection

Files encrypted and inaccessible with device •

locked

Can selec8vely encrypt files•

Mobile Application Development in iOS 31

Resources• File I/O

– developer.apple.com/documentation/foundation/filemanager

• Core Data– developer.apple.com/documentation/coredata

– www.raywenderlich.com/173972/getting-started-with-core-data-tutorial-2

• SQLite– github.com/stephencelis/SQLite.swift

• iCloud backup– developer.apple.com/icloud/documentation/data-storage

• Data Protection– developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy/encrypti

ng_your_app_s_files

Mobile Application Development in iOS 32