nklhtv
nklhtv

Reputation: 118

What is the best way to create offline first application with Firebase Storage and Firebase Firestore? #AskFirebase

First of all i am new to Firebase :)

I couldn't find a tutorial or example that shows how to create an offline-first mobile app with Firebase Storage and Firebase Firestore. I use Firestore to persist data about items: id, name, image. I use Storage to persist images and link those images with items from Firestore. When the phone is connected to internet everything is great, but i am not sure what is the best way to handle adding an item while device is offline.

The only good solution i could think of:

  1. Set item's image prop to a local file url so image can be shown in my recycler view.
  2. Start upload item and image simultaneously.
  3. Persist image sessionUri and the local file path so the task can be resumed later.
  4. Detect connectivity change and resume image tasks if some.
  5. When the image upload is completed get the download url and update item in firestore. Then clear the persisted image task.

I was really expecting from Firebase client library to be able to handle such a trivial use case out of the box.

MCVE:

public void addImageOnClick(final View view) {
    final Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(intent, 123);
}

@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    if (requestCode == 123) {
        save("id1", "name1", intent.getData());
    }
}

private void save(final String id, final String name, final Uri imageUri) {
    final Map<String, Object> item = new HashMap<>();
    item.put("id", id);
    item.put("name", name);
    FirebaseStorage.getInstance()
            .getReference("items")
            .child(id)
            .putFile(imageUri)
            .continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
                @Override
                public Task<Uri> then(final Task<UploadTask.TaskSnapshot> task) throws Exception {
                    return task.getResult()
                            .getMetadata()
                            .getReference()
                            .getDownloadUrl();
                }
            })
            .continueWith(new Continuation<Uri, Void>() {
                @Override
                public Void then(final Task<Uri> task) throws Exception {
                    item.put("image", task.getResult().toString());
                    FirebaseFirestore.getInstance()
                            .collection("items")
                            .document(id)
                            .set(item);
                    return null;
                }
            })
            .continueWith(new Continuation<Void, Void>() {
                @Override
                public Void then(final Task<Void> task) throws Exception {
                    Toast.makeText(MainActivity.this, String.valueOf(task.isSuccessful()), Toast.LENGTH_LONG).show();
                    return null;
                }
            });
}

Upvotes: 7

Views: 2090

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317467

This is actually not really a "trivial case" that can be solved with a single API.

You're dealing with two different products here - Cloud Storage and Firestore. Each has their own, fully independent APIs and methods of storing data. They area designed to be used individually, or together, if you choose. Since you've chosen to use them together (which is great!), you will still have to write code to deal with them independently, while also gluing them together using some scheme that you come up with. It looks like you know what you're doing. It doesn't get simpler.

Upvotes: 2

Related Questions