Topic 6 Posts

programming

Saving array of custom objects to SharedPreferences on Android

On iOS when you store an object in UserDefaults, you just put it there via setValueForKey, and it doesn't matter if you put the class there on its own, or an array of your custom classes. What matters is when you get it (them) back via objectForKey or arrayForKey, you cast the object(s) into a correct type/array of types.

On Android the principle is similar with the primitives (ints, strings, booleans, etc.) but it gets a bit tricky from an iOS-dev's perspective to store an array of objects in SharedPreferences (Android's analog for iOS's UserDefaults), but there is a solution.

First, we save your array (list) of objects:

// Needed libraries
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;

// Accessing SharedPreferences
SharedPreferences sp = getSharedPreferences("MyPreferences", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();

// Saving accounts
public void saveAccounts(List<Account> accounts) {
    Gson gson = new Gson();
    String accountsJson = gson.toJson(accounts);
    editor.putString(r.getString("SavedAccounts"), accountsJson);
    editor.apply();
}

As you can see, even though we pass an array of objects, we then convert and store them actually as a string. Nothing unusual here, except storing an array of objects as a string 🙂

Where it gets interesting is when we need to get the objects back from SharedPreferences:

public List<Account> getAccounts() {
    Gson gson = new Gson();
    Type type = new TypeToken<List<Account>>(){}.getType();
    String accountsString = sharedPref.getString("SavedAccounts", "");
    return gson.fromJson(accountsString, type);
}

Here we get our array of objects as a string and convert it back to our list of accounts via Gson's built-in functionality.

Universal data+notification FCM payload for iOS+Android

In case you're following my FCM payload saga, here's a happy ending that covers most usecases with one payload which will allow you sending basic FCM messages with data and notification in them. And such messages are universal and will produce the same result on both iOS and Android. On iOS this will work out of the box, whereas on Android a small hack is neccessary to be added to the app itself.

With the payload below, you'll be able send a unified FCM message to iOS and Android and on both platforms you will receive a notifcation pop-up/heads-up message as well as pass some data to the callback method of the app which then may use it to do something meaningful in the background:

{  
   "message":{  
      "token":"<device registration id>",
      "apns":{
         "payload":{
            "aps":{
               "content-available":1,
               "alert":{
                  "title":"title",
                  "subtitle":"subtitle",
                  "body":"body"
               },
               "badge":7,
               "sound":"default"
            }
         }
      },
      "data":{
         "account":{
            "first-name":"Igor",
            "last-name":"Z"
         },
         "androidTitle":"title",
         "androidBody":"body"
      }
   }
}

iOS will use the apns part to display the notification, and pass data=>account to your app for further use. And essentially ignore the androidTitle and androidBody part.

Android on the other hand will ignore the whole apns branch, pass data=>account to the app as well and with the small hack mentioned above use androidTitle and androidBody to display a local notification.

That's a lot of power in a single payload. I hope you find it helpful 🙂

Combining data+notification payload in FCM Android

If you send this payload to an iOS device via FCM, and also invoke UNUserNotificationCenter's didReceive response: withCompletionHandler callback method, iOS will both display a text notification as well as allow you app some background time to deal with the data object you're sending along your push notification.

Unfortunately on Android even using my recommended payload you won't achieve this behavior. If you send both notification and data, only the notification will be shown, and the OnMessageReceived callback method won't be called to handle the data object.

But if you're in charge of the FCM payload creation, you can put the notification's title and body into the data object like this:

{  
   "message":{  
      "token":"<device registration id>",
      "data":{
         "account":{
            "first-name":"Igor",
            "last-name":"Z"
         },
         "androidTitle":"title",
         "androidBody":"body"
      }
   }
}

And when OnMessageReceived is called with the contents of this payload, you just create a local heads-up notification based on the info in androidTitle and androidBody with this code:

This code is a bit verbose, but is guaranteed to be working on all current versions of Android, which is invaluable 🙂 You just put androidTitle and androidBody as parameters of the method above and a nice default nofitication will appear on each message.

Now you can enjoy notification+data push notifications in a single message on Android with this hack, instead of sending two messages each time.

Firebase Android notification payload

Unlike iOS, for Android Firebase's FCM server accepts payload in slightly different form:

{  
   "message":{  
      "token":"<device registration id>",
      "android":{  
         "notification":{  
            "title":"title",
            "sound":"default",
            "body":"body"
         },
         "priority":"high"
      },
      "data":{
         "account":{
            "first-name":"Igor",
            "last-name":"Z"
         }
      }
   }
}

But there's a caveat: when both the android and data objects are present - only the notification is shown, without data being passed to OnMessageReceived FCM callback method. So in order to send both data and notification at the same time on Android you either have to send two messages - one data and one notification. Or to be a bit more creative 🙂

Firebase iOS push payload

If you're using Firebase and its FCM (Firebase Cloud Messaging) server directly to send out notifications to iOS users, you might wonder what kind of payload you should send to Firebase in order for the information to be delivered.

So wonder no more 🙂

{
   "message":{
      "token":"<device registration id>",
      "apns":{
         "payload":{
            "aps":{
               "content-available":1,
               "alert":{
                  "title":"title",
                  "subtitle":"subtitle",
                  "body":"body"
               },
               "badge":7,
               "sound":"default"
            }
         }
      },
      "data":{
         "account":{
            "first-name":"Igor",
            "last-name":"Z"
         }
      }
   }
}

In case you need, here's an example of the payload for Android

UITableView automatic cell height change with animation

UITableView-cell-resize

Today I had to update cell's height in UITableView while typing into a UITextView in that cell. After going through several approaches without any luck I stumbled on this interesting solution.
Objective-c:

[tableView beginUpdates];
[tableView endUpdates];

Swift:

tableView.beginUpdates()
tableView.endUpdates()

You call these two methods whenever you need to update the cell's height, whether you use tableView heightForRowAtIndexPath (tableView heightForRowAt)
with predefined cell height or AutoLayout with automatic

estimatedRowHeight,
rowHeight

In both cases UITableView will reload only the cell which height needs to be updated and not the whole table. And also as a nice bonus it will do that with a smooth built-in animation.

Even after few radars to include this behavior into their official documentation Apple didn't add this side-effect to the method's description, despite actually recommending such use in their WWDC 2010 'Mastering Table View' session 😊

So if you want to just change your the height of the rows in your table view, it's an easy way to do it.
You can do that in conjunction with a change of the rows in your table view.
You can also actually just do a simple Empty Update Block.
You can call it beginUpdates immediately followed by endUpdates with no actual changes to your table view.
We'll go through all the same steps here.