Codelab ของ Firebase iOS

1. ภาพรวม

2efe6805ef369641.png

ยินดีต้อนรับสู่ Codelab ของ Chat ที่เป็นมิตร ใน Codelab นี้ คุณจะได้เรียนรู้วิธีใช้แพลตฟอร์ม Firebase เพื่อสร้างแอปพลิเคชัน iOS คุณจะใช้งานโปรแกรมแชทและตรวจสอบประสิทธิภาพของโปรแกรมแชทด้วย Firebase

สิ่งที่คุณจะได้เรียนรู้

  • อนุญาตให้ผู้ใช้ลงชื่อเข้าใช้
  • ซิงค์ข้อมูลโดยใช้ฐานข้อมูลเรียลไทม์ของ Firebase
  • จัดเก็บไฟล์ไบนารีในพื้นที่เก็บข้อมูลของ Firebase

สิ่งที่คุณต้องมี

  • Xcode
  • CocoaPods
  • อุปกรณ์ทดสอบที่ใช้ iOS 8.0 ขึ้นไปหรือเครื่องมือจำลอง

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านเท่านั้น อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์ในการสร้างแอป iOS อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

2. รับโค้ดตัวอย่าง

โคลนที่เก็บ GitHub จากบรรทัดคำสั่ง

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

3. สร้างแอปเริ่มต้น

2f4c98d858c453fe.png

วิธีสร้างแอปเริ่มต้น

  1. ในหน้าต่างเทอร์มินัล ให้ไปที่ไดเรกทอรี android_studio_โฟลเดอร์.pngios-starter/swift-starter จากการดาวน์โหลดโค้ดตัวอย่าง
  2. การเรียกใช้ครั้งที่ pod install --repo-update
  3. เปิดไฟล์ EasyChatSwift.xcworkspace เพื่อเปิดโปรเจ็กต์ใน Xcode
  4. คลิกปุ่ม 98205811bbed9d74.pngเรียกใช้

คุณควรเห็นหน้าจอหลักของ friendly Chat ปรากฏขึ้นหลังจากผ่านไป 2-3 วินาที UI ควรจะปรากฏขึ้น อย่างไรก็ตาม ณ จุดนี้คุณจะไม่สามารถลงชื่อเข้าใช้ ส่ง หรือรับข้อความได้ แอปจะล้มเลิกโดยมีข้อยกเว้นจนกว่าคุณจะดำเนินการขั้นตอนถัดไปให้เสร็จสมบูรณ์

4. สร้างโปรเจ็กต์คอนโซล Firebase

สร้างโปรเจ็กต์

เลือกเพิ่มโปรเจ็กต์จากคอนโซล Firebase

เรียกใช้โปรเจ็กต์ FriendlyChat แล้วคลิกสร้างโปรเจ็กต์

ภาพหน้าจอจาก 06-11-2015 14:13:39.png

เชื่อมต่อแอป iOS

  1. จากหน้าจอภาพรวมโปรเจ็กต์ของโปรเจ็กต์ใหม่ ให้คลิกเพิ่ม Firebase ไปยังแอป iOS
  2. ป้อนรหัสชุดเป็น "com.google.firebase.codelab.FriendlyChatSwift"
  3. ป้อนรหัส App Store เป็น "123456"
  4. คลิกลงทะเบียนแอป

เพิ่มไฟล์ GoogleService-Info.plist ลงในแอป

ในหน้าจอที่ 2 ให้คลิกดาวน์โหลด GoogleService-Info.plist เพื่อดาวน์โหลดไฟล์การกำหนดค่าที่มีข้อมูลเมตา Firebase ที่จำเป็นทั้งหมดสำหรับแอป คัดลอกไฟล์นั้นไปยังแอปพลิเคชันของคุณ แล้วเพิ่มไปยังเป้าหมาย friendlyChatSwift

ตอนนี้คุณสามารถคลิก "x" ที่มุมบนขวาของป๊อปอัปเพื่อปิด ให้ข้ามขั้นตอนที่ 3 และ 4 ไป เนื่องจากคุณจะต้องดำเนินการตามขั้นตอนดังกล่าวที่นี่

19d59efb213ddbdc.png

นำเข้าโมดูล Firebase

เริ่มต้นด้วยการตรวจสอบว่าได้นำเข้าโมดูล Firebase แล้ว

AppDelegate.swift, FCViewController.swift

import Firebase

กำหนดค่า Firebase ใน AppDelegate

