【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!データの取得

この記事からわかること

  • Swift/Firebase作成したiOSアプリRealtime Database導入する方法
  • Swift UICocoa Pods使用している場合のインストール方法
  • データベース作成書き込み/読み取りなどの操作方法
  • setValueメソッドupdateChildValuesobserveSingleEventgetDataの使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Firebaseの概要や登録方法については下記記事を参考にしてください。

Realtime Databaseとは?

Firebase Realtime Databaseとはリアルタイムでデータを保存してユーザー間で同期することができるクラウドホスト型NoSQLデータベースです。

Webアプリとモバイルアプリに同じデータを表示させたり、異なるユーザー同士に同じデータを表示させることで共有可能なアプリを作成することができるようになります。

またFirebase Realtime Databaseはデータをサーバーとローカルの2箇所に保持します。ローカルにデータをキャッシュすることでオフラインになってもデータ表示を継続して行えるようになっており、再びオンラインに戻った際に自動でデータを同期してくれます。

アプリからの操作は基本的にはローカルに書き込み処理を行い、サーバーと接続できるタイミングでサーバーと同期します。

データの管理方法

データの保存先は3箇所から選択できるようになっています。同じプロジェクト内でも異なるロケーションにデータベースを構築することができるようです。

公式ドキュメント:リアルタイムデータベースの場所

そして実際のデータはJSON形式で蓄積、管理されていきます。テーブルやレコードではなく、ツリー上になったJSON形式の構造でデータは管理されます。そのため入れ子にできるのは32レベルまでと制限が設けられています。

iOSアプリにRealtime Databaseを導入する流れ

公式ドキュメント:Realtime Databaseのセットアップ方法

流れ

  1. Firebaseプロジェクトを作成
  2. データベースの作成
  3. iOSアプリの登録
  4. GoogleService-Info.plistの追加
  5. SDKの導入
  6. 初期化コードの組み込み
  7. 完了

ここでは通常の「Firebaseプロジェクトを作成」や「iOSアプリの登録」の手順は割愛しています。詳細は以下の記事を参考にしてください。

【Swift/Xcode】Firebaseの導入方法!iOSアプリでの使い方

データベースの作成

Firebaseプロジェクトを作成したら、プロジェクト内にデータベースを作成しておきます。左側メニューの「Realtime Database」>「データベースを作成」をクリックします。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

続いてロケーションを問われるので「米国」にして進めていきます。(※米国ではなく東京(asia-northeast1)か大阪(asia-northeast2)のが良いかもです。)

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

データベースの設定は本番環境であればロックモードで、練習であればテストモードにチェックを入れて「有効にする」をクリックします。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

ロックモードを指定した場合はクライアントがデータの読み取り/書き取りができない状態になっているので「ルール」のfalsetrueに変更しておきます。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

ここまできたらプロジェクトにiOSアプリを登録していきます。

【Swift/Xcode】Firebaseの導入方法!iOSアプリでの使い方

さらに「GoogleService-Info.plist をダウンロード」をクリックし、ダウンロードした「GoogleService-Info.plist」をドラッグ&ドロップでアプリに入れ込みます。

【Swift/Xcode】Firebaseの導入方法!iOSアプリでの使い方

Copy items if needed」と「Create groups」にチェックを入れて「Finish」をクリックします。

【Swift/Xcode】Firebaseの導入方法!iOSアプリでの使い方

Realtime Database SDKの導入

Cocoa Pods」を使用して「Realtime Database SDK」を導入していきます。「PodFile」に以下の一文を追記してpod installを実行します。

pod 'FirebaseDatabase'

おすすめ記事:【Swift UI】CocoaPodsのインストール方法と使い方!

最後にアプリのエントリポイント部分に初期化のためのコードを記述していきます。

import SwiftUI
import FirebaseCore // 追加

// 追加
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

@main
struct TestFirebaseApp: App {
    // 追加
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

これで指定のiOSアプリからRealtime Databaseを使用できるようにすることができました。

使用方法

ここからはiOSアプリ内でRealtime Databaseを操作する方法をまとめていきます。iOSアプリから操作するとリアルタイムでデータベース内のデータが変更されていくのを「データ」の中から確認することができます。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

データベースへの参照

まずはFIRDatabaseReferenceインスタンスを生成してRealtime Databaseへ参照できるようにする必要があります。FirebaseDatabaseをインポートすることでDatabase.database().reference()が使用できるようになり、ここからインスタンスを生成できます。

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

データの書き込み処理

データの書き込み処理setValueメソッドを使用します。引数にはNSStringNSNumberNSDictionaryNSArray型の値を渡すことができます。childメソッドを使用することでネストさせることができます。

ref.child("users").setValue(["username": "ame"])

例えば上記のコードを実行するとデータベース内は以下のようになります。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

データの更新処理

データの更新処理setValueメソッドを使用して上書きすることができます。また先ほどと同様の値を変更するには以下のようにchildメソッドチェーンでつなぐ方法「/」で区切って指定することも可能です。以下は全て同じ深さのデータを変更しています。

ref.child("users").setValue(["username": "kasa"])
ref.child("users").child("username").setValue("kasa")
ref.child("users/username").setValue("kasa")

しかしsetValueメソッドは指定した階層の値を新規で追加(あれば上書き)するためその階層より下に階層があっても消えてしまいます。指定した階層の値だけ書き換えるにはupdateChildValuesメソッドを使用します。

updateChildValues

データを更新するにはupdateChildValuesメソッドを使用した方が良さそうです。任意の深さでメソッドを呼び出すことで引数に指定した値に更新することができます。

