MCSv3

Mobile Development


Ilya Loshkarev
loshkarev.i@gmail.com

Backend As A Service

The Concept of BaaS

An API provider that covers most of the mobile application server tasks

Pros:

  • Easy to start
  • Easy to maintain

Cons:

  • Limited features
  • Pricing

Options

CloudKit Firebase AWS
Supplier Apple Google Amazon
Platforms iOS, JS, Web iOS, Android, JS, Web, C++, Unity iOS, Android, JS, Web, C++, Unity
Complexity Low Average High
Open Source - +/- -

Prices are usually scaled on data per user basis.

CloudKit Services

CloudKit Logo
  • Remote Database
  • Authentication with Apple ID
  • Shared Storage
  • Notifications

Requires Apple Developer account to use

Firebase Services

Firebase Logo
  • Realtime Database
  • Authentication
  • Cloud Storage
  • Cloud Functions
  • Notifications
  • Web-Hosting
  • And More...

Firebase Setup

  1. Create new iOS project in Firebase console
  2. Configure required parameters
  3. Get GoogleService-Info.plist from online project configurator
  4. Add GoogleService-Info.plist to your XCode project
  5. Install Firebase SDK for iOS

Install Firebase

Podfile


							use_frameworks!
							source 'https://github.com/CocoaPods/Specs.git'
							target 'firebase-test' do
								pod 'Firebase/Core'
								pod 'Firebase/Auth'
								pod 'Firebase/Database'
								# ...
							end
						

							func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
								FirebaseApp.configure()
							}
						

Authentication

Firebase Authentication

Identity Providers

Firebase supports several providers of user credentials:

Also a backend service,
easy-to-use SDKs,
and ready-made UI

  • Google
  • Twitter
  • Facebook
  • Git
  • Email
  • Phone
  • Anonimous
  • Customized

Users in Firebase

A Firebase User has a fixed set of basic properties

  • Unique ID
  • Display Name
  • Email
  • photoUrl

User info is stored in a database separate from project data

User lifecycle

There several events your app can react to

  1. User is signed in at startup
  2. User signs in
  3. User signs out
  4. User's access token is refreshed
    When user is promted to reauthenticate or by expiration

Authentication Setup

  1. Enable required auth method in Firebase console
  2. Update your GoogleService-Info.plist
  3. Add SDK for a provider
  4. Add required options to the project configuration

Firebase UI

Firebase UI
  • Multiple providers
  • Account management
  • Account linking
  • Customizable

Firebase UI Setup


							FirebaseApp.configure()
							let authUI = FUIAuth.defaultAuthUI()!
							authUI.providers = [
								FUIGoogleAuth(), FUIFacebookAuth()
							]
							authUI.delegate = self
							self.present(authUI.authViewController(), animated: true)
						

							func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
								let sourceApplication = options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String?
								if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication) ?? false {
									return true
								}
								// other URL handling goes here.
								return false
							}
						

Firebase UI Auth Delegate

Handles user's signIn event and introduces callbacks for UI customization


							func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {
  							guard let user = user, error == nil else { return }
								self.title = user.displayName
							}
							func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
								// return custom picker subclass
							}
						

Manual Authentication

Auth object allows to implement custom flow for user authorisation


							Auth.auth().addStateDidChangeListener { (auth, user) in
								if let u = user {
									print("Signed In as ", u.displayName)
									try! Auth.auth().signOut()
								} else {
									print("Signed Out")
								}
							}
							Auth.auth().signIn(withEmail: emailStr, password: passwordStr)
						

Storage

Cloud Storage for Firebase

Provides a declarative rules language that allows you to define how your data should be structured, how it should be indexed, and when your data can be read from and written to


							let storage = Storage.storage()
						

Storage Reference

A reference can be thought of as a pointer to a file or catalog in the cloud


							let storageRef = storage.reference()
							var spaceRef = storageRef.child("images/space.jpg")
							let path = spaceRef.fullPath;
							let name = spaceRef.name;
							let images = spaceRef.parent()
						