ใช้คอลัมน์ "Configure" ใน FirebaseApp ภายในฟังก์ชัน application:didFinishLaunchingWithOptions เพื่อกำหนดค่าบริการ Firebase ที่สำคัญจากไฟล์ .plist ของคุณ

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"
    }
  }
}

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการทำงานนี้ (รวมถึงเอกสารประกอบเกี่ยวกับตัวแปร "การตรวจสอบสิทธิ์") โปรดดูเอกสารด้านความปลอดภัยของ Firebase

กำหนดค่า API การตรวจสอบสิทธิ์

ก่อนที่แอปพลิเคชันของคุณจะสามารถเข้าถึง API การตรวจสอบสิทธิ์ Firebase ในนามของผู้ใช้ คุณจะต้องเปิดใช้ API ดังกล่าว

  1. ไปที่คอนโซล Firebase แล้วเลือกโปรเจ็กต์
  2. เลือกการตรวจสอบสิทธิ์
  3. เลือกแท็บวิธีการลงชื่อเข้าใช้
  4. สลับสวิตช์ Google เป็นเปิดใช้ (สีน้ำเงิน)
  5. กดบันทึกบนกล่องโต้ตอบที่ปรากฏขึ้น

หากคุณได้รับข้อผิดพลาดในภายหลังใน Codelab นี้พร้อมข้อความ "CONFIGURATION_NOT_FOUND" ให้กลับมาที่ขั้นตอนนี้แล้วตรวจสอบงานอีกครั้ง

ยืนยันทรัพยากร Dependency ของ Firebase

ยืนยันว่ามีทรัพยากร Dependency ของการตรวจสอบสิทธิ์ Firebase ในไฟล์ Podfile

Podfile

pod 'Firebase/Auth'

ตั้งค่า Info.plist สำหรับ Google Sign-In

คุณจะต้องเพิ่มรูปแบบ URL ที่กำหนดเองลงในโปรเจ็กต์ XCode

  1. เปิดการกำหนดค่าโปรเจ็กต์โดยดับเบิลคลิกที่ชื่อโปรเจ็กต์ในมุมมองแบบต้นไม้ด้านซ้าย เลือกแอปของคุณจากส่วน "เป้าหมาย" จากนั้นเลือกแท็บ ข้อมูล แล้วขยายส่วนประเภท URL
  2. คลิกปุ่ม + และเพิ่มรูปแบบ URL สำหรับรหัสไคลเอ็นต์ที่ย้อนกลับ หากต้องการค้นหาค่านี้ ให้เปิดไฟล์การกำหนดค่า GoogleService-Info.plist แล้วมองหาคีย์ REVERSED_CLIENT_ID คัดลอกค่าของคีย์นั้นแล้ววางลงในช่องสคีม URL ในหน้าการกำหนดค่า เว้นฟิลด์อื่นๆ ว่างไว้
  3. เมื่อดำเนินการเสร็จแล้ว การกำหนดค่าควรมีลักษณะคล้ายกับข้อความต่อไปนี้ (แต่มีค่าเฉพาะแอปพลิเคชัน)

1b54d5bd2f4f1448.png

ตั้งค่า clientID สำหรับ Google Sign-In

หลังจากกำหนดค่า Firebase แล้ว เราสามารถใช้ clientID เพื่อตั้งค่าการลงชื่อเข้าใช้ Google ภายใน "didFinishLaunchingWithOptions:" ได้

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 Sign-In เสร็จสมบูรณ์ ให้ใช้บัญชีเพื่อตรวจสอบสิทธิ์กับ 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
      }
    }
  }

ลงชื่อเข้าใช้ให้ผู้ใช้โดยอัตโนมัติ จากนั้นเพิ่ม Listener ของ Firebase Auth เพื่อให้ผู้ใช้เข้าสู่แอปหลังจากลงชื่อเข้าใช้สำเร็จ แล้วเอา Listener ออกให้ฟังนะ

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 Sign-In
  3. จากนั้นระบบจะนำคุณไปยังหน้าจอการรับส่งข้อความหากทุกอย่างเป็นไปอย่างถูกต้อง

6. เปิดใช้งาน Realtime Database

2efe6805ef369641.png

นำเข้าข้อความ

ในโปรเจ็กต์ของคุณในคอนโซล Firebase ให้เลือกรายการฐานข้อมูลที่แถบนำทางด้านซ้าย ในเมนูรายการเพิ่มเติมของฐานข้อมูล ให้เลือกนำเข้า JSON เรียกดูไฟล์ initial_messages.json ในไดเรกทอรี friendlychat แล้วเลือกไฟล์นั้น จากนั้นคลิกปุ่มนำเข้า การดำเนินการนี้จะแทนที่ข้อมูลปัจจุบันในฐานข้อมูล นอกจากนี้คุณยังแก้ไขฐานข้อมูลได้โดยตรงโดยใช้เครื่องหมาย + สีเขียวและ x สีแดงเพื่อเพิ่มและนำรายการออก

