1. Обзор
Добро пожаловать на практическое занятие по FriendlyChat для iOS! На этом занятии вы научитесь создавать полнофункциональное iOS-приложение для чата в реальном времени, используя Firebase и современные возможности Swift, такие как SwiftUI, Swift Concurrency и фреймворк Observation.
Что вы узнаете
- Управляйте состоянием с помощью современной платформы для наблюдения (
@Observable) . - Аутентификация пользователей осуществляется с помощью Firebase Authentication .
- Синхронизируйте сообщения чата в режиме реального времени с помощью Cloud Firestore .
- Храните и предоставляйте изображения с помощью Cloud Storage for Firebase .
- Получайте доступ к API Firebase, используя современные возможности параллельного программирования Swift .
Что вам понадобится
- Последняя версия Xcode
- Симулятор iOS или физическое тестовое устройство
- Учетная запись Google для создания и управления вашим проектом Firebase.
2. Получите пример кода.
Клонируйте репозиторий GitHub из командной строки:
$ git clone https://github.com/firebase/codelab-friendlychat-ios
В репозитории codelab-friendlychat-ios содержится несколько примеров проектов:
-
ios-starter/swift-starter— стартовый проект Xcode, содержащий каркас макета SwiftUI и комментарииTODO, где вы будете реализовывать функции приложения с помощью Firebase. -
ios/swift— Завершённый проект Xcode с готовым примером приложения.
3. Создайте стартовое приложение.
Соберите и запустите стартовое приложение, чтобы ознакомиться с пользовательским интерфейсом:
- В Finder или Терминале перейдите в каталог
ios-starter/swift-starter. - Чтобы открыть проект в Xcode, дважды щелкните файл
FriendlyChatSwift.xcodeproj. - Обратите внимание, что Xcode автоматически разрешает зависимости Swift Package Manager в фоновом режиме (загружая
firebase-ios-sdkверсии 12+ с GitHub). - Выберите симулятор iOS (например, iPhone 17 Pro ) и нажмите кнопку «Запустить» (cmd+R).
Через несколько секунд появится экран входа в FriendlyChat. На этом этапе кнопки входа и лента сообщений являются лишь заполнителями. Подключение Firebase к приложению будет выполнено на следующих шагах.
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-приложение
- На экране «Обзор проекта» щелкните значок iOS , чтобы запустить процесс настройки приложения.
- Введите идентификатор пакета:
com.google.firebase.codelab.FriendlyChatSwift - Нажмите «Зарегистрировать приложение» .
Добавьте файл GoogleService-Info.plist в свой проект Xcode.
- Нажмите кнопку «Скачать GoogleService-Info.plist» , чтобы сохранить файл конфигурации.
- В Xcode перетащите загруженный файл
GoogleService-Info.plistв основную группуFriendlyChatSwiftв навигаторе проекта. - При появлении запроса убедитесь, что установлен флажок «Копировать элементы при необходимости» и выбран целевой объект
FriendlyChatSwift, затем нажмите «Готово» .
Настройте Firebase в файле FriendlyChatSwiftApp.swift
Наконец, при запуске приложения SwiftUI инициализируйте Firebase с помощью конфигурации вашего проекта. Откройте файл FriendlyChatSwiftApp.swift и замените заполнители TODO на import FirebaseCore и FirebaseApp.configure() :
import SwiftUI
import FirebaseCore
@main
struct FriendlyChatSwiftApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
5. Аутентификация пользователей
Настройте аутентификацию в консоли Firebase.
Прежде чем аутентифицировать пользователей, необходимо сначала включить выбранный вами поставщик аутентификации в консоли Firebase. В этом руководстве используется аутентификация по электронной почте и паролю:
- В консоли Firebase перейдите в раздел Безопасность > Аутентификация , затем нажмите «Начать» .
- Выберите вкладку «Способ входа» .
- Нажмите «Электронная почта/Пароль» , затем переключатель «Включить » и нажмите «Сохранить» .
Защитите Cloud Firestore с помощью правил аутентификации.
Далее, необходимо требовать от пользователей аутентификации перед чтением или записью сообщений:
- В консоли Firebase перейдите в раздел «Базы данных и хранилище» > «База данных Firestore» , затем нажмите «Создать базу данных» .
- Выберите местоположение и нажмите «Далее» .
- Выберите «Начать в тестовом режиме» и нажмите «Создать» .
- Выберите вкладку «Правила» и обновите правила безопасности следующим образом:
Эти правила позволяют любому авторизованному пользователю читать или записывать сообщения в базу данных, что подходит для учебных целей. Подробнее о правилах безопасности см. в документации по правилам безопасности .rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { allow read, write: if request.auth != null; } } } - Нажмите «Опубликовать» .
Отслеживайте состояние аутентификации в UserViewModel.swift
В SwiftUI реактивное отслеживание состояния Firebase Auth обеспечивает плавное переключение пользовательского интерфейса между экраном входа в систему и интерфейсом чата.
Откройте ViewModels/UserViewModel.swift и замените TODO в init() чтобы отслеживать изменения состояния аутентификации с помощью AsyncSequence authStateChanges :
init() {
authTask = Task {
for await user in Auth.auth().authStateChanges {
self.user = user
}
}
}
Реализуйте вход, регистрацию и выход из системы с помощью Swift Concurrency.
В UserViewModel.swift замените оставшиеся комментарии TODO на современные методы аутентификации async / await :
func signIn(email: String, password: String) async {
errorMessage = nil
if email.isEmpty || password.isEmpty {
showError("Please enter both email and password.")
return
}
do {
try await Auth.auth().signIn(withEmail: email, password: password)
} catch {
showError(error.localizedDescription)
}
}
func signUp(email: String, password: String, displayName: String) async {
errorMessage = nil
if email.isEmpty || password.isEmpty {
showError("Please enter both email and password.")
return
}
do {
let result =
try await Auth.auth().createUser(withEmail: email, password: password)
let changeRequest = result.user.createProfileChangeRequest()
changeRequest.displayName = displayName.isEmpty ? email : displayName
try await changeRequest.commitChanges()
self.user = Auth.auth().currentUser
} catch {
showError(error.localizedDescription)
}
}
func updateDisplayName(_ displayName: String) async {
guard let currentUser = Auth.auth().currentUser else { return }
do {
let changeRequest = currentUser.createProfileChangeRequest()
changeRequest.displayName = displayName
try await changeRequest.commitChanges()
self.user = Auth.auth().currentUser
} catch {
showError(error.localizedDescription)
}
}
func signOut() {
do {
try Auth.auth().signOut()
} catch {
showError(error.localizedDescription)
}
}
Проверка аутентификации пользователя
- Нажмите кнопку «Запустить» в Xcode.
- В симуляторе приложения нажмите «Регистрация» , введите отображаемое имя, адрес электронной почты и пароль, а затем нажмите «ЗАРЕГИСТРИРОВАТЬСЯ» .
- Вы должны автоматически пройти аутентификацию и перейти к пустому экрану сообщений!
6. Чтение из Cloud Firestore
Добавьте примеры сообщений в консоль Firebase.
Заполните базу данных примерами сообщений:
- В консоли Firebase перейдите в раздел Firestore Database и выберите вкладку Data .
- Нажмите « Начать сборку» .
- Введите
messagesдля идентификатора коллекции , затем нажмите «Далее» . - Оставьте параметр « Идентификатор документа» установленным на автоматическую генерацию (или нажмите «Автоматическая генерация идентификатора »).
- Добавьте в документ следующие поля:
-
text(тип:string, значение:Hello) -
displayName(type:string, value:anonymous)
-
- Нажмите « Сохранить ».
- При желании нажмите «Добавить документ» , чтобы добавить дополнительные примеры сообщений.
Синхронизация сообщений в FriendlyMessageViewModel.swift
В SwiftUI декларативное обновление пользовательского интерфейса осуществляется с помощью модели @Observable . Откройте ViewModels/FriendlyMessageViewModel.swift и реализуйте startListening() и stopListening() используя snapshots AsyncSequence :
func startListening() {
stopListening()
listenerTask = Task {
let db = Firestore.firestore()
do {
for try await snapshot in db.collection("messages").snapshots {
self.messages = snapshot.documents.compactMap { document in
try? document.data(as: FriendlyMessage.self)
}
}
} catch {
print("Error listening for messages: \(error)")
}
}
}
func stopListening() {
listenerTask?.cancel()
listenerTask = nil
messages.removeAll()
}
Как SwiftUI отображает и автоматически прокручивает сообщения
В Views/ContentView.swift обратите внимание, как ScrollViewReader и LazyVStack отображают всплывающие окна чата и автоматически прокручивают страницу до последнего сообщения при изменении значения messages.count :
ScrollViewReader { scrollViewReader in
ScrollView {
LazyVStack(spacing: 12) {
ForEach(messageViewModel.messages) { message in
FriendlyMessageView(friendlyMessage: message)
.id(message.id)
}
}
.padding(.horizontal)
.onChange(of: messageViewModel.messages.count) { _, count in
guard count > 0,
let lastId = messageViewModel.messages.last?.id else { return }
withAnimation(.easeInOut) {
scrollViewReader.scrollTo(lastId, anchor: .bottom)
}
}
}
}
Благодаря прямой привязке состояния базы данных к состоянию пользовательского интерфейса через модель представления, SwiftUI позволяет коду приложения четко сопоставлять переходы с анимациями. На следующем шаге вы запишете данные в базу данных из приложения, чтобы наблюдать за этой анимацией в действии.
7. Отправляйте сообщения
Реализуйте функцию sendMessage с использованием параллельного программирования в Swift.
При добавлении документа в коллекцию с помощью addDocument() Cloud Firestore генерирует уникальный идентификатор для каждого сообщения чата.
В ViewModels/FriendlyMessageViewModel.swift замените TODO в sendMessage(text:imageUrl:) на async throws :
func sendMessage(text: String?, imageUrl: String?) async throws {
guard let currentUser = Auth.auth().currentUser else { return }
let message = FriendlyMessage(
text: text,
displayName: currentUser.displayName ?? currentUser.email ?? "Anonymous",
imageUrl: imageUrl,
userId: currentUser.uid
)
let db = Firestore.firestore()
_ = try db.collection("messages").addDocument(from: message)
}
Проверка отправки сообщений
- Нажмите кнопку «Запустить» в Xcode.
- Войдите в свой аккаунт.
- Введите сообщение в нижнее текстовое поле и нажмите значок «Отправить».
- Сообщение мгновенно появится в вашем симуляторе!
8. Хранение и получение изображений
Настройка облачного хранилища для Firebase
- В консоли Firebase перейдите в раздел «Базы данных и хранилище» > «Хранилище» , затем нажмите «Начать» .
- Выберите «Запустить в тестовом режиме» и нажмите «Далее» .
- Примите место хранения по умолчанию и нажмите «Готово» .
Выбор изображений с помощью встроенных функций SwiftUI PhotosPicker
Современный SwiftUI (iOS 16+) обеспечивает нативный выбор фотографий с помощью PhotosPicker из import PhotosUI — никаких мостов делегатов UIKit или оберток UIImagePickerController не требуется!
В Views/FooterView.swift обратите внимание на то, как PhotosPicker привязывает выбранные изображения:
PhotosPicker(selection: $selectedItem, matching: .images) {
Image(systemName: "photo.on.rectangle.angled")
.font(.system(size: 26))
.foregroundStyle(.blue)
.accessibilityLabel("Select photo")
}
.onChange(of: selectedItem) { _, newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self) {
await uploadAndSendImage(data: data)
}
}
}
Реализовать загрузку изображений.
После выбора фотографии загрузите данные изображения в облачное хранилище и сохраните общедоступную ссылку для скачивания в Cloud Firestore.
В Views/FooterView.swift замените TODO в uploadAndSendImage(data:) :
private func uploadAndSendImage(data: Data) async {
guard let uid = Auth.auth().currentUser?.uid else { return }
isUploading = true
defer {
isUploading = false
selectedItem = nil
}
do {
let filename = "\(uid)/\(UUID().uuidString).jpg"
let storageRef =
Storage.storage().reference().child("images").child(filename)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
_ = try await storageRef.putDataAsync(data, metadata: metadata)
let downloadURL = try await storageRef.downloadURL()
try await viewModel.sendMessage(
text: nil, imageUrl: downloadURL.absoluteString)
} catch {
print("Error uploading image: \(error.localizedDescription)")
}
}
Обмен изображениями через облачное хранилище.
В Views/FriendlyMessageImageView.swift URL-адреса изображений загружаются асинхронно с использованием Swift Concurrency и Cloud Storage SDK:
- Стандартные URL-адреса для загрузки по протоколу HTTP/HTTPS извлекаются асинхронно с помощью
URLSession. - URL-адреса Google Cloud Storage (например,
gs://...) извлекаются с помощьюStorage.storage().reference(forURL:).
Тестовые сообщения с изображениями
- Нажмите кнопку «Запустить» в Xcode.
- Войдите в свой аккаунт.
- Нажмите на значок «Фото», выберите изображение из библиотеки, и оно загрузится и появится в чате!
9. Заключение
Поздравляем, вы успешно создали приложение для чата в реальном времени на iOS с использованием Swift и Firebase!
Что вы узнали
- Интеграция Swift Package Manager (SPM) с Xcode для
firebase-ios-sdk. - Декларативная компоновка SwiftUI ,
PhotosPickerи автоматическая прокруткаScrollViewReader. - Параллельное выполнение кода в Swift (
async/await) для аутентификации, Cloud Firestore и Cloud Storage. - Аутентификация по электронной почте/паролю и анонимная аутентификация с реактивным наблюдением за состоянием.
- Прослушивание моментальных снимков в реальном времени в Cloud Firestore и автоматическая запись идентификаторов.
- Облачное хранилище для загрузки бинарных файлов Firebase и публичных URL-адресов для скачивания.