FirebaseDatabase - Read

之前有寫過 FirebaseDatabase REST API的文章,
而這篇則會是在 iOS 上的使用。

安裝套件

由於 Google 認為 Carthage 的方式不符合他們的使用模式,
畢竟 Firebase 的 framework 並非是開源的,
所以只有提供 CocoaPods 的安裝方式或是直接下載檔案;
而我這邊就以 CocoaPods 來安裝 Firebase 相關的套件,其他則用 Carthage 來管理。

設定

我們在 Firebase console 那先建立好專案並匯入 GoogleService-Info.plist,
如果你有多個 Target 要使用的話,建議放在不同的資料夾,並且設定好 Target Membership。
並且要注意 Firebase console 內的 Database rules,
若沒有做 auth 相關內容的話,記得要調整;
如我開放給 App 讀取但不可寫入的話:

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

接著在 AppDelegate.swift 中加入

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

順道提醒一下,若要讓 Database 的資料在離線也能使用上一次的 cache 的話,
需要在 AppDelegate.swift 裡頭加入

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // --**--
    Database.database().isPersistenceEnabled = true
    // --**--
}

官方文件中沒有特別註記,但若在其他地方執行這段程式,則會直接報錯。

讀取資料

Firebase Database 所提供的是一個可監聽的資料庫,
來做到 Realtime Database 的效果;
Reference 便是 path 的概念,
假設我的資料長這樣:

{
    "test": {
        "user": {
            "name": "Archie"
        }
    }
}

則 ref 有幾種設法;

  • 觀察全部的資料: Database.database().reference.observe
  • 只看 test 下的異動: Database.database().reference.child("test").observe
  • 只看 user:Database.database().reference.child("test").child("user").observe

隨著資料的結構,我們可以讓每個地方只專注它需要監聽的部分就好。

CocoaPods

CocoaPods

上一篇介紹 Carthage,這篇寫一下 CocoaPods。
其實我個人是先使用 CocoaPods,後來才部分改用 Carthage 作為主要的管理方式;
而 CocoaPods 的安裝方式也很簡單,可以透過 gem 來安裝:

安裝

sudo gem install cocoapods

不同於 Carthage 的 Cartfile,CocoaPods 的是使用 Podfile
格式有些不同,不過都可以在資料夾下使用 Terminal 輸入初始化的指令:

pod init

系統就會自行建立一個 Podfile,並用 pod search 的指令來找找你要什麼東西。
弄完 Podfile 之後,就可以執行安裝進專案:

pod install

並打開它幫你建立的 .xcworkspace 就好了。

一些分享

  • 可安裝的版本和 GitHub 上寫的不同?

CocoaPods 會建立一個 local 的所有 repo 資訊,若找不到的時候,可以先試著更新它:

pod repo update
  • Carthage 和 CocoaPods 是可以混用的

Carthage

Carthage

Carthage 是一個套件管理的開源軟體,可以使用 Homebrew 安裝:

brew install carthage

和 CocoaPods 的差異在於它並不會將第三方的 Framework 直接加入專案,
而是獨立在外,並在 build 的時候直接將 framework 複製進去到 App 內;
CocoaPods 的話則會在每次 build 的時候同時 build 你所匯入的 framework,
而隨著專案使用到的套件內容越多,則會花越多時間。

去中心化

這個詞最近隨著區塊鏈的爆紅,也常常可以看見。
不過在這邊的情境是,CocoaPods 是由它那邊搜集很多個 repo,提供給我們來安裝;
所以若 CocoaPods 的 repo 資料沒更新,或是作者只放在 GitHub 上,沒有提交到 CocoaPods 的話,
則無法使用。
而 Carthage 則沒有這方面的問題,可以自行將 GitHub 上的專案加入到 Cartfile

github "ReactiveX/RxSwift"

不過這也是比較麻煩的地方,我們就沒有辦法像 CocoaPods 一樣直接在 Terminal 下 pods search RxSwift 來取得資訊。

個人的使用方式

