Skip to content

Instantly share code, notes, and snippets.

@alexpaul
Last active June 1, 2018 20:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexpaul/88f3e4ae0e3c24348da00853323cc3fe to your computer and use it in GitHub Desktop.
Save alexpaul/88f3e4ae0e3c24348da00853323cc3fe to your computer and use it in GitHub Desktop.
A Firebase startup guide. Built against Firebase 4.11.0. Also includes a Unit Test file (FirebaseTests.swift) that tests against CRUD functions.

Firebase: Quick Start Guide

Firebase Console

It all starts at the Firebase console

Steps

  1. Add a new project

  2. Add Firebase to your iOS app.

    1. Add the iOS bundler from your Xcode app. Register the app.
    2. Download config file (GoogleService-Info.plist) Move the GoogleService-Info.plist file you just downloaded into the root of your Xcode project and add it to all targets.
  3. Adding Authentication: Authenticate and manage users from a variety of providers without server-side code.

    • Setup sign-in method. Some options include Email / Password, Phone, Google......
    • Also included as part of Email / Password Authentication is a Email link (passwordless sign-in) to enable
  4. Database Options:

    • Cloud Firestore (Beta)
    • Realtime Database: default is start in locked mode

    Default values where the database is in locked mode, no reading or writing

      {
       "rules": {
         ".read": false,
         ".write": false
       }
     }

    To allow for authentication

      {
       "rules": {
         ".read": "auth != null",
         ".write": "auth != null"
       }
     }

Authenticate with Firebase using Password-Based Accounts on iOS

Navigate over to your project folder and run pod init This will create a CocoaPods Podfile. Open the Podfile and add the following pod:

pod 'Firebase/Auth'

Now run pod install The following pods will be installed:

Installing Firebase (4.11.0)
Installing FirebaseAnalytics (4.1.0)
Installing FirebaseAuth (4.5.0)
Installing FirebaseCore (4.0.18)
Installing FirebaseInstanceID (2.0.10)
Installing GTMSessionFetcher (1.1.15)
Installing GoogleToolboxForMac (2.1.3)
Installing nanopb (0.3.8)

Close your Xcode project and now navigate to your project folder and open YourProjectName.xcworkspace. This will now be the project you need to work in as the workspace now consists of your installed Pods and required folders.

Navigate to the AppDelegate.swift and add the following:

import Firebase
//
//
//
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Use Firebase library to configure APIs
    FirebaseApp.configure()
    return true
}

testCreateUser

func testCreateUser() {
     let exp = XCTestExpectation.init(description: "user created")
     // password must be 6 characters long or more
     let testEmail = "testuser1@test.com"
     Auth.auth().createUser(withEmail: testEmail, password: "123456") { (user, error) in
         if let error = error {
             XCTFail("create user error: \(error.localizedDescription)")
         }
         guard let user = user else { fatalError("user is nil") }
         if let email = user.email {
             exp.fulfill()
             XCTAssertEqual(email, testEmail)
         } else {
             XCTFail("email is nil")
         }
     }
     wait(for: [exp], timeout: 3.0)
 }

Add Firebase Realtime Database to your app

Add the following pods

pod 'Firebase/Core'
pod 'Firebase/Database'

The following pods will be added after running pod install

Installing Firebase 4.11.0
Installing FirebaseDatabase (4.1.5)
Installing leveldb-library (1.20)

Creating a reference to the database

var ref: DatabaseReference!
ref = Database.database().reference()

Read and Write Data on iOS

Writing data to the Database
testDBAddPost

func testDBAddPost() {
  let exp = XCTestExpectation.init(description: "added post to database")
  // Getting the database reference
  let dbRef = Database.database().reference()
  let key = dbRef.child("posts").childByAutoId().key
  let post = ["userId"    : "12345",
              "created"   : "\(Date().timeIntervalSince1970)",
              "author"    : "Sally",
              "email"     : "sally@sally.com",
              "mediaType" : "text",
              "title"     : "Great Festival",
              "body"      : "Best festival ever."]
  let childUpdates = ["/posts/\(key)": post]
  dbRef.updateChildValues(childUpdates) { (error, dbRef) in
      if let error = error {
          XCTFail("post failed with error: \(error.localizedDescription)")
      } else {
          exp.fulfill()
          XCTAssertNotNil(dbRef)
      }
  }
  wait(for: [exp], timeout: 3.0)
}