20ccf4856b715b4c.png

หลังจากนำเข้าฐานข้อมูลแล้วควรมีลักษณะเช่นนี้

f3e0367f1c9cd187.png

ยืนยันทรัพยากร Dependency ของฐานข้อมูล Firebase

ในบล็อกทรัพยากร Dependency ของไฟล์ Podfile ให้ยืนยันว่ามี Firebase/Database รวมอยู่ด้วย

Podfile

pod 'Firebase/Database'

ซิงค์ข้อมูลข้อความที่มีอยู่

เพิ่มโค้ดที่ซิงค์ข้อความที่เพิ่มใหม่ลงใน UI ของแอป

โค้ดที่คุณเพิ่มในส่วนนี้จะ

  • เริ่มต้นฐานข้อมูล Firebase และเพิ่ม Listener เพื่อจัดการกับการเปลี่ยนแปลงที่ทำในฐานข้อมูล
  • อัปเดต DataSnapshot เพื่อแสดงข้อความใหม่

แก้ไข "deinit", "configureDatabase" และ "tableView:cellForRow indexPath" ของ FCViewController: วิธีการ แทนที่ด้วยโค้ดที่กำหนดไว้ด้านล่าง

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 โดยตรงโดยคลิกเครื่องหมาย + สีเขียวข้าง "ข้อความ" รายการและเพิ่มออบเจ็กต์ดังตัวอย่างต่อไปนี้ f9876ffc8b316b14.png
  4. ยืนยันว่าชื่อดังกล่าวปรากฏใน UI ของ friendly Chat

7. ส่งข้อความ

ใช้การส่งข้อความ

พุชค่าไปยังฐานข้อมูล เมื่อใช้เมธอดพุชเพื่อเพิ่มข้อมูลลงในฐานข้อมูลเรียลไทม์ของ Firebase ระบบจะเพิ่มรหัสอัตโนมัติ รหัสที่สร้างขึ้นโดยอัตโนมัติเหล่านี้จะเรียงตามลำดับ ซึ่งช่วยให้มั่นใจได้ว่าข้อความใหม่จะถูกเพิ่มตามลำดับที่ถูกต้อง

แก้ไข "sendMessage:" ของ FCViewController วิธีการ แทนที่ด้วยโค้ดที่กำหนดไว้ด้านล่าง

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

ในบล็อกทรัพยากร Dependency ของ Podfile ให้ยืนยันว่ามี Firebase/Storage รวมอยู่ด้วย

Podfile

pod 'Firebase/Storage'

เปิดใช้งานพื้นที่เก็บข้อมูลของ Firebase ในหน้าแดชบอร์ด

ไปที่คอนโซล Firebase แล้วยืนยันว่าได้เปิดใช้งานพื้นที่เก็บข้อมูลด้วย "gs://PROJECTID.appspot.com" แล้ว โดเมน

b0438b37a588bcee.png

หากเห็นหน้าต่างการเปิดใช้งานแทน ให้คลิก "เริ่มต้นใช้งาน" เพื่อเปิดใช้งานด้วยกฎเริ่มต้น

c290bbebff2cafa7.png

กำหนดค่า FirebaseStorage

FCViewController.swift

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

รับรูปภาพในข้อความที่มีอยู่

เพิ่มโค้ดที่ดาวน์โหลดรูปภาพจากพื้นที่เก็บข้อมูลของ Firebase

แก้ไข "tableView: cellForRowAt indexPath:" ของ FCViewController วิธีการ แทนที่ด้วยโค้ดที่กำหนดไว้ด้านล่าง

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 และส่งรูปภาพ

อัปโหลดรูปภาพจากผู้ใช้ แล้วซิงค์ URL พื้นที่เก็บข้อมูลของรูปภาพนี้ไปยังฐานข้อมูล เพื่อให้มีการส่งรูปภาพนี้ภายในข้อความ

แก้ไข "image PickerController: DidFinish PickingMediaWithInfo:" ของ FCViewController วิธีการ แทนที่ด้วยโค้ดที่กำหนดไว้ด้านล่าง

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
  • การลงชื่อเข้าใช้แบบรวมศูนย์
  • พื้นที่เก็บข้อมูล

ดูข้อมูลเพิ่มเติม