Начинаем работать с хранилищем Firebase для iOS
Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)
Введение
Помимо того, что разработчики iOS могут легко хранить данные в облаке, а также аутентифицировать пользователей через их надежные SDK, Firebase также предоставляет удобное решение для хранения данных для мультимедиа. Firebase Storage позволяет разработчикам сохранять и получать аудио, изображения и видео файлы в облаке. То есть Firebase Storage предоставляет набор SDK, чтобы дать разработчикам возможность управлять своими пользовательскими ресурсами контента вместе со своим непосредственным продуктом - базой данных Firebase Realtime, которая хранит текстовое содержимое пользователя.
Тем не менее, Firebase Storage больше, чем просто контейнер для хранения мультимедийных активов. Он помогает разработчикам, предлагая автономную синхронизацию для пользователей и их устройств, очереди и возобновления изображений и видео, когда пользователь уходит и возвращается в Интернет. Это работает аналогично тому, как Firebase Realtime Database организует синхронизацию пользовательских данных с фоновым контентом.



Эта статья - продолжение нашего предыдущего руководства по началу работы с Firebase Authentication для iOS, где мы рассмотрели, как управлять, хранить и работать с пользователями в Firebase.
Цели этого учебника
Это руководство представит вам SDK Firebase Storage, чтобы помочь вам управлять медиа-активами вашего приложения, такими как изображения, аудио- и видеофайлы, хранить их удаленно в облаке и получать их во всем приложении. В этом уроке вы узнаете, как:
- настроить приложение для хранилища Firebase
- создавать и работать с ссылками на хранилище
- загрузить медиа-ресурсы в хранилище Firebase
- выгрузить медиа-ресурсы из хранилища Firebase
Предполагаемые знания
В этом руководстве предполагается, что у вас было некоторое знакомство с Firebase, а также опыт разработки Swift и Xcode. Также важно, чтобы вы сначала ознакомились со статьей начало работы с Firebase Authentication для iOS, так как вам нужно будет аутентифицировать своих пользователей до доступа к большинству функциональных возможностей Firebase Storage, включая пути к ресурсам.
Что такое хранилище Firebase?
Как разработчик, вы можете использовать базу данных Firebase Realtime для доступа и взаимодействия с бакетом хранилища Firebase без сервера без необходимости создавать и размещать свои собственные серверы. Firebase Storage использует локальное кэширование на устройстве для хранения ресурсов в автономном режиме и обслуживания ресурсов, когда пользователь возвращается в сеть, при этом локальные данные автоматически синхронизируются.
Разработчикам больше не приходится иметь дело со сложностями синхронизации данных и контента через стандартные сетевые библиотеки Apple iOS и иметь дело с несколькими сценариями, которые могут вызвать перебои в передаче.
Фактически, продукты Firebase признают, что мобильные пользователи в реальном мире сталкиваются с перспективой прерывания или ситуации с низким уровнем сигнала. Возможность синхронизации данных на устройстве для последующей передачи обеспечивает гораздо лучший пользовательский интерфейс, одновременно экономя разработчикам кучу работы.
Безопасность также имеет первостепенное значение с Firebase Storage, так же как и с остальной частью набора продуктов Firebase. Это означает, что разработчики могут ограничить доступ к элементам хранения путем аутентификации пользователей с использованием Firebase Authentication, которая построена поверх императивной модели безопасности, которая позволяет контролировать доступ к путям, файлам и метаданным на основе ролей.
Наконец, приложения, размещенные на Firebase Storage, получают выгоду от инфраструктуры Google, которая масштабируется по мере роста абонентской базы. Мы рассмотрим некоторые из этих концепций позже в учебнике, но для начала давайте займемся настройкой вашего приложения для работы с Firebase. Затем мы рассмотрим указатели на хранилище.
Настройка проекта
Если вы раньше работали с Firebase, многое из этого должно быть вам знакомо. В противном случае вам нужно будет создать учетную запись в Firebase и следовать инструкциям раздела «Настройка проекта» статьи «Начало работы с Firebase Authentication для iOS».
Вы можете загрузить полный исходный код для этого проекта, указав в терминале следующее:
1 |
$ git clone git@github.com:tutsplus/get-started-with-firebase-storage-for-ios.git |
Для Storage нам нужно добавить Firebase/Storage в наш Podfile, как показано ниже:
1 |
pod 'Firebase/Core' |
2 |
pod 'Firebase/Storage' |
Сохраните, а затем введите в своем терминале следующее:
1 |
pod install |
В AppDelegate application:didFinishLaunchingWithOptions: методе существует следующая строка:
1 |
FirebaseApp.configure() |
Убедитесь, что вы также настроили свой проект через Firebase Console правильно, как описано в разделе «Настроить проект» в разделе «Начало работы с Firebase Authentication для iOS».
Как только ваша среда будет готова, мы можем перейти к рассмотрению ссылок на хранилище, и начнем с того, как создать ссылочный указатель.
Создание и работа со ссылками на хранилище
Используя Firebase Storage, вы можете взаимодействовать с собственной облачной корзиной, которая представляет собой файловую систему ваших сохраненных изображений и видео. Вы используете так называемую ссылку на хранилище для определенного пути или файла в пути, в файловой системе, которую вы затем предоставляете вашему приложению, чтобы вы могли взаимодействовать с ним, передавая данные.
Наличие указателя на путь или файл в пути позволяет загружать, скачивать, обновлять или удалять этот путь. Чтобы создать ссылку, вы просто создаете экземпляр Storage.storage(), как показано ниже:
1 |
let store = Storage.storage() |
2 |
let storeRef = store.reference() |
Теперь у вас есть ссылка на корень вашей иерархии файловой системы, и вы можете настроить структуру своей корзины, как вы хотите, например, создав структуру папок.
Чтобы получить доступ к файлам и путям в вашей корзине, вызовите метод child() следующим образом:
1 |
let userProfilesRef = storeRef.child("images/profiles")
|
2 |
... |
3 |
let logoRef = storeRef.child("images/logo.png")
|
Ссылки являются сокращением для полного пути Firebase к вашему файлу с помощью вашей корзины вместо того, чтобы вводить весь URL-адрес пути Firebase. Помимо метода child(), вы также можете перемещаться по своей иерархии с помощью методов root() и parent(), и вы можете связать эти методы, как вы увидите ниже:
1 |
let userProfilesRef = logoRef.parent()?.child("profiles")
|
Как вы можете видеть, мы получим те же результаты для userProfilesRef, что и в предыдущем блоке кода. Самое замечательное в ссылках - это то, что они очень легкие, поэтому вы можете иметь столько ссылок в своем приложении, сколько захотите, при этом не влияя на производительность вашего приложения.
Теперь, когда вы понимаете основные аспекты работы с ссылками на Firebase Storage, давайте перейдем к загрузке и скачиванию файлов из вашей корзины.
Загрузка данных в хранилище Firebase
Самый простой способ загрузить файл - передать в NSData представление его содержимого в памяти:
1 |
let uploadUserProfileTask = userProfilesRef.child("\(userID).png").putData(data, metadata: nil) { (metadata, error) in
|
2 |
guard let metadata = metadata else {
|
3 |
print("Error occurred: \(error)")
|
4 |
return |
5 |
} |
6 |
print("download url for profile is \(metadata.downloadURL)")
|
7 |
} |
Вы можете управлять своими загрузками в процессе, контролируя то, когда начинать, приостанавливать, возобновлять и отменять ваши загрузки. Вы также можете прослушивать последующие события, которые запускаются, а именно:
- pause
- resume
- cancel
Ссылаясь на ранее загруженную uploadUserProfileTask, вы можете управлять своими загрузками, используя следующие методы:
1 |
uploadUserProfileTask.pause() |
2 |
uploadUserProfileTask.resume() |
3 |
uploadUserProfileTask.cancel() |
Вы также можете отслеживать прогресс загрузки, просто установив наблюдателя в объект экземпляра задачи:
1 |
let progressObserver = uploadUserProfileTask.observe(.progress) { snapshot in
|
2 |
let percentComplete = 100.0 * Double(snapshot.progress!.completedUnitCount) |
3 |
/ Double(snapshot.progress!.totalUnitCount) |
4 |
print(percentComplete) |
5 |
} |
Посмотрим, каким образом можно скачать видео и изображения из корзины хранилища.
Скачивание данных из хранилища Firebase
Чтобы иметь возможность скачивать и представлять свои изображения, вы начинаете работу с загрузкой и объявляете ссылочный указатель на указанный вами путь. Затем начните скачивание с использованием функции dataWithMaxSize:completion: :
1 |
logoRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
|
2 |
if let error = error {
|
3 |
print("Error \(error)")
|
4 |
} else {
|
5 |
let logoImage = UIImage(data: data!) |
6 |
} |
7 |
} |
Если вы используете FirebaseUI, вы можете просто им управлять загрузкой, и тогда кэширование и отображение изображений для вас будет еще более простым:
1 |
... |
2 |
self.imageView.sd_setImage(with: logoRef, placeholderImage: placeholderImage) |
Информацию об использовании FirebaseUI см. в документации FirebaseUI.
Управление скачиваниями работает так же, как управление и контроль над загрузками. Вот пример:
1 |
let downloadTask = storageRef.child("images/logo.jpg").write(toFile: localFile)
|
2 |
|
3 |
// Pause the download |
4 |
downloadTask.pause() |
5 |
|
6 |
// Resume the download |
7 |
downloadTask.resume() |
8 |
|
9 |
// Cancel the download |
10 |
downloadTask.cancel() |
Вы также можете назначить наблюдателя, как мы это делали для загрузки, отслеживать ход процесса загрузки в режиме реального времени:
1 |
let progressObserverDownload = downloadTask.observe(.progress) { snapshot in
|
2 |
let percentComplete = 100.0 * Double(snapshot.progress!.completedUnitCount) |
3 |
/ Double(snapshot.progress!.totalUnitCount) |
4 |
print(percentComplete) |
5 |
} |
Вооружившись видинием того, как работать со ссылками и как загружать и скачивать ресурсы из вашей корзины, теперь вы можете взглянуть на то, как реализовать Firebase Storage для нашего примерного проекта: FirebaseDo.
Пример проекта FirebaseDo
К настоящему моменту вы должны клонировать приложение FirebaseDo, поэтому двигайтесь дальше, создайте и запустите проект. Вы увидите, что все, что он делает, - это аутентифицирует пользователей, используя либо телефон, либо электронную почту:



