1. Обзор

Добро пожаловать на практический семинар по Friendly Chat. В этом семинаре вы научитесь использовать платформу 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. Создайте стартовое приложение.

Для создания стартового приложения:
- В окне терминала перейдите по следующему пути:
Каталог ios-starter/swift-starterиз вашего примера кода загружен - Выполните команду
pod install --repo-update - Откройте файл FriendlyChatSwift.xcworkspace, чтобы запустить проект в Xcode.
- Нажмите
Кнопка «Запуск» .
Через несколько секунд должен появиться главный экран Friendly Chat. Должен отобразиться пользовательский интерфейс. Однако на данном этапе вы не можете войти в систему, отправлять или получать сообщения. Приложение завершит работу с исключением, пока вы не выполните следующий шаг.
4. Создайте проект Firebase.
Создайте новый проект Firebase.
- Войдите в консоль Firebase, используя свою учетную запись Google.
- Нажмите кнопку, чтобы создать новый проект, а затем введите название проекта (например,
FriendlyChat). - Нажмите «Продолжить» .
- Если появится запрос, ознакомьтесь с условиями использования Firebase и примите их, после чего нажмите «Продолжить» .
- (Необязательно) Включите помощь ИИ в консоли Firebase (в Firebase она называется "Gemini").
- Для этого практического занятия вам не понадобится Google Analytics, поэтому отключите эту опцию.
- Нажмите «Создать проект» , дождитесь завершения подготовки проекта, а затем нажмите «Продолжить» .
Обновите свой тарифный план Firebase.
Для использования Cloud Storage for Firebase ваш проект Firebase должен быть подключен к тарифному плану с оплатой по мере использования (Blaze) , то есть он должен быть связан с учетной записью Cloud Billing .
- Для использования учетной записи Cloud Billing требуется способ оплаты, например, кредитная карта.
- Если вы новичок в Firebase и Google Cloud, проверьте, имеете ли вы право на получение кредита в размере 300 долларов США и бесплатной пробной версии учетной записи Cloud Billing .
- Если вы выполняете этот практический семинар в рамках мероприятия, уточните у организатора, есть ли возможность получить облачные кредиты.
Чтобы перейти на тарифный план Blaze для вашего проекта, выполните следующие шаги:
- В консоли Firebase выберите вариант обновления вашего тарифного плана .
- Выберите тарифный план Blaze. Следуйте инструкциям на экране, чтобы связать учетную запись Cloud Billing с вашим проектом.
Если в рамках этого обновления вам потребовалось создать учетную запись Cloud Billing, возможно, вам нужно будет вернуться к процессу обновления в консоли Firebase, чтобы завершить обновление.
Подключите ваше iOS-приложение
- На экране «Обзор проекта» вашего нового проекта нажмите «Добавить Firebase в ваше iOS-приложение» .
- Введите идентификатор пакета в формате "
com.google.firebase.codelab.FriendlyChatSwift". - Введите идентификатор приложения в App Store как "
123456". - Нажмите «Зарегистрировать приложение» .
Добавьте файл GoogleService-Info.plist в ваше приложение.
На втором экране нажмите «Загрузить GoogleService-Info.plist» , чтобы скачать файл конфигурации, содержащий все необходимые метаданные Firebase для вашего приложения. Скопируйте этот файл в ваше приложение и добавьте его в целевой объект FriendlyChatSwift .
Теперь вы можете нажать на крестик в правом верхнем углу всплывающего окна, чтобы закрыть его, пропустив шаги 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
}
5. Идентификация пользователей
Используйте правила для ограничения доступа только для авторизованных пользователей.
Теперь добавим правило, требующее аутентификации перед чтением или записью любых сообщений. Для этого добавим следующие правила в наш объект данных сообщений. В разделе «База данных» консоли Firebase выберите «База данных реального времени», затем перейдите на вкладку «Правила». Затем обновите правила так, чтобы они выглядели следующим образом:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Для получения более подробной информации о том, как это работает (включая документацию по переменной "auth"), см. документацию по безопасности Firebase.
Настройка API аутентификации
Прежде чем ваше приложение сможет получить доступ к API аутентификации Firebase от имени ваших пользователей, вам необходимо его включить.
- Перейдите в консоль Firebase и выберите свой проект.
- Выберите аутентификацию
- Выберите вкладку «Способ входа» .
- Переключите переключатель Google в положение «Включено» (синий).
- Нажмите кнопку «Сохранить» в появившемся диалоговом окне.
Если в дальнейшем в этом практическом задании возникнут ошибки с сообщением "CONFIGURATION_NOT_FOUND", вернитесь к этому шагу и перепроверьте свою работу.
Подтвердите зависимость Firebase Auth.
Убедитесь, что зависимости Firebase Auth присутствуют в файле Podfile .
Podfile
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».
- Если все прошло успешно, вас перенаправят на экран обмена сообщениями.
6. Активируйте базу данных в режиме реального времени.

Импорт сообщений
В консоли Firebase в вашем проекте выберите пункт «База данных» на левой панели навигации. В меню «База данных» выберите «Импорт JSON» . Найдите файл initial_messages.json в каталоге friendlychat, выберите его и нажмите кнопку «Импорт» . Это заменит все данные, которые в данный момент находятся в вашей базе данных. Вы также можете редактировать базу данных напрямую, используя зеленые значки «+» и красный крестик для добавления и удаления элементов.

После импорта ваша база данных должна выглядеть примерно так:

Подтвердите зависимость базы данных Firebase.
В блоке зависимостей файла Podfile убедитесь, что в него включена Firebase/Database .
Podfile
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, нажав на зелёный значок «+» рядом с записью «Сообщения» и добавив объект, подобный следующему:

- Убедитесь, что они отображаются в пользовательском интерфейсе дружественного чата.
7. Отправка сообщений
Реализовать отправку сообщений
При добавлении данных в базу данных Firebase Realtime Database методом push автоматически присваивается идентификатор. Эти автоматически сгенерированные идентификаторы являются последовательными, что гарантирует добавление новых сообщений в правильном порядке.
Измените метод `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.
8. Хранение и получение изображений
Подтвердите зависимость от хранилища Firebase.
В блоке зависимостей файла Podfile убедитесь, что Firebase/Storage включен в список зависимостей.
Podfile
pod 'Firebase/Storage'
Настройка облачного хранилища для Firebase
Вот как настроить Cloud Storage для Firebase в вашем проекте Firebase:
- В левой панели консоли Firebase разверните раздел «Сборка» , а затем выберите «Хранилище» .
- Нажмите « Начать» .
- Выберите местоположение для вашего хранилища по умолчанию.
Для регионовUS-WEST1,US-CENTRAL1иUS-EAST1доступен тариф "Всегда бесплатно" от Google Cloud Storage. Для регионов во всех остальных регионах действуют тарифные планы и правила использования Google Cloud Storage . - Нажмите «Пуск» в тестовом режиме . Ознакомьтесь с отказом от ответственности в отношении правил безопасности.
В дальнейшем в этом практическом занятии вы добавите правила безопасности для защиты ваших данных. Не распространяйте и не предоставляйте публичный доступ к приложению без добавления правил безопасности для вашего хранилища. - Нажмите «Создать» .
Настройка 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. Отправка изображений в сообщениях
Реализуйте функции хранения и отправки изображений.
Загрузите изображение, предоставленное пользователем, затем синхронизируйте 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.
10. Поздравляем!
Вы использовали Firebase для простого создания приложения для чата в реальном времени.
Что мы рассмотрели
- База данных в реальном времени
- Федеративный вход
- Хранилище
