Skip to content

Instantly share code, notes, and snippets.

@gilbox
Last active April 5, 2021 05:23
Show Gist options
  • Save gilbox/1a19e1e18ffcaa70e7d6b0fe0e837e10 to your computer and use it in GitHub Desktop.
Save gilbox/1a19e1e18ffcaa70e7d6b0fe0e837e10 to your computer and use it in GitHub Desktop.
// Created by gil_birman on 11/22/19.
import Combine
import FirebaseFirestore
import FirebaseStorage
import Foundation
enum FirebaseCombineError: Error {
case encodeImageFailed
case nilResultError
case uploadFailed
}
extension StorageReference {
/// Retrieves a download URL for this object.
/// Returns a publisher that you can use to track the progress.
var downloadURLPublisher: AnyPublisher<URL, Error> {
Future<URL, Error> { [weak self] promise in
self?.downloadURL { (url, error) in
if let error = error {
promise(.failure(error))
} else if let url = url {
promise(.success(url))
} else {
promise(.failure(FirebaseCombineError.nilResultError))
}
}
}.eraseToAnyPublisher()
}
/// Uploads data.
/// Returns a publisher that you can use to track the progress.
func putDataPublisher(_ uploadData: Data, metadata: StorageMetadata)
-> AnyPublisher<StorageMetadata, Error>
{
return Future<StorageMetadata, Error> { [weak self] promise in
self?.putData(uploadData, metadata: metadata) { (metadata, error) in
if let error = error {
promise(.failure(error))
return
}
guard let metadata = metadata else {
promise(.failure(FirebaseCombineError.uploadFailed))
return
}
promise(.success(metadata))
}
}.eraseToAnyPublisher()
}
/// Uploads an image with the specified file type
/// Returns a publisher that you can use to track the progress.
func uploadImagePublisher(
_ image: CGImage,
fileType: NSBitmapImageRep.FileType = .jpeg)
-> AnyPublisher<StorageMetadata, Error>
{
let nsImage = NSImage(cgImage: image, size: .zero)
guard
let tiffData = nsImage.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffData),
let data = bitmap.representation(using: fileType, properties: [:])
else {
return Fail(error: FirebaseCombineError.encodeImageFailed).eraseToAnyPublisher()
}
let metadata = StorageMetadata()
switch fileType {
case .jpeg, .jpeg2000:
metadata.contentType = "image/jpeg"
case .png:
metadata.contentType = "image/png"
case .gif:
metadata.contentType = "image/gif"
case .bmp:
metadata.contentType = "image/bmp"
case .tiff:
metadata.contentType = "image/tiff"
@unknown default:
metadata.contentType = "image/unknown"
}
return putDataPublisher(data, metadata: metadata)
}
/// Uploads an image with the specified name and extension based on the file type.
/// Returns a publisher that you can use to track the progress.
func uploadImageWithFilenamePublisher(
_ image: CGImage,
fileName: String,
fileType: NSBitmapImageRep.FileType = .jpeg)
-> AnyPublisher<(storageReference: StorageReference, metadata: StorageMetadata), Error>
{
let ext: String
switch fileType {
case .jpeg, .jpeg2000:
ext = "jpg"
case .png:
ext = "png"
case .gif:
ext = "gif"
case .bmp:
ext = "bmp"
case .tiff:
ext = "tiff"
@unknown default:
ext = "image"
}
let fileName = "\(fileName).\(ext)"
let storageReference = child(fileName)
return storageReference.uploadImagePublisher(image)
.map { metadata in (storageReference, metadata) }
.eraseToAnyPublisher()
}
/// Uploads an image and creates a file named with the UUID() function
/// and the specified extension based on the file type
/// Returns a publisher that you can use to track the progress.
func uploadImageWithRandomFilenamePublisher(
_ image: CGImage,
fileType: NSBitmapImageRep.FileType = .jpeg)
-> AnyPublisher<(storageReference: StorageReference, metadata: StorageMetadata), Error>
{
return uploadImageWithFilenamePublisher(
image,
fileName: UUID().uuidString,
fileType: fileType)
}
}
extension DocumentReference {
func snapshotPublisher(includeMetadataChanges: Bool = false) -> AnyPublisher<DocumentSnapshot, Error> {
return Future<DocumentSnapshot, Error> { [weak self] promise in
self?.addSnapshotListener(includeMetadataChanges: includeMetadataChanges) { snapshot, error in
if let error = error {
promise(.failure(error))
} else if let snapshot = snapshot {
promise(.success(snapshot))
} else {
promise(.failure(FirebaseCombineError.nilResultError))
}
}
}.eraseToAnyPublisher()
}
}
@kshivang
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment