With Facebook shutting down Parse, everybody knows to never again depend on a third party for their backend solution, right? Sure, and after you spend six months trying to write your own syncing service, how's that working? In 2016, Google has added a ton of features to Firebase, their popular backend-as-a-service solution. Firebase's primary offering is a realtime database in the cloud that syncs changes to and from multiple concurrent users, and their Swift-friendly iOS SDK makes it ideal for mobile use. In this session, you'll learn how to set up a Firebase backend and build an iOS app around it.
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Ā
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose, Nov. 2016)
1. Firebase: Totally Not
Parse All Over Again
(Unless It Is)
Chris Adamson (@invalidname)
CocoaConf San Jose ā¢ November, 2016
Slides available at slideshare.net/invalidname
Code available at github.com/invalidstream
2.
3. Firebase
ā¢ Founded in 2011
ā¢ Offshoot of Envolve, a chat service that game
developers started using to sync state across
devices
ā¢ Main product is a realtime database
ā¢ Acquired by Google in October 2014
5. Firebase @ Google
ā¢ 470,000 developers using Firebase
ā¢ Arguably the star of Google I/O 2016
ā¢ Analytics (from developers of Google
Analytics)
ā¢ Notiļ¬cations (based on Google Cloud
Messaging)
6. Realtime database
ā¢ Cloud-based NoSQL database
ā¢ Syncs instantly across devices, handles going
ofļ¬ine
ā¢ Client SDKs for iOS and Android, REST for web
ā¢ Free tier supports 100 simultaneous users, 1GB
storage
7. Getting Started
ā¢ Create app on ļ¬rebase.google.com using your
apps bundle identiļ¬er
ā¢ Download and add GoogleService-info.plist to
your project
8.
9.
10. Getting Started
ā¢ Add the Firebase Cocoapod
ā¢ As with all things pod, remember to
use .xcworkspace instead of .xcproj from now
on
ā¢ Yes, it is possible to add the frameworks
without Cocoapods
11. Getting Started
ā¢ Initialize Firebase in application(_:
didFinishLaunchingWithOptions:)
import Firebase
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
return true
}
13. Wait, what theā¦
ā¢ Firebase I/O works with a local cache, which in
turn syncs with the backend
ā¢ With good connectivity, syncing to backend
and other devices is instantaneous
ā¢ When ofļ¬ine, you can keep working with your
local db, which syncs when youāre back online
15. Tree-structured data
ā¢ Your database is basically one big JSON tree
ā¢ You access branches and nodes by path
ā¢ e.g., /sessions/adamson-ļ¬rebase/title
ā¢ You can query at a given location, but this is not a
relational database.
ā¢ If you want to do a table join, you structured
your data incorrectly. Prefer ļ¬atness.
16. Getting a Firebase
Reference
ā¢ FIRDatabase.reference() returns root of tree as a
FIRDatabaseReference
ā¢ Child returns the named child as a
FIRDatabaseReference
firebaseSessions = FIRDatabase.database().reference().
child("sessions")
18. Observing Firebase
ā¢ All interactions with Firebase are asynchronous
ā¢ You donāt read the value of a location, you
observe it for changes
ā¢ Contents of the child are passed to you on every
change
19. Observe!
ā¢ eventType: the type of event you want to
observe (value, child CRUD, etc)
ā¢ block: a closure to execute on these events
ā¢ returns a handle (dispose it in deinit, or earlier)
firebaseHandle = firebaseSessions?.observe(
FIRDataEventType.value, with: { [weak self] (snapshot) in
self?.parseSessionsFrom(snapshot)
self?.tableView.reloadData()
})
20. Parse!
ā¢ Observer block receives a FIRDataSnapshot for
every change
ā¢ Immutable, fetch contents with .value()
ā¢ Value types: NSDictionary, NSArray (rare),
NSString, NSNumber [or Swift equivalents]
21. Parse sessions list
private func parseSessionsFrom(_ snapshot: FIRDataSnapshot) {
guard let fbSessions = snapshot.value as? [String : Any]
else {
return
}
sessions.removeAll()
for (id, value) in fbSessions {
if let sessionDict = value as? [String : Any],
let session = Session(id: id, dict: sessionDict) {
sessions.append(session)
}
}
}
22. Parse a session
init? (id: String, dict : [String : Any]) {
guard let title = dict["title"] as? String,
let speakerName = dict["speakerName"] as? String,
let description = dict["description"] as? String
else
{
return nil
}
self.id = id
self.title = title
self.speakerName = speakerName
self.description = description
}
Note: id is the node name (the key in the dictionary
on the last slide)
27. Setting a value
let firebaseUserFavorites = firebaseUser?.child("favorites")
let firebaseFavorite = firebaseUserFavorites?.child(sessionId)
firebaseFavorite?.setValue(true)
28. Lists of stuff
ā¢ Convention is to have a dict where keys are ids
and values are just ātrueā
29. Arrays in Firebase ą² _ą²
ā¢ A dictionary with numeric keys in order will be
sent to your observer as an array rather than a
dictionary
ā¢ Not as convenient as youād think. Pretty much a
Firebase anti-pattern
30. observeSingleEvent()
favoriteTitles.removeAll()
for (sessionId, _) in fbFavorites {
firebaseSessions?.child(sessionId).child(ātitle").observeSingleEvent(
of: FIRDataEventType.value, with: { [weak self] snapshot in
if let title = snapshot.value as? String {
self?.favoriteTitles.append(
FavoriteItem(favoriteId: sessionId, title: title))
self?.tableView.reloadData()
}
})
}
Note: observeSingleEvent() does not return a handle
for you to hold on to and dispose later
31. Authentication
ā¢ Firebase provides an email + password system
ā¢ Can also use Google, Twitter, Facebook, or
GitHub credentials
ā¢ Or roll your own and provide an OAuth token
32. Database rules
ā¢ Deļ¬ne per-branch access based on
authentication
ā¢ Can require that user just be logged in, or that
their Firebase user id matches the one that
created the node
34. Public access
// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
Ā "rules": {
Ā Ā ".read": true,
Ā Ā ".write": true
Ā }
}
Private access: just make read/write false
35. User access
// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
Ā "rules": {
Ā Ā "users": {
Ā Ā Ā "$uid": {
Ā Ā Ā Ā ".read": "$uid === auth.uid",
Ā Ā Ā Ā ".write": "$uid === auth.uid"
Ā Ā Ā }
Ā Ā }
Ā }
}
37. MathElf
ā¢ āĆber for high-school math tutoringā
ā¢ Students request help on a topic, are paired with
a tutor in less than a minute
ā¢ Student and tutor work on problems via voice
chat and a shared whiteboard
39. MathElf & Firebase
ā¢ Authentication and user ļ¬nancials are done
through rev.com (parent company) backend,
making REST calls to Firebase
ā¢ Basically everything in the whiteboard and
session history is Firebase
45. Firebase āconās
ā¢ Limited visibility when something goes wrong
ā¢ When things donāt sync, is it them or you?
ā¢ Single point of failure, owned and operated by a
third party
ā¢ Could be Parse all over again
46. Takeaways
ā¢ Firebase database-as-a-service is well-suited to
mobile apps
ā¢ Real-time sync, still works when ofļ¬ine
ā¢ Structure your data as ļ¬at JSON trees, not SQL-
like tables
ā¢ All reads are asynchronous. Hope you like
closures/blocks.
47. Firebase: Totally Not
Parse All Over Again
(Unless It Is)
Chris Adamson (@invalidname)
CocoaConf San Jose ā¢ November, 2016
Slides available at slideshare.net/invalidname
Code available at github.com/invalidstream