Наша цель - постепенно улучшать функциональность приложения, так что, как только наши пользователи будут успешно проходить аутентификацию, они смогут загрузить фотографию профиля. Большая часть нашей работы будет в HomeViewController и его Associated Storyboard. Сначала обратимся к файлу HomeViewController.
HomeViewController
Прежде чем перейти к методам этого контроллера, нам нужно добавить в наш класс протокол UIImagePickerControllerDelegate, чтобы мы могли работать с его методами делегирования. Нам также нужно добавить экземпляр сборщика, чтобы наши пользователи могли выбирать фотографию из своей библиотеки.
1 |
class HomeViewController: UIViewController, FUIAuthDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
2 |
|
3 |
@IBOutlet weak var myImageView: UIImageView! |
4 |
let picker = UIImagePickerController() |
5 |
|
6 |
... |
7 |
fileprivate(set) var auth:Auth? |
8 |
fileprivate(set) var authUI: FUIAuth? //only set internally but get externally |
9 |
fileprivate(set) var authStateListenerHandle: AuthStateDidChangeListenerHandle? |
Добавьте к концу метода viewDidLoad() следующее:
1 |
self.picker.delegate = self |
2 |
self.refreshProfileImage() |
Мы собираемся реализовать метод refreshProfileImage(), который будет вызываться для загрузки изображения, которое мы отобразили в нашем ViewController. Сначала мы проверим, что пользователь действительно аутентифицирован до создания ссылки, которая будет извлекать изображение профиля пользователя из пути /images/user_id/profile_photo.jpg в нашей корзине. Наконец, мы обновим наше изображение с полученным изображением.
1 |
func refreshProfileImage(){
|
2 |
if let user = Auth.auth().currentUser{
|
3 |
let store = Storage.storage() |
4 |
let storeRef = store.reference().child("images/\(user.uid)/profile_photo.jpg")
|
5 |
|
6 |
storeRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
|
7 |
if let error = error {
|
8 |
print("error: \(error.localizedDescription)")
|
9 |
} else {
|
10 |
let image = UIImage(data: data!) |
11 |
self.myImageView.image = image |
12 |
} |
13 |
} |
14 |
}else{
|
15 |
print("You should be logged in")
|
16 |
self.loginAction(sender: self) |
17 |
return |
18 |
} |
19 |
|
20 |
} |
Затем мы создаем метод @IBAction для кнопки библиотеки фотографий, с которой мы вскоре подключимся из нашего сториборда:
1 |
@IBAction func libraryAction(_ sender: Any) {
|
2 |
self.picker.allowsEditing = false |
3 |
self.picker.sourceType = .photoLibrary |
4 |
self.picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)! |
5 |
self.present(picker, animated: true, completion: {
|
6 |
print("handle saving")
|
7 |
}) |
8 |
} |
Наконец, мы добавляем два метода делегата для нашего UIImagePickerController, чтобы обрабатывать, когда пользователь отменяет UIImagePicker, а также обрабатывает выбранное изображение:
1 |
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
2 |
dismiss(animated: true, completion: nil) |
3 |
} |
4 |
|
5 |
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
|
6 |
self.dismiss(animated: true, completion: nil) |
7 |
|
8 |
let profileImageFromPicker = info[UIImagePickerControllerOriginalImage] as! UIImage |
9 |
|
10 |
let metadata = StorageMetadata() |
11 |
metadata.contentType = "image/jpeg" |
12 |
|
13 |
let imageData: Data = UIImageJPEGRepresentation(profileImageFromPicker, 0.5)! |
14 |
|
15 |
let store = Storage.storage() |
16 |
let user = Auth.auth().currentUser |
17 |
if let user = user{
|
18 |
let storeRef = store.reference().child("images/\(user.uid)/profile_photo.jpg")
|
19 |
ASProgressHud.showHUDAddedTo(self.view, animated: true, type: .default) |
20 |
let _ = storeRef.putData(imageData, metadata: metadata) { (metadata, error) in
|
21 |
ASProgressHud.hideHUDForView(self.view, animated: true) |
22 |
guard let _ = metadata else {
|
23 |
print("error occurred: \(error.debugDescription)")
|
24 |
return |
25 |
} |
26 |
|
27 |
self.myImageView.image = profileImageFromPicker |
28 |
} |
29 |
|
30 |
} |
31 |
|
32 |
} |
Как только пользователь выбирает изображение, мы убираем пикер, но сохраняем ссылку на выбранное изображение. Затем мы создаем экземпляр StorageMetadata(), чтобы сообщить Firebase, что мы загрузим файл JPEG.
Как и в методе refreshProfileImage(), мы будем проверять, что пользователь аутентифицирован, а затем создадим ссылку на путь с изображениями, где мы хотим сохранить профиль нашего пользователя. Используя метод putData(), мы затем асинхронно загружаем наше изображение в указанное место в корзине, прежде чем устанавливать вью на выбранное изображение.
Прежде чем мы сможем создать и запустить наше приложение, нам нужно будет добавить соответствующие элементы управления в наш сториборд.
Сториборд
В наш основной сториборд добавьте изображение с образцом-заполнителем, которое будет представлять текущий профиль пользователя, а затем перетащите, чтобы связать представление изображения с тем, которое мы объявили как @IBOutlet в нашем классе HomeViewController. Затем добавьте панель инструментов с помощью кнопки, которую вы будете использовать в качестве @IBAction для вызова метода libraryAction(), который мы создали ранее в HomeViewController.
Ваш сториборд должен теперь напоминать следующее:



