Добро пожаловать в лабораторию «Дружественный чат». В этой кодовой лаборатории вы узнаете, как использовать платформу Firebase для создания приложений iOS. Вы реализуете чат-клиент и будете отслеживать его производительность с помощью Firebase.
Эта лаборатория кода также доступна в Objective-C.
Что ты узнаешь
- Разрешить пользователям входить в систему.
- Синхронизируйте данные с помощью базы данных Firebase Realtime.
- Храните двоичные файлы в Firebase Storage.
Что вам понадобится
- Xcode
- Какао-стручки
- Тестовое устройство с iOS 8.0+ или симулятор
Как вы будете использовать это руководство?
Как бы вы оценили свой опыт создания приложений для iOS?
Клонируйте репозиторий GitHub из командной строки.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
Чтобы создать стартовое приложение:
- В окне терминала перейдите к
каталог
ios-starter/swift-starter
из загрузки вашего образца кода - Запустить
pod install --repo-update
- Откройте файл FriendlyChatSwift.xcworkspace, чтобы открыть проект в Xcode.
- Щелкните значок
Кнопка запуска .
Через несколько секунд вы должны увидеть главный экран Friendly Chat. Должен появиться пользовательский интерфейс. Однако на этом этапе вы не можете входить в систему, отправлять или получать сообщения. Приложение будет прервано с исключением, пока вы не выполните следующий шаг.
Создать проект
В консоли Firebase выберите Добавить проект .
Вызовите проект FriendlyChat
, затем нажмите Create Project .
Подключите приложение для iOS
- На экране «Обзор проекта» вашего нового проекта нажмите « Добавить Firebase в приложение iOS» .
- Введите идентификатор пакета, например
com.google.firebase.codelab.FriendlyChatSwift
. - Введите идентификатор App Store как «
123456
». - Щелкните Зарегистрировать приложение .
Добавьте файл GoogleService-Info.plist в свое приложение
На втором экране нажмите « Загрузить GoogleService-Info.plist», чтобы загрузить файл конфигурации, содержащий все необходимые метаданные Firebase для вашего приложения. Скопируйте этот файл в свое приложение и добавьте его в цель FriendlyChatSwift .
Теперь вы можете щелкнуть «x» в правом верхнем углу всплывающего окна, чтобы закрыть его - пропуская шаги 3 и 4 - поскольку вы будете выполнять эти шаги здесь.
Импортировать модуль 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
}
Используйте правила для ограничения только аутентифицированными пользователями
Теперь мы добавим правило, требующее аутентификации перед чтением или записью любых сообщений. Для этого мы добавляем следующие правила к нашему объекту данных сообщений. В разделе «База данных» консоли Firebase выберите «База данных в реальном времени», затем щелкните вкладку «Правила». Затем обновите правила, чтобы они выглядели так:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Дополнительную информацию о том, как это работает (включая документацию по переменной auth), см. В документации по безопасности Firebase.
Настроить API аутентификации
Прежде чем ваше приложение сможет получить доступ к API аутентификации Firebase от имени ваших пользователей, вам необходимо включить его.
- Перейдите в консоль Firebase и выберите свой проект.
- Выберите аутентификацию
- Выберите вкладку Метод входа.
- Установите переключатель Google в положение включено (синий)
- Нажмите Сохранить в появившемся диалоговом окне.
Если вы получите ошибки позже в этой кодовой таблице с сообщением «CONFIGURATION_NOT_FOUND», вернитесь к этому шагу и дважды проверьте свою работу.
Подтвердите зависимость Firebase Auth
Убедитесь, что в файле Podfile
существуют зависимости Firebase Auth.
Подфайл
pod 'Firebase/Auth'
Настройте свой Info.plist для входа в Google.
Вам нужно будет добавить настраиваемую схему URL-адресов в свой проект XCode.
- Откройте конфигурацию проекта: дважды щелкните имя проекта в левом дереве. Выберите свое приложение в разделе ЦЕЛИ, затем выберите вкладку «Информация» и разверните раздел «Типы URL-адресов».
- Нажмите кнопку + и добавьте схему URL для вашего обратного идентификатора клиента. Чтобы найти это значение, откройте файл конфигурации GoogleService-Info.plist и найдите ключ REVERSED_CLIENT_ID. Скопируйте значение этого ключа и вставьте его в поле Схемы URL-адресов на странице конфигурации. Остальные поля оставьте пустыми.
- По завершении ваша конфигурация должна выглядеть примерно так (но со значениями, зависящими от вашего приложения):
Установить clientID для входа в Google
После настройки 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 используйте учетную запись для аутентификации в 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)")
}
}
Проверьте чтение сообщений от имени вошедшего пользователя
- Щелкните значок
Кнопка запуска .
- Вы должны быть немедленно отправлены на экран входа в систему. Нажмите кнопку входа в Google.
- Затем вы должны быть отправлены на экран обмена сообщениями, если все сработало.
Импортировать сообщения
В вашем проекте в консоли Firebase выберите пункт « База данных» на левой панели навигации. В дополнительном меню базы данных выберите Импорт JSON . Перейдите к файлу initial_messages.json
в каталоге дружественного чата, выберите его и нажмите кнопку « Импорт» . Это заменит все данные в вашей базе данных. Вы также можете редактировать базу данных напрямую, используя зеленый + и красный x для добавления и удаления элементов.
После импорта ваша база данных должна выглядеть так:
Подтвердите зависимость базы данных Firebase
В блоке зависимостей файла Podfile
убедитесь, что Firebase/Database
включен.
Подфайл
pod 'Firebase/Database'
Синхронизировать существующие сообщения
Добавьте код, который синхронизирует недавно добавленные сообщения в пользовательский интерфейс приложения.
Код, который вы добавляете в этот раздел, будет:
- Инициализируйте базу данных Firebase и добавьте слушателя для обработки изменений, внесенных в базу данных.
- Обновите
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
}
Тестовая синхронизация сообщений
- Щелкните значок
Кнопка запуска .
- Нажмите кнопку « Войти, чтобы начать» , чтобы перейти к окну сообщений.
- Добавляйте новые сообщения прямо в консоли Firebase, щелкнув зеленый символ + рядом с записью «messages» и добавив объект, подобный следующему:
- Убедитесь, что они отображаются в интерфейсе дружественного чата.
Реализовать Отправить сообщение
Отправьте значения в базу данных. Когда вы используете метод push для добавления данных в базу данных Firebase Realtime, будет добавлен автоматический идентификатор. Эти автоматически сгенерированные идентификаторы являются последовательными, что гарантирует добавление новых сообщений в правильном порядке.
Измените метод 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)
}
Тестовая отправка сообщений
- Щелкните значок
Кнопка запуска .
- Нажмите « Войти», чтобы перейти в окно сообщений.
- Введите сообщение и нажмите "Отправить". Новое сообщение должно быть видно в пользовательском интерфейсе приложения и в консоли Firebase.
Подтвердите зависимость хранилища Firebase
В блоке зависимостей Podfile
убедитесь, что Firebase/Storage
включен.
Подфайл
pod 'Firebase/Storage'
Активировать Firebase Storage на панели управления
Перейдите в консоль Firebase и убедитесь, что Хранилище активировано с доменом gs: //PROJECTID.appspot.com.
Если вместо этого вы видите окно активации, нажмите «НАЧАТЬ», чтобы активировать его с правилами по умолчанию.
Настроить 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
}
Реализовать сохранение и отправку изображений
Загрузите изображение от пользователя, затем синхронизируйте 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])
}
}
}
Тестовая отправка и получение графических сообщений
- Щелкните значок
Кнопка запуска .
- Нажмите « Войти», чтобы перейти в окно сообщений.
- Щелкните значок «добавить фото», чтобы выбрать фотографию. Новое сообщение с фотографией должно быть видно в пользовательском интерфейсе приложения и в консоли Firebase.
Вы использовали Firebase, чтобы легко создать приложение для чата в реальном времени.
Что мы покрыли
- База данных в реальном времени
- Федеративный вход
- Место хранения