Firebase iOS Codelab Swift

1. 概要

2efe6805ef369641.png

Friendly Chat の Codelab へようこそ。この Codelab では、Firebase プラットフォームを使用して iOS アプリを作成する方法について説明します。チャット クライアントを実装し、Firebase を使用してそのパフォーマンスをモニタリングします。

ラボの内容

  • ユーザーがログインできるようにします。
  • Firebase Realtime Database を使用してデータを同期する。
  • バイナリ ファイルを Firebase Storage に保存します。

必要なもの

  • Xcode
  • CocoaPods
  • iOS 8.0 以降を搭載したテストデバイスまたはシミュレータ

このチュートリアルをどのように使用されますか?

通読のみ 通読して演習を行う

iOS アプリ作成のご経験についてお答えください。

初心者 中級者 上級者

2. サンプルコードを取得する

コマンドラインから GitHub リポジトリのクローンを作成します。

$ git clone https://github.com/firebase/codelab-friendlychat-ios 

3. スターター アプリを作成する

2f4c98d858c453fe.png

スターター アプリをビルドするには:

  1. ターミナル ウィンドウで、ダウンロードしたサンプルコードの android_studio_folder.pngios-starter/swift-starter ディレクトリに移動します。
  2. 実行 pod install --repo-update
  3. FriendlyChatSwift.xcworkspace ファイルを開き、Xcode でプロジェクトを開きます。
  4. [98205811bbed9d74.png実行] ボタンをクリックします。

数秒後に、フレンドリー チャットのホーム画面が表示されます。UI が表示されます。ただし、現時点ではログインやメッセージの送受信はできません。次のステップを完了するまで、アプリは例外で中止されます。

4. Firebase コンソール プロジェクトを作成する

プロジェクトを作成する

Firebase コンソールで [プロジェクトを追加] を選択します。

プロジェクトを FriendlyChat と呼び出し、[Create Project] をクリックします。

Screenshot from 2015-11-06 14:13:39.png

Firebase の料金プランをアップグレードする

Cloud Storage for Firebase を使用するには、Firebase プロジェクトが従量課金制(Blaze)のお支払いプランCloud 請求先アカウントにリンクされている)である必要があります。

  • Cloud 請求先アカウントには、クレジット カードなどのお支払い方法が必要です。
  • Firebase と Google Cloud を初めて使用する場合は、$300 のクレジットと無料トライアル用 Cloud 請求先アカウントを利用できるかどうかご確認ください。
  • この Codelab をイベントの一環として実施する場合は、利用可能な Cloud クレジットがあるかどうかを主催者に確認してください。

プロジェクトを Blaze プランにアップグレードする手順は次のとおりです。

  1. Firebase コンソールで、プランをアップグレードを選択します。
  2. Blaze プランを選択します。画面上の手順に沿って、Cloud 請求先アカウントをプロジェクトにリンクします。
    このアップグレードの一環として Cloud 請求先アカウントを作成する必要があった場合は、Firebase コンソールのアップグレード フローに移動してアップグレードを完了する必要があります。

iOS アプリを接続する

  1. 新しいプロジェクトの [プロジェクトの概要] 画面で、[iOS アプリに Firebase を追加] をクリックします。
  2. バンドル ID(「com.google.firebase.codelab.FriendlyChatSwift」など)を入力します。
  3. App Store ID を「123456」と入力します。
  4. [アプリを登録] をクリックします。

アプリに GoogleService-Info.plist ファイルを追加する

2 つ目の画面で [GoogleService-Info.plist をダウンロード] をクリックして、アプリに必要なすべての Firebase メタデータを含む構成ファイルをダウンロードします。そのファイルをアプリにコピーし、FriendlyChatSwift ターゲットに追加します。

ポップアップの右上にある [x] をクリックして閉じます。ステップ 3 と 4 はここで行うため、スキップします。

19d59efb213ddbdc.png

Firebase モジュールをインポートする

まず、Firebase モジュールがインポートされていることを確認します。

AppDelegate.swiftFCViewController.swift

import Firebase 

AppDelegate で Firebase を構成する

.plist ファイルから基盤となる Firebase サービスを構成するには、application:didFinishLaunchingWithOptions 関数内の FirebaseApp の「configure」メソッドを使用します。