由於我自行在開發,同時會有很多個專案用到相同的 framework(e.g RxSwift、Siren),
原本若是使用 CocoaPods 的話,則會在很多資料夾內都有一樣的東西;
而 Carthage 的話,則是將 FRAMEWORK_SEARCH_PATHS 都指定到同一個資料夾即可,
並在 Build phase 加上 Carthage 的 Run script。
而我大多數只會開發 iOS 的 App,Carthage update 的時候則可以只更新 iOS 的部分:

carthage update --platform iOS

MKGeodesicPolyline

先來看看 MKGeodesicPolyline 在 Apple Developer Documentation 上的介紹:
A line-based shape that follows the contours of the Earth to create the shortest path between the specified points.

繪製 Polyline

首先我們在建置 MKGeodesicPolyline 的時候,
給予它一個 [CLLocationCoordinate2D],並宣告要繪製幾個點;
接著讓 MKMapView 新增進去。

let geodesicPolyline = MKGeodesicPolyline.init(coordinates: [start, end], count: 2)
mapView.add(geodesicPolyline)

再來我們需要透過 MKMapViewDelegate 的 function 來定義 MKGeodesicPolyline 的 UI:

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    guard let polyline = overlay as? MKPolyline else {
        return MKOverlayRenderer(overlay: overlay)
    }
    let renderer = MKPolylineRenderer.init(polyline: polyline)
    renderer.lineWidth = 1
    renderer.strokeColor = .red
    return renderer
}

取得 Polyline 中間的經緯度

MKGeodesicPolyline 的繪製是由很多個點所連起來的,
可以利用 points() -> UnsafeMutablePointer 來取得。

@available(iOS 4.0, *)
open class MKMultiPoint : MKShape {
    open func points() -> UnsafeMutablePointer<MKMapPoint>
    open var pointCount: Int { get }
    // Unproject and copy points into the provided array of coordinates that
    // must be large enough to hold range.length coordinates.
    open func getCoordinates(_ coords: UnsafeMutablePointer<CLLocationCoordinate2D>, range: NSRange)
}

我們可以透過 pointCount 來得知有幾個點,
接著決定要使用哪個點的位置;

MKCoordinateForMapPoint(polyline.points()[index])

從我家到 AT&T Center 的範例

Demo

Siren - 通知使用者更新

我們時常可以在 App 之中看到,「目前有新版本可以提供下載」等相關的訊息;
而實作通知使用者更新的方法很多,這篇則是介紹一個開源的 Framework:

Siren

Siren 的運作邏輯是,你可以透過版號來決定跳出什麼通知來提醒使用者,
並且提供多語系的訊息內容。
它會透過 Bundle Identifier 去 App Store 上尋找資訊,
再來比對版號執行後續動作。

版號的定義

1.0.123.5678

  • 1:major
  • 0:minor
  • 123:patch
  • 5678:revision

一般我個人的習慣是:
major 會是在商業模式改變或是重大功能發布時,才會動到的;
而 minor 則是有必要的更新,像是嚴重的 bug 或是無法向下相容的異動。
patch 是更新一些 issue 或是修正 bug;
revision 則讓它跟著 commit 的數量。

Siren 的設定

舉個例子,在 major、minor 有提升時;
像是從 1.0.0 -> 2.0.0 或是 1.0.0 -> 1.1.0,
我會希望舊的使用者一定要更新 App 才能使用,
則會設為強制更新(.force)。
而 patch 則讓使用則決定要不要更新,或是可以跳過此次更新。

Siren.shared.majorUpdateAlertType = .force
Siren.shared.minorUpdateAlertType = .force
Siren.shared.patchUpdateAlertType = .skip
Siren.shared.revisionUpdateAlertType = .none

還沒上架前的測試

Siren 建議可以先將 Bundle Identifier 更改為 iTunes Connect Mobile 的 Bundle Identifier:com.apple.itunesconnect.mobile
並把 Siren 的 debugEnabled 調整為 true。

Bitnami