При отсутствии каких-либо ошибок вы можете продолжить и снова собрать и запустить приложение, а также выполнить аутентификацию либо путем создания нового пользователя, либо с помощью набора существующих учетных данных пользователя.
Затем вам будет представлен HomeViewController, где вы выберете кнопку +, чтобы добавить изображение с вашего устройства или библиотеки фотографий симулятора. После того, как вы выбрали фотографию, она будет загружена в корзину Firebase. Вы можете удостоверится, что она успешно загружена, перейдя на вкладку «Storage» вашей Firebase Console, как показано ниже:



Если вы остановите и запустите приложение в Xcode, вы также увидите изображение, которое вы последний раз загрузили, и подтвердите, что мы успешно загрузили и загрузили с помощью Firebase Storage.
Заключение
В этом учебном пособии показано, как легко добавлять асинхронное хранение и управление ресурсами в существующее приложение Firebase всего несколькими строками кода. Это дает вам удобный способ управления ресурсами вашего приложения, позволяя вам отрегулировать автономную синхронизацию элегантно и удобно.
Firebase Storage является очевидным выбором для разработчиков iOS, которые уже находятся в экосистеме Firebase. Оно предоставляет разработчикам безопасность императивной модели безопасности, предоставляемой Firebase Authentication, а также возможности, предоставляемые базой данных Firebase Realtime.
Пока вы здесь, ознакомьтесь с некоторыми другими нашими статьями по разработке приложений для iOS!









