Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 23 additions & 25 deletions FirebaseStorageUI/Sources/FUIStorageImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -126,33 +126,31 @@ - (BOOL)canRequestImageForURL:(NSURL *)url {
}];
// Observe the progress changes
[download observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot * _Nonnull snapshot) {
// Check progressive decoding if need
// Progressive decoding requires access to partial data via the internal GTMSessionFetcher,
// which was removed in Firebase Storage 9.0.0. Guard to prevent crash; progressive rendering
// is silently skipped on Firebase 9+.
if (options & SDWebImageProgressiveLoad) {
FIRStorageDownloadTask *task = (FIRStorageDownloadTask *)snapshot.task;
// Currently, FIRStorageDownloadTask does not have the API to grab partial data
// But since FirebaseUI and Firebase are seamless component, we access the internal fetcher here
GTMSessionFetcher *fetcher = task.fetcher;
// Get the partial image data
NSData *partialData = [fetcher.downloadedData copy];
// Get response
int64_t expectedSize = fetcher.response.expectedContentLength;
expectedSize = expectedSize > 0 ? expectedSize : 0;
int64_t receivedSize = fetcher.downloadedLength;
if (expectedSize != 0) {
// Get the finish status
BOOL finished = receivedSize >= expectedSize;
// This progress block may be called on main queue or global queue (depends configuration), always dispatched on coder queue
if (coderQueue.operationCount == 0) {
[coderQueue addOperationWithBlock:^{
UIImage *image = SDImageLoaderDecodeProgressiveImageData(partialData, url, finished, task, options, context);
if (image) {
dispatch_main_async_safe(^{
if (completedBlock) {
completedBlock(image, partialData, nil, NO);
}
});
}
}];
if ([task respondsToSelector:@selector(fetcher)]) {
GTMSessionFetcher *fetcher = [task performSelector:@selector(fetcher)];
NSData *partialData = [fetcher.downloadedData copy];
int64_t expectedSize = fetcher.response.expectedContentLength;
expectedSize = expectedSize > 0 ? expectedSize : 0;
int64_t receivedSize = fetcher.downloadedLength;
if (expectedSize != 0) {
BOOL finished = receivedSize >= expectedSize;
if (coderQueue.operationCount == 0) {
[coderQueue addOperationWithBlock:^{
UIImage *image = SDImageLoaderDecodeProgressiveImageData(partialData, url, finished, task, options, context);
if (image) {
dispatch_main_async_safe(^{
if (completedBlock) {
completedBlock(image, partialData, nil, NO);
}
});
}
}];
}
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions FirebaseStorageUI/SwiftBridge/FirebaseStorageUISwift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import UIKit
import SDWebImage
import FirebaseStorage

@_exported import FirebaseStorageUI

extension UIImageView {

public func sd_setImageWithStorageReference(
_ storageRef: StorageReference,
maxImageSize size: UInt64? = nil,
placeholderImage placeholder: UIImage? = nil,
options: SDWebImageOptions = [],
context: [SDWebImageContextOption: Any]? = nil,
completion: ((UIImage?, Error?, SDImageCacheType, StorageReference) -> Void)? = nil
) {
sd_setImageWithStorageReference(
storageRef,
maxImageSize: size,
placeholderImage: placeholder,
options: options,
context: context,
progress: nil,
completion: completion
)
}

public func sd_setImageWithStorageReference(
_ storageRef: StorageReference,
maxImageSize size: UInt64? = nil,
placeholderImage placeholder: UIImage? = nil,
options: SDWebImageOptions = [],
context: [SDWebImageContextOption: Any]? = nil,
progress progressBlock: ((Int, Int, StorageReference) -> Void)?,
completion: ((UIImage?, Error?, SDImageCacheType, StorageReference) -> Void)? = nil
) {
guard let url = Self.storageURL(for: storageRef) else { return }

var ctx = context ?? [:]
ctx[.imageLoader] = StorageImageLoader.shared
ctx[.fuiStorageMaxImageSize] = size ?? StorageImageLoader.shared.defaultMaxImageSize

let sdProgress: SDImageLoaderProgressBlock? = progressBlock.map { block in
{ received, expected, _ in block(Int(received), Int(expected), storageRef) }
}
let sdCompletion: SDExternalCompletionBlock? = completion.map { block in
{ image, error, cacheType, _ in block(image, error, cacheType, storageRef) }
}

sd_setImage(
with: url,
placeholderImage: placeholder,
options: options,
context: ctx,
progress: sdProgress,
completed: sdCompletion
)
}

private static func storageURL(for storageRef: StorageReference) -> URL? {
guard !storageRef.bucket.isEmpty else { return nil }
var components = URLComponents()
components.scheme = "gs"
components.host = storageRef.bucket
components.path = "/" + storageRef.fullPath
return components.url
}
Comment thread
demolaf marked this conversation as resolved.
}
13 changes: 13 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ let package = Package(
name: "FirebaseStorageUI",
targets: ["FirebaseStorageUI"]
),
.library(
name: "FirebaseStorageUISwift",
targets: ["FirebaseStorageUISwift"]
),
.library(
name: "FirebaseAuthSwiftUI",
targets: ["FirebaseAuthSwiftUI"]
Expand Down Expand Up @@ -126,6 +130,15 @@ let package = Package(
.headerSearchPath("../../"),
]
),
.target(
name: "FirebaseStorageUISwift",
dependencies: [
"FirebaseStorageUI",
.product(name: "FirebaseStorage", package: "firebase-ios-sdk"),
.product(name: "SDWebImage", package: "SDWebImage"),
],
path: "FirebaseStorageUI/SwiftBridge"
),
.target(
name: "FirebaseAuthUIComponents",
dependencies: [],
Expand Down
Loading