概要
複数のファイルのアップロードを以下のように扱えるようにStorageUploaderを作りました。どれくらいアップロードされているのか監視もできます。
sample.swift
let uploader = StorageUploader()
// アップロードしたいファイルを追加
uploader.addFile(file:URL.init("video"), childRef:videoChildRef, metadata:videoMetadata)
uploader.addData(data:imageData, childRef:imageChildRef, metadata:imageMetadata)
// アップロード状況の監視
uploader.progressHandler = {completedCount, totalCount in
print("\(100 * completedCount / totalCount)%")
}
// アップロード完了時の処理
uploader.completeHandler = { metadatas in
print("complete")
}
// アップロード失敗時の処理
uploader.failureHandler = { error in
// switch (StorageErrorCode(rawValue: error.code)!) { ...
print("fail")
}
// アップロード開始
uploader.start()
StorageUploaderのコード
StorageUploader.swift
import Foundation
import FirebaseStorage
class StorageUploader {
fileprivate struct SUComponent {
var file:URL?
var data:Data?
var childRef:StorageReference?
var metadata:StorageMetadata?
var isComplete = false
}
var progressHandler: (_ completedUnitCount: Int64, _ totalUnitCount: Int64) -> Void = {_, _ in }
var completeHandler: (_ metaDatas: [StorageMetadata]) -> Void = {_ in }
var failureHandler: (_ error: NSError?) -> Void = {_ in }
fileprivate var components = [SUComponent]()
fileprivate var uploadTasks = [StorageUploadTask]()
fileprivate var totalUnitCount:Int64 = 1
fileprivate var completedUnitCount:Int64 = 0
fileprivate var isfailure = false
func addFile(file:URL, childRef:StorageReference, metadata:StorageMetadata) {
var uploadInfo = SUComponent()
uploadInfo.file = file
uploadInfo.childRef = childRef
uploadInfo.metadata = metadata
components.append(uploadInfo)
}
func addData(data:Data, childRef:StorageReference, metadata:StorageMetadata) {
var uploadInfo = SUComponent()
uploadInfo.data = data
uploadInfo.childRef = childRef
uploadInfo.metadata = metadata
components.append(uploadInfo)
}
func start() {
prepareUpload()
progressHandler(completedUnitCount, totalUnitCount)
}
fileprivate func prepareUpload() {
components.forEach { uploadInfo in
let childRef = uploadInfo.childRef
let metadata = uploadInfo.metadata
if let file = uploadInfo.file {
guard let uploadTask = childRef?.putFile(from: file, metadata: metadata) else {
return
}
observeStatus(uploadTask)
} else if let data = uploadInfo.data {
guard let uploadTask = childRef?.putData(data, metadata: metadata) else {
return
}
observeStatus(uploadTask)
}
}
}
fileprivate func observeStatus(_ uploadTask: StorageUploadTask) {
uploadTasks.append(uploadTask)
uploadTask.observe(.progress) { snapshot in
var currentCompletedUnitCount:Int64 = 0
var currentTotalUnitCount:Int64 = 0
self.uploadTasks.forEach { uploadTask in
currentCompletedUnitCount += (uploadTask.snapshot.progress?.completedUnitCount ?? 0)
currentTotalUnitCount += (uploadTask.snapshot.progress?.totalUnitCount ?? 0)
}
self.completedUnitCount = currentCompletedUnitCount
self.totalUnitCount = currentTotalUnitCount
self.progressHandler(currentCompletedUnitCount, currentTotalUnitCount)
}
uploadTask.observe(.success) { snapshot in
if self.completedUnitCount == self.totalUnitCount {
let metaDatas = self.uploadTasks.map { uploadTask -> StorageMetadata in
return uploadTask.snapshot.metadata ?? StorageMetadata()
}
self.completeHandler(metaDatas)
}
}
uploadTask.observe(.failure) { snapshot in
if !self.isfailure {
self.isfailure = true
self.uploadTasks.forEach { uploadTask in
if uploadTask.snapshot.progress?.isCancellable ?? false {
uploadTask.cancel()
}
}
let error = snapshot.error as NSError?
self.failureHandler(error)
}
}
}
}
エラーハンドリング
エラーから以下の情報がわかる。
sample.swift
switch (StorageErrorCode(rawValue: error.code)!) {
case .unknown:
// An unknown error occurred
break
case .objectNotFound:
// No object exists at the desired reference
break
case .bucketNotFound:
// No bucket is configured for Firebase Storage
break
case .projectNotFound:
// No project is configured for Firebase Storage
break
case .quotaExceeded :
// Quota on your Firebase Storage bucket has been exceeded
// If you're on the free tier, upgrade to a paid plan
// If you're on a paid plan, reach out to Firebase support
break
case .unauthenticated:
// User is unauthenticated. Authenticate and try again
break
case .unauthorized:
// User is not authorized to perform the desired action
// Check your rules to ensure they are correct
break
case .retryLimitExceeded :
// The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded
// Try uploading again
break
case .nonMatchingChecksum :
// File on the client does not match the checksum of the file received by the server
// Try uploading again
break
case .downloadSizeExceeded :
// Size of the downloaded file exceeds the amount of memory allocated for the download
// Increase memory cap and try downloading again
break
case .cancelled:
// User cancelled the operation
break
case .invalidArgument :
// An invalid argument was provided
break
/* ... */
default:
// A separate error occurred. This is a good place to retry the upload.
break
}