 ref.child("users").child("username").updateChildValues("kasa")

また引数に辞書形式でキーパスと値を複数渡すことで一度に複数箇所のデータを更新することも可能です。

guard let key = ref.child("posts").childByAutoId().key else { return }
let post = ["uid": userID,
      "author": username,
      "title": title,
      "body": body]
let childUpdates = ["/posts/\(key)": post,
          "/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)

自動でIDを付与する

childByAutoIdメソッドを使用することで一意となる文字列を付与することができます。

ref.child("users").childByAutoId().setValue("ame")
【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

また生成される文字列はタイムスタンプを元にしているためソートをかけると時系列に並べることができます。

データの削除処理

データの削除処理removeValueメソッドを使用します。指定した値より子要素があった場合はその子要素も全て削除されます。

ref.child("users").removeValue()

また書き込み処理ができるsetValueupdateChildValuesnilを渡すことで削除することも可能です。その場合は一度に複数箇所のデータを削除することもできます。

データの読み取り処理

データの読み取り処理は書き込みや削除よりはややこしくなっています。Realtime Databaseではサーバー側とローカルのキャッシュ側にデータを保持しているのでどちらのデータを読みに行くかを指定することができるようになっています。

この状況の場合で読み取り処理を行なってみます。

【Swift UI/Firebase】Realtime Databaseの導入方法と使い方!

getDataメソッド

getData1回だけデータを読み取るメソッドです。基本的にはサーバーの値を読みにいき、オフライン環境などによってサーバーの値の取得に失敗した場合はローカルキャッシュの値を読みにいきます。

実装コードを見てみるといきなり行数が増えました。読み解いていくと最初の入れ子を掘り進める過程は同じです。値を取り出したい深さまで到達したらgetDataメソッドを呼び出します。引数のcompletionHandlerからエラーと、snapshot(FIRDataSnapshot型)にアクセスすることができます。(スナップショットとは「その時点のもの」といった意味の英単語です)

おすすめ記事:【Swift】completionHandlerとは?使い方と@escapingの意味

ref.child("users").getData(completion:  { error, snapshot in
    guard error == nil else {
        print(error!.localizedDescription)
        return
    }
    if let dic = snapshot?.value as? [String:AnyObject]{
        self.userName = dic["username"] as? String ?? "Unknown"
    }
});

valueプロパティから値にアクセスし適切な型(NSDictionary型:[String:AnyObject]など)に変換してその中に参照します。

データを明示的に1回読みにいくこのメソッドは多用すると帯域幅の使用が増加し、パフォーマンスが低下する可能性があります。

observeSingleEvent

ローカルキャッシュの値を読み込むにはobserveSingleEventメソッドを使用します。リアルタイムでの変更の反映が必要ないデータなどを取得する際はこちらを使用します。

ref.child("users").observeSingleEvent(of: .value, with: { snapshot in
    guard error == nil else {
        print(error!.localizedDescription)
        return
    }
    if let dic = snapshot?.value as? [String:AnyObject]{
        self.userName = dic["username"] as? String ?? "Unknown"
    }
});

observe

サーバーにあるデータを観測し、常に更新されたデータを取得するためにはobserveメソッドを使用します。このメソッドは初期の呼び出し時とサーバーの値が更新されたタイミングに毎回実行されます。しかしツリー状のルートを指定し呼び出すと全ての変更を観測することになるため、必要な深さまでを指定しアタッチするのが推奨されています。

ref.child("users").observe(DataEventType.value, with: {
    print("値が変更されました: \(snapshot.value as AnyObject)")
}

コールバック処理の設置

データを書き込む処理には成功か失敗かをコールバックとして受け取れるようにすることができます。

ref.child("users").setValue(["username": ame]) { (error:Error?, ref:DatabaseReference) in
  if let error = error {
    print("失敗: \(error).")
  } else {
    print("データの保存に成功しました!")
  }
}

オフライン環境でデータをキャッシュする

オフライン環境でもデータを永続的に表示できるようにするためには明示的に設定をする必要があります。

Database.database().isPersistenceEnabled = true

詳細な設定方法は下記記事を参考にしてください。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index