Files are stored in heirarchal manner

Upload Files

You can upload data from memory or files directly from your device


							let localFile = URL(string: "path/to/image")!
							let mountainsRef = storageRef.child("mountains.jpg")
							let uploadTask = mountainsRef.putFile(from: localFile, metadata: nil) {
								metadata, error in
								if let error = error {
									// Uh-oh, an error occurred!
								} else {
									// Metadata contains file metadata such as size, content-type, and download URL.
									let downloadURL = metadata!.downloadURL()
								}
							}
						

You cannot upload data with a reference to the root

Download Files


							let starsRef = storage.reference(withPath: "images/stars.jpg")
							let downloadTask = starsRef.write(toFile: localURL) {
								url, error in
								if let error = error {
									// Uh-oh, an error occurred!
								} else {
									// Local file URL for "images/island.jpg" is returned
								}
							}
						

Managing Tasks

Upload and Download Tasks can be managed in a similar manner


							let uploadTask = mountainsRef.putFile(from: localFile
							uploadTask.pause()
							uploadTask.resume()
							uploadTask.cancel()
							let observer = uploadTask.observe(.progress) {
								snapshot in
								// A progress event occured
							}
						

All observers are removed automaticaly once the task is finished

Delete Files


							let desertRef = storageRef.child("desert.jpg")
							desertRef.delete { error in
								if let error = error {
									// Uh-oh, an error occurred!
								} else {
									// File deleted successfully
								}
							}
						

SDWebImage and FirebaseUI

https://github.com/rs/SDWebImage

SDWebImage provides an async image downloader with cache support


							let reference = storageRef.child("images/stars.jpg")
							let imageView: UIImageView = self.imageView
							let placeholderImage = UIImage(named: "placeholder.jpg")
							imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)
						

Downloads image directly into imageView

Database

Firebase Realtime Database

Data is stored as JSON and synchronized in realtime to every connected client


							"chats": {
								"one": {
									"title": "Historical Tech Pioneers",
									"lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
									"timestamp": 1459361875666
								},
								"two": { ... },
								"three": { ... }
							}
						

Database Reference

Represents a particular location in your Firebase Database
and can be used for reading or writing data to that Firebase Database location


							Database.database().reference()
						

Write Data


							notesRef.child("01").setValue(["title": title])
						

Read Data

Snapshots


							notesRef.observeSingleEvent(of: .value, with: {
								(snapshot) in
								let value = snapshot.value as? [String:Any] ?? [:]
								/* ... */
							} ) {
								(error) in
								print(error.localizedDescription)
							}
						

Update and Delete Data


							deletedRef.removeValue()
							let updates = ["notes/01" : updNote.toDict(),
										"total-notes" : notes.count ]
							updatedRef.updateCildValues(updates)
						

Listen to Data Changes


							notesObserver = notesRef.observe(.value, with: {
								(snapshot) in
								Note.storage.removeAll()
								for child in snapshot.children {
									let note = Note(withDict: child.value as? [String:Any] ?? [:])
									Note.storage.append(note)
								}
							})
						

Always listen to the changes on the lowes level possible

Listen to Child Events


							notesRef.observe(.childAdded, with: { (snapshot) -> Void in
							  self.notes.append(snapshot)
							})
							notesRef.observe(.childRemoved, with: { (snapshot) -> Void in
							  let index = self.indexOfNote(snapshot)
							  self.notes.remove(at: index)
							})
						

Responsible observation

Listeners have to be removed explicitly to stop data syncronization


							notesRef.removeAllObservers()
							notesRef.child("special").removeObserver(withHandle: specialObserver)
						

Persistance

Firebase can store syncronized data locally


							Database.database().isPersistenceEnabled = true
						

Firebase synchronizes and stores a local copy of the data for active listeners but can also be forced to sync


							Database.database().reference(withPath: "scores").keepSynced(true)
						

Related Resources