Reading data from the Database

To read data at a path and listen for changes, use the observeEventType:withBlock orobserveSingleEventOfType:withBlock methods of FIRDatabaseReference to observe FIRDataEventTypeValue events.

Reads all Posts
testDBFetchAllPosts

func testDBFetchAllPosts() {
  let exp = XCTestExpectation.init(description: "fetched all posts from database")
  let dbRef = Database.database().reference()
  dbRef.child("posts").observe(.value, with: { (snapshot) in
      if let dict = snapshot.value as? [String : Any] {
          exp.fulfill()
          XCTAssertEqual(dict.count, 2)
      } else {
          XCTFail("dict is nil")
      }
  }) { (error) in
      XCTFail("fetch data error: \(error.localizedDescription)")
  }
  wait(for: [exp], timeout: 3.0)
}

Query for a User's Posts
testQueryUserPosts

func testQueryUserPosts() {
  let exp = XCTestExpectation.init(description: "fetched data from database")
  let dbRef = Database.database().reference()
  let query = dbRef.child("posts").queryOrdered(byChild: "userId")
                                  .queryEqual(toValue: "np9VFlQMkAWOAbMQK2e6dqOFpLv2")
  query.observe(.value, with: { (snapshot) in
      if let dict = snapshot.value as? [String : Any] {
          exp.fulfill()
          XCTAssertEqual(dict.count, 1)
      } else {
          XCTFail("dict is nil")
      }
  }) { (error) in
      XCTFail("fetch data error: \(error.localizedDescription)")
  }
  wait(for: [exp], timeout: 3.0)
}

Updating a Child - testDBUpdatePost

func testDBUpdatePost() {
  let exp = XCTestExpectation.init(description: "")
  let dbRef = Database.database().reference()
  let postRef = dbRef.child("posts/-LDoAIG7mnig83oHVzQG")

  postRef.updateChildValues(["title" : "it's friday"]) { (error, dbRef) in
      if let error = error {
          XCTFail("update post error: \(error.localizedDescription)")
      } else {
          exp.fulfill()
          XCTAssertTrue(true, "updated")
      }
  }

  wait(for: [exp], timeout: 3.0)
}

Deleting from the Firebase Realtime Database

func testDeletePost() {
  let exp = XCTestExpectation.init(description: "post was deleted")
  let dbRef = Database.database().reference()
  let postToDeleteRef = dbRef.child("posts/-LDoErqkDrtX48NfFA0b")
  postToDeleteRef.removeValue { (error, dbRef) in
      if let error = error {
          XCTFail("delete post error: \(error.localizedDescription)")
      } else {
          exp.fulfill()
          XCTAssert(true, "post was deleted")
      }
  }
  wait(for: [exp], timeout: 3.0)
}

Firebase Storage

Default security rules for Storage

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Add Cloud Storage to your app

pod 'Firebase/Core'
pod 'Firebase/Storage'

Now the following will be installed

Installing Firebase 4.11.0
Installing FirebaseStorage (2.1.3)

Getting a reference to the storage service:

// gets root reference from Storage
let storageRef = Storage.storage().reference()

Adding an image to Firebase Storage
testStorageUploadImage

func testStorageUploadImage() {
  let exp = XCTestExpectation.init(description: "added image to storage")

  // get image data
  let imageURLString = "https://www.gizmochina.com/wp-content/uploads/2018/02/WWDC-2018.jpg"
  let imageURL = URL(string: imageURLString)!
  let imageData = try! Data.init(contentsOf: imageURL)

  // gets root reference from Storage
  let storageRef = Storage.storage().reference()

  // Create a reference to the file you want to upload
  let uploadImageRef = storageRef.child("images/\(Date().timeIntervalSince1970)")

  // Create the file metadata
  let metadata = StorageMetadata()
  metadata.contentType = "image/jpeg"

  // Upload the file to the path "images/someImaage.jpg"
  let _ = uploadImageRef.putData(imageData, metadata: metadata) { (storageMetadata, error) in
      if let error = error {
          XCTFail("upload image error: \(error.localizedDescription)")
      } else if let storageMetadata = storageMetadata {
          if let downloadURLs = storageMetadata.downloadURLs {
              exp.fulfill()
              XCTAssertGreaterThan(downloadURLs.count, 0)
          }
      }
  }

  wait(for: [exp], timeout: 3.0)
}

