Dietmar Schwarz Webers Dietmar Schwarz Webers - 5 months ago 46
Objective-C Question

Firebase download image from Storage directly

Started to use Firebase and so far I like it. But now I am topping - trying to download images from Storage. But I don't want to download the images in background, I want to download them directly - hope I am clear.

So far I have tested:

for (NSInteger iLoop=0; iLoop<aFriendsKey.count; iLoop++) {
NSDictionary *dicFriend = [dicFriends objectForKey:[aFriendsKey objectAtIndex:iLoop]];
FIRStorage *storage = [FIRStorage storage];
FIRStorageReference *storageRef = [storage referenceForURL:[dicFriend objectForKey:@"avatarURL"]];
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
[storageRef dataWithMaxSize:5 * 1024 * 1024 completion:^(NSData *data, NSError *error){
if (error != nil) {
// Uh-oh, an error occurred!
} else {
[aFriendsAvatar addObject:data];
}
}];
}
[_tvFriends reloadData];


and it works fine, but the images are not downloaded in time, that means they are not available when I reload the UITableView. As you see from before code, I am trying to load all images in a NSMutableArray and use this NSMutableArray to show the images in the UITableView.

But because of the delay between background download and reloadData, never any image is shown.

Any idea or solution?

Answer

It's not a good idea to block the main thread while loading the images or the app will become entirely unresponsive to the user. Instead, you simply want to be notified when all of the downloads have completed and then reload the table view. This can be done with a dispatch_group_t from Grand Central Dispatch (GCD).

dispatch_group_t group = dispatch_group_create();

for (NSInteger iLoop=0; iLoop<aFriendsKey.count; iLoop++) {
    NSDictionary *dicFriend = [dicFriends objectForKey:[aFriendsKey objectAtIndex:iLoop]];
    FIRStorage *storage = [FIRStorage storage];
    FIRStorageReference *storageRef = [storage referenceForURL:[dicFriend objectForKey:@"avatarURL"]];
    // Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
    dispatch_group_enter(group);
    [storageRef dataWithMaxSize:5 * 1024 * 1024 completion:^(NSData *data, NSError *error){
        if (error != nil) {
            // Uh-oh, an error occurred!
        } else {
            [aFriendsAvatar addObject:data]
        }
        dispatch_group_leave(group);
    }];
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    [_tvFriends reloadData];
});

You can think of a dispatch group as a simple counter of outstanding operations. Before we start each download, we call dispatch_group_enter on the group to increment it's count. When each download finishes, we call dispatch_group_leave to decrement the count. We register a listener block with dispatch_group_notify so that when the count reaches 0, our block is called and we know all operations have finished, and it is safe to reload the table view. Also, this approach will not block the main thread, meaning the user can still interact with the UI while the downloads are happening.

Comments