Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tim Oliver: Realm: How I learned to love database again - iOS Conf SG 2016

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
October 20, 2016

Tim Oliver: Realm: How I learned to love database again - iOS Conf SG 2016

Abstract/excerpt: iOS provides two system level database frameworks: SQLite and Core Data. While both of these frameworks are great at what they do, they both have a high learning curve, and sometimes require a fair amount of boilerplate code in order to get started. Realm is a new database framework wholly designed from scratch to try and make persisting data easier than that. In this talk, Tim will introduce Realm and contrast it to his experiences with SQLite and Core Data in his own apps. He will also provide a demonstration on how to get started with it, best implementation practices, and how to migrate an existing app to it.

Bio: Tim has been a huge enthusiast of developing apps on iOS ever since the launch of the iPhone 3G. Originally a web designer/developer out of university, he moved to full-time mobile software development in 2013 and has never looked back. Presently, he works for Realm, remotely from Western Australia.

https://twitter.com/timoliverau

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

October 20, 2016
Tweet

Transcript

  1. Realm : How I Learned to Love Databases Again iOS

    Conf SG Tim Oliver to@realm.io
  2. Overview 1) My history with databases, up to Realm to@realm.io

    2) Power users’ guide to Realm Mobile Database 3) Introducing the Realm Mobile Platform
  3. Who is this guy? • From Perth, Australia, currently living

    in San Francisco, USA. to@realm.io • Full time iOS Engineer since 2013. • Studied joint CompSci and Multimedia degree at university. • Loves building apps and framework libraries. • Also loves karaoke and video games.
  4. My Apps (And the databases they use) to@realm.io

  5. iPokédex - A Pokédex for iOS to@realm.io

  6. iPokédex - SQLite to@realm.io • Custom built SQLite database file

    to store data. • Results of queries were manually mapped to NSObject subclasses. • Stored as a read-only resource in the app bundle. • “Light” and “Heavy” versions of each subclass. • Graphing Pokémon data is not trivial!
  7. to@realm.io

  8. SQLite Takeaways to@realm.io • As a C library, will always

    be necessary to have some sort of ‘bridge’ to Objective-C / Swift. • A non-trivial amount of effort to map query results to in- app objects. • Requires knowledge of database design and SQL syntax. • Memory usage is a huge concern, prompting the need for ‘paging’ query results. • Schema migration/thread handling is a very manual process.
  9. iComics - An iOS Comic Reader to@realm.io

  10. iComics - Core Data to@realm.io • Data persistence needed for

    caching comic book metadata / user settings / page order. • Core Data touted as ‘nicer version of SQLite` with object-mapping, schema migration, native queries being provided for free. • Native framework supported and promoted by Apple, so it must be good. Right?
  11. to@realm.io Not so much…

  12. Core Data Takeaways to@realm.io • Steep learning curve. VERY steep

    learning curve. • An Apple ‘black box’™ where it’s impossible to see its internals; hard to debug when errors arise. • A LOT of boilerplate code required to even begin. • Memory usage is still a concern, with fetch results needing to be paged. • Schema migration can corrupt a database without warning.
  13. Core Data is untenable. What do I do now? to@realm.io

    Back to SQLite…?
  14. to@realm.io http://realm.io

  15. to@realm.io http://realm.io, circa November 2014

  16. “What the heck. There’s no possible way this thing could

    be as bad as Core Data.” to@realm.io
  17. Converting from Core Data to Realm to@realm.io • Rename NSManagedObject

    subclasses to RLMObject. • Re-implement all Core Data fetch/threaded/save operations to Realm equivalents. • Redefine NSNumber properties to Int, Double, etc • Optionally, implement a small helper class to copy data from a Core Data to Realm. • The entire process took less than 1 evening of work.
  18. to@realm.io It just worked!

  19. to@realm.io This is amazing!

  20. to@realm.io Joined the company in March 2015

  21. How to be Awesome with the Realm Mobile Database to@realm.io

  22. What is Realm Mobile Database? to@realm.io • A completely custom

    C++ database engine. • Proper Objective-C / Swift frameworks for a native feel. • Zero-copy, memory mapped data access to objects. • Free and open source on GitHub. • Uses Objective-C runtime reflection to simplify process. • Purely object driven. No abstraction between disk format.
  23. to@realm.io Hello World! import RealmSwift // Define our model object

    class Dog: Object { dynamic var name = “” dynamic var age = 0 } // Create a new instance to save let myDog = Dog() myDog.name = “Rex” myDog.age = 4 // Save it to Realm let realm = try! Realm() try! realm.write { realm.add(myDog) }
  24. to@realm.io App Documents Directory Realm Browser App

  25. to@realm.io Realm() Object() • Represents .realm file on disk. •

    Can be configured with Configuration. • Strictly thread-confined. • Provides context for read/writes. • Represents a single entry in the database. • Also strictly thread- confined. • Supports all major data types natively. • Cached on subsequent calls.
  26. to@realm.io Changes occur in write transactions for ACID compliance

  27. to@realm.io Getting the data back out again import RealmSwift //

    Create the reference to the Realm on disk let realm = try! Realm() // Fetch all dog objects in Realm let allDogs = realm.objects(Dog.self) // Get just the first dog let rex = allDogs[0] // Access the dog’s properties print(rex.name)
  28. to@realm.io Perform chains of query methods to refine results

  29. to@realm.io Results() / List() • Behaves exactly like an Array.

    • Strictly thread-confined. • Object members are lazy-loaded upon access.
  30. to@realm.io Zero-Copy Architecture • Object properties point straight to their

    values on disk. • Memory-mapping is used to lazily page data in on demand. • No need for worrying about memory usage, or pagination!
  31. to@realm.io But that’s not all…

  32. to@realm.io Realm objects live-update in response to changes

  33. to@realm.io Register for update notifications // Observe Realm Notifications let

    token = realm.addNotificationBlock { notification, realm in viewController.updateUI() } // Observe Results Notifications notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in switch changes { case .initial: // Results are now populated and can be accessed without blocking the UI break case .update(_, let deletions, let insertions, let modifications): // Query results have changed break case .error(let error): fatalError(“\(error)") // An error occurred while opening the Realm file break } } • Notified whenever anything changes inside Realm() • Notified whenever contents of Results() changes dog.addObserver(self, forKeyPath: “age”, options: [.new], context: nil) • Key-Value Observing of specific properties
  34. Best practices with Realm Mobile Database to@realm.io

  35. 1. Take advantage of live objects to@realm.io • Don’t worry

    about implementing ‘re-fetch’ code. • Don’t try and optimise for memory consumption (ie paging). • Use Realm Results object; don’t copy to NSArray. • Use fine-grained notifications to specifically control when it’s necessary to trigger a UI update. • This is not SQLite. :)
  36. 2. Write Transactions to@realm.io • Minimise/batch write transactions as much

    as possible. for item in results { try! realm.write { item.value = newValue } } try! realm.write { for item in results { item.value = newValue } }
  37. 2. Write Transactions to@realm.io • Offload write transactions to background

    threads as much as possible. • For ACID compliance, only one write transaction may be open at once. Background writes can block main threaded ones. DispatchQueue.global(attributes: [.qosDefault]).async { // Background thread let realm = try! Realm() // thread-specific realm instance try! realm.write { { // do Realm updates } }
  38. 3. Working Across Threads to@realm.io • Any objects backed by

    a realm property aren’t thread-safe. let dog = try! Realm().objects(Dog.self).first DispatchQueue.global(attributes: [.qosDefault]).async { print(dog.name) // WILL TRIGGER AN EXCEPTION } • Each thread has its own confined version of a Realm instance.
  39. 3. Working Across Threads to@realm.io • Re-fetch the object on

    the new thread using a primary key. class Dog: Object { dynamic var id = UUID().uuidString dynamic var name = “” override static func primaryKey() -> String { return “id” } } let dog = try! Realm().objects(Dog.self).first let dogID = dog.id DispatchQueue.global(attributes: [.qosDefault]).async { let backgroundDog = try! Realm().object(ofType: Dog.self, primaryKey: dogID) print(localDog.name) }
  40. Introducing the Realm Mobile Platform to@realm.io

  41. to@realm.io

  42. to@realm.io

  43. to@realm.io (RMP Demo Reel)

  44. to@realm.io

  45. to@realm.io Hello World! (RMP Edition) import RealmSwift let user =

    … // Authenticate for a valid User object let URL = URL(string: “realm://api.myserver.com:9080/~/user") let configuration = Realm.Configuration() configuration.syncConfiguration = (user: user, realmURL: URL) // Create a new instance to save let myDog = Dog() myDog.name = “Rex” myDog.age = 4 // Save it to Realm let realm = try! Realm(configuration: configuration) try! realm.write { realm.add(myDog) }
  46. to@realm.io How do we detect remote changes? // Observe Realm

    Notifications let token = realm.addNotificationBlock { notification, realm in viewController.updateUI() } // Observe Results Notifications notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in switch changes { case .initial: // Results are now populated and can be accessed without blocking the UI break case .update(_, let deletions, let insertions, let modifications): // Query results have changed break case .error(let error): fatalError(“\(error)") // An error occurred while opening the Realm file break } } • Notified whenever anything changes inside Realm() • Notified whenever contents of Results() changes dog.addObserver(self, forKeyPath: “age”, options: [.new], context: nil) • Key-Value Observing of specific properties
  47. to@realm.io

  48. to@realm.io http://realm.io/pricing

  49. Thanks for watching! to@realm.io

  50. to@realm.io Questions? Tim Oliver to@realm.io www.realm.io @TimOliverAU