Downloading an image from Storage
testStorageDownloadImage

func testStorageDownloadImage() {
  let exp = XCTestExpectation.init(description: "image was downloaded for post using postId")

  // gets root reference from Storage
  let storageRef = Storage.storage().reference()

  // Create a reference to the file you want to upload
  let downloadImageRef = storageRef.child("images/1527780453.37331")

  // Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
  downloadImageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
      if let error = error {
          XCTFail("download image error: \(error.localizedDescription)")
      } else if let data = data {
          let image = UIImage.init(data: data)
          exp.fulfill()
          XCTAssertNotNil(image)
      }
  }

  wait(for: [exp], timeout: 3.0)
}

Deleting an image from Firebase Storage
testStorageDeleteImage

func testStorageDeleteImage() {
  let exp = XCTestExpectation.init(description: "image was deleted from storage")

  // gets root reference from Storage
  let storageRef = Storage.storage().reference()

  // Create a reference to the file you want to upload
  let downloadImageRef = storageRef.child("images/1527779596.7369")

  downloadImageRef.delete { (error) in
      if let error = error {
          XCTFail("delete error: \(error.localizedDescription)")
      } else {
          exp.fulfill()
          XCTAssert(true, "image deleted")
      }
  }
  wait(for: [exp], timeout: 3.0)
}
//
// FirebaseTests.swift
// BlogAppTests
//
// Created by Alex Paul on 5/30/18.
// Copyright © 2018 Alex Paul. All rights reserved.
//
import XCTest
import Firebase
@testable import BlogApp
class FirebaseTests: XCTestCase {
// MARK:- Authentication
func testCreateUser() {
let exp = XCTestExpectation.init(description: "user created")
// password must be 6 characters long or more
let testEmail = "testuser4@test.com"
Auth.auth().createUser(withEmail: testEmail, password: "123456") { (user, error) in
if let error = error {
XCTFail("create user error: \(error.localizedDescription)")
}
guard let user = user else { fatalError("user is nil") }
if let email = user.email {
exp.fulfill()
XCTAssertEqual(email, testEmail)
} else {
XCTFail("email is nil")
}
}
wait(for: [exp], timeout: 3.0)
}
func testSigninUser() {
let exp = XCTestExpectation.init(description: "user signed in")
let email = "testuser2@test.com"
let password = "123456"
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if let error = error {
XCTFail("signin error: \(error.localizedDescription)")
} else if let user = user {
exp.fulfill()
XCTAssertEqual(email, user.email)
}
}
wait(for: [exp], timeout: 3.0)
}
func testSignoutUser() {
let exp = XCTestExpectation.init(description: "user signed out")
do {
try Auth.auth().signOut()
exp.fulfill()
XCTAssert(true, "sign out successful")
} catch {
XCTFail("sign out error: \(error.localizedDescription)")
}
wait(for: [exp], timeout: 3.0)
}
// MARK:- Database
func testDBAddPost() {
let exp = XCTestExpectation.init(description: "added post to database")
let dbRef = Database.database().reference()
let key = dbRef.child("posts").childByAutoId().key
let post = ["userId" : "np9VFlQMkAWOAbMQK2e6dqOFpLv2",
"postId" : "\(key)",
"created" : "\(Date().timeIntervalSince1970)",
"author" : "Rachel",
"email" : "rachel@rachel.com",
"mediaType" : "text",
"title" : "Beach day",
"body" : "Surfs were awesome"]
let childUpdates = ["/posts/\(key)": post]
dbRef.updateChildValues(childUpdates) { (error, dbRef) in
if let error = error {
XCTFail("post failed with error: \(error.localizedDescription)")
} else {
exp.fulfill()
XCTAssertNotNil(dbRef)
}
}
wait(for: [exp], timeout: 3.0)
}
func testDBUpdatePost() {
let exp = XCTestExpectation.init(description: "")
let dbRef = Database.database().reference()
let postRef = dbRef.child("posts/-LDoAIG7mnig83oHVzQG")
postRef.updateChildValues(["title" : "it's friday"]) { (error, dbRef) in
if let error = error {
XCTFail("update post error: \(error.localizedDescription)")
} else {
exp.fulfill()
XCTAssertTrue(true, "updated")
}
}
wait(for: [exp], timeout: 3.0)
}
func testDBFetchAllPosts() {
let exp = XCTestExpectation.init(description: "fetched all posts from database")
let dbRef = Database.database().reference()
dbRef.child("posts").observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String : Any] {
exp.fulfill()
XCTAssertGreaterThan(dict.count, 0)
} else {
XCTFail("dict is nil")
}
}) { (error) in
XCTFail("fetch data error: \(error.localizedDescription)")
}
wait(for: [exp], timeout: 3.0)
}
func testQueryUserPosts() {
let exp = XCTestExpectation.init(description: "fetched user's posts")
let dbRef = Database.database().reference()
let query = dbRef.child("posts").queryOrdered(byChild: "userId")
.queryEqual(toValue: "np9VFlQMkAWOAbMQK2e6dqOFpLv2")
query.observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String : Any] {
exp.fulfill()
XCTAssertEqual(dict.count, 1)
} else {
XCTFail("dict is nil")
}
}) { (error) in
XCTFail("fetch data error: \(error.localizedDescription)")
}
wait(for: [exp], timeout: 3.0)
}
func testDeletePost() {
let exp = XCTestExpectation.init(description: "post was deleted")
let dbRef = Database.database().reference()
let postToDeleteRef = dbRef.child("posts/-LDoErqkDrtX48NfFA0b")
postToDeleteRef.removeValue { (error, dbRef) in
if let error = error {
XCTFail("delete post error: \(error.localizedDescription)")
} else {
exp.fulfill()
XCTAssert(true, "post was deleted")
}
}
wait(for: [exp], timeout: 3.0)
}
// MARK:- Storage
func testStorageUploadImage() {
let exp = XCTestExpectation.init(description: "added image to storage")
// create image data from a url
let imageURLString = "https://www.gizmochina.com/wp-content/uploads/2018/02/WWDC-2018.jpg"
let imageURL = URL(string: imageURLString)!
let _ = try! Data.init(contentsOf: imageURL)
// create image data from the app's bundle
let image = UIImage.init(named: "dog")!
let imageData = UIImagePNGRepresentation(image)!
// gets root reference from Storage
let storageRef = Storage.storage().reference()
// Create a reference to the file you want to upload
let uploadImageRef = storageRef.child("images/\(Date().timeIntervalSince1970)")
// Create the file metadata
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
// Upload the file to the path "images/someImaage.jpg"
let _ = uploadImageRef.putData(imageData, metadata: metadata) { (storageMetadata, error) in
if let error = error {
XCTFail("upload image error: \(error.localizedDescription)")
} else if let storageMetadata = storageMetadata {
if let downloadURLs = storageMetadata.downloadURLs {
exp.fulfill()
XCTAssertGreaterThan(downloadURLs.count, 0)
}
}
}
wait(for: [exp], timeout: 3.0)
}
func testStorageDownloadImage() {
let exp = XCTestExpectation.init(description: "image was downloaded for post using postId")
// gets root reference from Storage
let storageRef = Storage.storage().reference()
// Create a reference to the file you want to upload
let downloadImageRef = storageRef.child("images/1527780453.37331")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
downloadImageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if let error = error {
XCTFail("download image error: \(error.localizedDescription)")
} else if let data = data {
let image = UIImage.init(data: data)
exp.fulfill()
XCTAssertNotNil(image)
}
}
wait(for: [exp], timeout: 3.0)
}
func testStorageDeleteImage() {
let exp = XCTestExpectation.init(description: "image was deleted from storage")
// gets root reference from Storage
let storageRef = Storage.storage().reference()
// Create a reference to the file you want to upload
let downloadImageRef = storageRef.child("images/1527779596.7369")
downloadImageRef.delete { (error) in
if let error = error {
XCTFail("delete error: \(error.localizedDescription)")
} else {
exp.fulfill()
XCTAssert(true, "image deleted")
}
}
wait(for: [exp], timeout: 3.0)
}
func testAsychrounousCall() {
let exp = XCTestExpectation.init(description: "")
wait(for: [exp], timeout: 3.0)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment