Firebase iOS Codelab Swift

1. ภาพรวม

2efe6805ef369641.png

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

Codelab นี้มีอยู่ใน Objective-C ด้วย

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

  • อนุญาตให้ผู้ใช้ลงชื่อเข้าใช้
  • ซิงค์ข้อมูลโดยใช้ฐานข้อมูลเรียลไทม์ของ Firebase
  • จัดเก็บไฟล์ไบนารีใน 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.png ios-starter/swift-starter ไดเรกทอรีจากโค้ดตัวอย่างของคุณดาวน์โหลด
  2. เรียก pod install --repo-update
  3. เปิดไฟล์ FriendlyChatSwift.xcworkspace เพื่อเปิดโครงการใน Xcode
  4. คลิก 98205811bbed9d74.png ปุ่มเรียกใช้

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

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

สร้างโครงการ

จาก Firebase คอนโซล เลือกเพิ่มโครงการ

โทรโครงการ FriendlyChat แล้วคลิกที่สร้างโครงการ

สกรีนช็อตจาก 2015-11-06 14:13:39.png

เชื่อมต่อแอพ iOS ของคุณ

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

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

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

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

19d59efb213ddbdc.png

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

เริ่มต้นด้วยการทำให้แน่ใจว่า Firebase โมดูลจะถูกนำเข้า

AppDelegate.swift , FCViewController.swift

import Firebase

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

ใช้เมธอด "กำหนดค่า" ใน 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 คอนโซล เลือกฐานข้อมูลเรียลไทม์จากนั้นคลิกที่แท็บกฎ จากนั้นอัปเดตกฎเพื่อให้มีลักษณะดังนี้:

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

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

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

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

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

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

ยืนยันการพึ่งพา Firebase Auth

ยืนยันการอ้างอิง Firebase รับรองความถูกต้องอยู่ใน Podfile ไฟล์

Podfile

pod 'Firebase/Auth'

ตั้งค่า Info.plist ของคุณสำหรับการลงชื่อเข้าใช้ Google

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

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

1b54d5bd2f4f1448.png

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

หลังจากกำหนดค่า Firebase เราสามารถใช้ clientID เพื่อตั้งค่า Google Sign In ภายในเมธอด "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 สำเร็จแล้ว ให้ใช้บัญชีเพื่อตรวจสอบสิทธิ์กับ 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 บน deinit

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. เปิดใช้งานฐานข้อมูลเรียลไทม์

2efe6805ef369641.png

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

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

20ccf4856b715b4c.png

หลังจากนำเข้าฐานข้อมูลของคุณควรมีลักษณะดังนี้:

f3e0367f1c9cd187.png

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

ในการอ้างอิงการปิดกั้นของ 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 แชทที่เป็นมิตร

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

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

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

แก้ไขเมธอด "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

ในการอ้างอิงการปิดกั้นของ Podfile ยืนยัน Firebase/Storage รวมอยู่

Podfile

pod 'Firebase/Storage'

เปิดใช้งาน Firebase Storage ในแดชบอร์ด

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

b0438b37a588bcee.png

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

c290bbebff2cafa7.png

กำหนดค่า FirebaseStorage

FCViewController.swift

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

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

เพิ่มโค้ดที่ดาวน์โหลดรูปภาพจาก Firebase Storage

แก้ไขเมธอด "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 ที่เก็บข้อมูลของรูปภาพนี้กับฐานข้อมูล เพื่อให้รูปภาพนี้ถูกส่งไปภายในข้อความ

แก้ไขเมธอด "imagePickerController: didFinishPickingMediaWithInfo:" ของ 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 เพื่อสร้างแอปพลิเคชันแชทแบบเรียลไทม์ได้อย่างง่ายดาย

สิ่งที่เราได้กล่าวถึง

  • ฐานข้อมูลเรียลไทม์
  • เข้าสู่ระบบสหพันธรัฐ
  • พื้นที่จัดเก็บ

เรียนรู้เพิ่มเติม