AppDelegate.swift

  func application(_ application: UIApplication, didFinishLaunchingWithOptions       launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {   FirebaseApp.configure()   GIDSignIn.sharedInstance().delegate = self   return true } 

5. ユーザーを特定する

ルールを使用して認証済みユーザーに制限する

次に、メッセージの読み取りまたは書き込みの前に認証を必須にするルールを追加します。これを行うには、メッセージ データ オブジェクトに次のルールを追加します。Firebase コンソールのデータベース セクションで Realtime Database を選択し、[ルール] タブをクリックします。次のようにルールを更新します。

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

この仕組みの詳細(「auth」変数に関するドキュメントを含む)については、Firebase のセキュリティのドキュメントをご覧ください。

Authentication API を構成する

アプリがユーザーに代わって Firebase Authentication API にアクセスするには、その API を有効にする必要があります。

  1. Firebase コンソールに移動してプロジェクトを選択します。
  2. [認証] を選択します。
  3. [ログイン方法] タブを選択します。
  4. [Google] スイッチを有効(青)に切り替えます。
  5. 表示されたダイアログで [保存] を押します。

この Codelab の後半で「CONFIGURATION_NOT_FOUND」というメッセージとともにエラーが発生した場合は、この手順に戻って作業を再確認してください。

Firebase Authentication の依存関係を確認する

Firebase Auth の依存関係が Podfile ファイルに存在することを確認します。

Podfile

pod 'Firebase/Auth' 

Google ログイン用に Info.plist を設定します。

XCode プロジェクトにカスタム URL スキームを追加する必要があります。

  1. プロジェクト構成を開きます(左側のツリービューでプロジェクト名をダブルクリックします)。[TARGETS] セクションでアプリを選択し、[情報] タブを選択して [URL タイプ] セクションを展開します。
  2. [+] ボタンをクリックし、反転クライアント ID の URL スキームを追加します。この値を確認するには、GoogleService-Info.plist 構成ファイルを開き、REVERSED_CLIENT_ID キーを探します。見つかったキーの値をコピーし、構成ページの [URL スキーム] ボックスに貼り付けます。その他の入力欄は空白にしておきます。
  3. 完了すると、構成は次のようになります(ただし、値はアプリケーションによって異なります)。

1b54d5bd2f4f1448.png

Google ログインのクライアント ID を設定する

Firebase を構成したら、clientID を使用して「didFinishLaunchingWithOptions:」メソッド内で Google ログインを設定できます。

AppDelegate.swift

  func application(_ application: UIApplication, didFinishLaunchingWithOptions       launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {   FirebaseApp.configure()   GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID   GIDSignIn.sharedInstance().delegate = self   return true } 

ログイン ハンドラを追加する

Google ログインの結果が成功したら、そのアカウントを使用して Firebase で認証を行います。

AppDelegate.swift

  func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {     if let error = error {       print("Error \(error)")       return     }      guard let authentication = user.authentication else { return }     let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,                                                       accessToken: authentication.accessToken)     Auth.auth().signIn(with: credential) { (user, error) in       if let error = error {         print("Error \(error)")         return       }     }   } 

ユーザーを自動的にログインさせます。次に、Firebase Auth にリスナーを追加して、ログインが成功した後にユーザーをアプリにアクセスできるようにします。デイナイトでリスナーを削除します。

SignInViewController.swift

  override func viewDidLoad() {     super.viewDidLoad()     GIDSignIn.sharedInstance().uiDelegate = self     GIDSignIn.sharedInstance().signInSilently()     handle = Auth.auth().addStateDidChangeListener() { (auth, user) in       if user != nil {         MeasurementHelper.sendLoginEvent()         self.performSegue(withIdentifier: Constants.Segues.SignInToFp, sender: nil)       }     }   }    deinit {     if let handle = handle {       Auth.auth().removeStateDidChangeListener(handle)     }   } 

ログアウト

ログアウト方法を追加する

FCViewController.swift

  @IBAction func signOut(_ sender: UIButton) {     let firebaseAuth = Auth.auth()     do {       try firebaseAuth.signOut()       dismiss(animated: true, completion: nil)     } catch let signOutError as NSError {       print ("Error signing out: \(signOutError.localizedDescription)")     }   } 

ログイン済みユーザーとしてメッセージの読み取りをテストする

  1. [98205811bbed9d74.png実行] ボタンをクリックします。
  2. すぐにログイン画面が表示されます。Google ログイン ボタンをタップします。
  3. すべてが正常に完了すると、メッセージ スクリーンが表示されます。

6. Realtime Database を有効にする

2efe6805ef369641.png

メッセージをインポートする

Firebase コンソールのプロジェクトで、左側のナビゲーション バーにある [データベース] アイテムを選択します。データベースのオーバーフロー メニューで、[JSON をインポート] を選択します。friendlychat ディレクトリの initial_messages.json ファイルに移動し、選択して [インポート] ボタンをクリックします。これにより、現在データベースに存在するデータがすべて置き換えられます。緑色の + と赤色の x を使用してアイテムを追加、削除することで、データベースを直接編集することもできます。

20ccf4856b715b4c.png

インポート後のデータベースは次のようになります。

f3e0367f1c9cd187.png

Firebase データベースの依存関係を確認する

Podfile ファイルの dependencies ブロックで、Firebase/Database が含まれていることを確認します。

Podfile

pod 'Firebase/Database' 

既存のメッセージを同期する

新しく追加されたメッセージをアプリ UI と同期するコードを追加します。

このセクションで追加するコードは、次の処理を行います。

  • Firebase データベースを初期化し、データベースに加えられた変更を処理するリスナーを追加します。
  • DataSnapshot を更新して、新しいメッセージが表示されるようにします。

FCViewController の「deinit」、「configureDatabase」、「tableView:cellForRow indexPath:」メソッドを変更し、以下で定義したコードに置き換えます。

FCViewController.swift

  deinit {     if let refHandle = _refHandle {       self.ref.child("messages").removeObserver(withHandle: _refHandle)     }   }     func configureDatabase() {     ref = Database.database().reference()     // Listen for new messages in the Firebase database     _refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in       guard let strongSelf = self else { return }       strongSelf.messages.append(snapshot)       strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)     })   }     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {     // Dequeue cell     let cell = self.clientTable.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)     // Unpack message from Firebase DataSnapshot     let messageSnapshot = self.messages[indexPath.row]     guard let message = messageSnapshot.value as? [String: String] else { return cell }     let name = message[Constants.MessageFields.name] ?? ""     let text = message[Constants.MessageFields.text] ?? ""     cell.textLabel?.text = name + ": " + text     cell.imageView?.image = UIImage(named: "ic_account_circle")     if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),         let data = try? Data(contentsOf: URL) {       cell.imageView?.image = UIImage(data: data)     }     return cell   } 

メッセージ同期をテストする

  1. [98205811bbed9d74.png実行] ボタンをクリックします。
  2. [ログインして開始] ボタンをクリックしてメッセージ ウィンドウに移動します。
  3. Firebase コンソールで新しいメッセージを直接追加するには、[messages] エントリの横にある緑色の + 記号をクリックし、次のようなオブジェクトを追加します。f9876ffc8b316b14.png
  4. これらのメッセージが Friendly-Chat UI に表示されていることを確認します。

7. メッセージを送信する

Send Message を実装する

値をデータベースにプッシュします。push メソッドを使用して Firebase Realtime Database にデータを追加すると、自動 ID が追加されます。これらの自動生成 ID は連続するため、新しいメッセージが正しい順序で追加されます。

FCViewController の「sendMessage:」メソッドを変更し、以下で定義したコードに置き換えます。

FCViewController.swift

  func sendMessage(withData data: [String: String]) {     var mdata = data     mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName     if let photoURL = Auth.auth().currentUser?.photoURL {       mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString     }      // Push data to Firebase Database     self.ref.child("messages").childByAutoId().setValue(mdata)   } 

メッセージの送信をテストする

  1. [98205811bbed9d74.png実行] ボタンをクリックします。
  2. [ログイン] をクリックして、メッセージ ウィンドウに移動します。
  3. メッセージを入力して送信します。新しいメッセージがアプリの UI と Firebase コンソールに表示されます。

8. 画像の保存と受信

Firebase Storage の依存関係を確認する

Podfile の dependencies ブロックで、Firebase/Storage が含まれていることを確認します。

Podfile

pod 'Firebase/Storage' 

Cloud Storage for Firebase を設定する

Firebase プロジェクトで Cloud Storage for Firebase を設定する方法は次のとおりです。

  1. Firebase コンソールの左側のパネルで [ビルド] を開き、[ストレージ] を選択します。
  2. [開始する] をクリックします。
  3. デフォルトの Storage バケットのロケーションを選択します。
    US-WEST1US-CENTRAL1US-EAST1 のバケットは、Google Cloud Storage の「Always Free」階層を利用できます。他のすべてのロケーションのバケットは、Google Cloud Storage の料金と使用量に従います。
  4. [テストモードで開始] をクリックします。セキュリティ ルールに関する免責条項を確認します。
    この Codelab の後半で、セキュリティ ルールを追加してデータを保護します。Storage バケットのセキュリティ ルールを追加せずに、アプリを配布または公開しないでください。
  5. [作成] をクリックします。

FirebaseStorage を構成する

FCViewController.swift

  func configureStorage() {     storageRef = Storage.storage().reference()   } 

既存のメッセージで画像を受信する

Firebase Storage から画像をダウンロードするコードを追加します。

FCViewController の「tableView: cellForRowAt indexPath:」メソッドを変更し、以下で定義したコードに置き換えます。

FCViewController.swift

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {     // Dequeue cell     let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)     // Unpack message from Firebase DataSnapshot     let messageSnapshot: DataSnapshot! = self.messages[indexPath.row]     guard let message = messageSnapshot.value as? [String:String] else { return cell }     let name = message[Constants.MessageFields.name] ?? ""     if let imageURL = message[Constants.MessageFields.imageURL] {       if imageURL.hasPrefix("gs://") {         Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in           if let error = error {             print("Error downloading: \(error)")             return           }           DispatchQueue.main.async {             cell.imageView?.image = UIImage.init(data: data!)             cell.setNeedsLayout()           }         }       } else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {         cell.imageView?.image = UIImage.init(data: data)       }       cell.textLabel?.text = "sent by: \(name)"     } else {       let text = message[Constants.MessageFields.text] ?? ""       cell.textLabel?.text = name + ": " + text       cell.imageView?.image = UIImage(named: "ic_account_circle")       if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),           let data = try? Data(contentsOf: URL) {         cell.imageView?.image = UIImage(data: data)       }     }     return cell   } 

9. 画像メッセージを送信する

Store and Send Images を実装する

ユーザーから画像をアップロードし、この画像のストレージ URL をデータベースと同期して、この画像がメッセージ内に送信されるようにします。

FCViewController の「imagePickerController: didFinishPickingMediaWithInfo:」メソッドを変更し、以下で定義したコードに置き換えます。

FCViewController.swift

  func imagePickerController(_ picker: UIImagePickerController,     didFinishPickingMediaWithInfo info: [String : Any]) {       picker.dismiss(animated: true, completion:nil)     guard let uid = Auth.auth().currentUser?.uid else { return }      // if it's a photo from the library, not an image from the camera     if #available(iOS 8.0, *), let referenceURL = info[UIImagePickerControllerReferenceURL] as? URL {       let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)       let asset = assets.firstObject       asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in         let imageFile = contentEditingInput?.fullSizeImageURL         let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"         guard let strongSelf = self else { return }         strongSelf.storageRef.child(filePath)           .putFile(from: imageFile!, metadata: nil) { (metadata, error) in             if let error = error {               let nsError = error as NSError               print("Error uploading: \(nsError.localizedDescription)")               return             }             strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])           }       })     } else {       guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }       let imageData = UIImageJPEGRepresentation(image, 0.8)       let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"       let metadata = StorageMetadata()       metadata.contentType = "image/jpeg"       self.storageRef.child(imagePath)         .putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in           if let error = error {             print("Error uploading: \(error)")             return           }           guard let strongSelf = self else { return }           strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])       }     }   } 

画像メッセージの送受信をテストする

  1. [98205811bbed9d74.png実行] ボタンをクリックします。
  2. [ログイン] をクリックして、メッセージ ウィンドウに移動します。
  3. [写真を追加] アイコンをクリックして写真を選びます。写真を含む新しいメッセージが、アプリの UI と Firebase コンソールに表示されます。

10. 完了

Firebase を使用してリアルタイム チャット アプリケーションを簡単に作成しました。

学習した内容

  • Realtime Database
  • 連携ログイン
  • ストレージ

詳細