Notifications on iOS 1/4: Local Notifications

Notify All The Things!

img0_notify

Notifications give us as App Developers a great opportunity to extend our App’s appearance beyond its actual frame.

In this 4-part series we will have an in-depth look on Notifications on iOS.

We can provide the user with informations that are important right now; even, if the App is currently not running.

Think about building a chat App: You want to notify the user when a new message arrived. Building a ToDo App? You surely want to send a reminder when a task is due.

iOS knows 3 kinds of Notifications:

– Local Notifications: A Notification which is triggered by the App. A Local Notification can be scheduled in advance. For example you can preset a Local Notification to fire when a user created a new ToDo Item with a due-date in your App. We will talk about Local Notifications in Part 1 and Part 2.

– Remote Notifications: A Notification which is triggered by your server. The advantage: You are completely flexible in when/what/why you fire a Notification. The drawback: You need your own server. Another drawback about standard Remote Notifications is that Apple neither guarantees you, that the Notification will be delivered nor informs you if so. To learn more about Remote Notifications check out Part 3 of this series.

– VoIP Notifications: VoIP Notifications are an important addition if you develop an App with VoIP functionality: Most important Apple guarantees you that a VoIP Notification will be delivered to the target Device. Furthermore you’ll enjoy an increased message size threshold. Last but not least your App on the device will be notified once the notification arrives. No notification will be automatically shown to the user! So you have time to do some background work and fire a local notification after you connected to the caller for example.

So, let’s start by having a look on Local Notifications!

First things first

Let’s start by creating a blank single view iOS App.

Don’t forget to give the app an unique name and Organisation Identifier since this will become relevant when we’ll take a look at remote notifications in Part 3.

Before we implement the actual notifications we have to decide what kind of notifications we want to show.

We can choose between (or combine)

– Banner: A small banner which appears on top of the screen for a few seconds, on the Lock Screen and in the Notification Center.

thumb_IMG_1232_1024

We can provide up to two additional actions for a Banner.

– Alerts: An AlertView showing outside the context of the actual Application (e.g. on the home screen). An Alert requires an action by the user. We can provide up to four Actions for an alert.

– Badge: A small, red circle on the top right corner of an App Icon, displaying the number of notifications by this App.

thumb_IMG_1233_1024

We can play an optional sound when we trigger any of the above notifications.

Coding time!

In Part 1 we will focus on Local Notifications, featuring Banner, Badges and Sound.

Before we are allowed to send out a Notification we have to ask the user for permission if we run on iOS 8.0 or above. In the “FinishedLaunching” Method of our AppDelegate we past the following:

if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes (
UIUserNotificationType.Alert
| UIUserNotificationType.Badge |
UIUserNotificationType.Sound, null
);

application.RegisterUserNotificationSettings (notificationSettings);
}

In this snippet we check if we are running on iOS 8 or higher first. After this we utilize UIUserNotificationSettings to get a NotificationSettings object. We can pass a combination of all UIUserNotificationTypes we want to use. In our example those are Alert, Badge and Sound. The first time we call RegisterUserNotificationSettings a AlertView will be shown to the user asking, if he wants to allow our App to send Notifications.

thumb_IMG_1231_1024

Now we are ready to send our first notification!

To do so, we will create a Button in our Storyboard. Name this Button “btNotify”.

In our ViewControllers ViewDidLoad() Method paste this snippet:

notify.TouchUpInside += (sender, e) =>
{
//New Notification
var notification = new UILocalNotification();

// Set FireDate
notification.FireDate = NSDate.FromTimeIntervalSinceNow(10);

// Set Message
notification.AlertAction = "Hello Badge";
notification.AlertBody = "Look! A Notification!";

// schedule it
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
};

Don’t forget to import Foundation since we are using NSDate.
In this Snippet we create a new UILocalNotification, set the FireDate, Title and Message and then fire it. It should now look like this when we click the Button:

thumb_IMG_1232_1024

That’s it! We’ve sent our first Notification! Yey!

Important note: Notifications won’t be shown if you are still inside the App. Notifications will only be displayed if your App is in the background. We will talk about this behaviour in a second.

Now we want to complement our Banner with a Badge. To do so we simply add notification.ApplicationIconBadgeNumber = 1; in our notify.TouchupInside Event.

After clicking the button now and leaving the App you should now see a Badge on your AppIcon as well as the Banner popping up.

Fell free to add a sound which will play when the Notification is fired by setting a SoundName: notification.SoundName = UILocalNotification.DefaultSoundName;.

But what happens, if the Notification is fired while the App is in the foreground? As we learned before the OS won’t display anything in this case. So we have to handle this by ourselves.

Fortunately iOS makes it easy to handle this by providing ReceivedLocalNotification which we can implement in our AppDelegate. Let’s switch back to this and add this code into your AppDelegate.cs:

public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
UIAlertController okayAlertController = UIAlertController.Create (notification.AlertAction, notification.AlertBody, UIAlertControllerStyle.Alert);
okayAlertController.AddAction (UIAlertAction.Create ("OK", UIAlertActionStyle.Default, null));
Window.RootViewController.PresentViewController (okayAlertController, true, null);

UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}

And that’s it for receiving Notifications while in the App!

We just create an AlertController, pass in the Informations from the passed UILocalNotification object and reset the App’s badge.

One more thing

As stated before you can provide a custom sound which should play when your Notification is fired. If you don’t want to stick with the default System Sound you can provide your own sound. The sound file which you have to include in your App Bundle can have a maximum length of 30 seconds. CAF and AIFF files are allowed. If you have installed XCode you can utilise afconvert to convert sound files:

afconvert -f caff -d LEI16@44100 -c 1 input.mp3 output.caf should do the job. You can then specify the custom sound by referencing it: notification.SoundName = "Sound/output.caf";

Next up

In this post you’ve learned which kinds of Notifications exist on iOS. We learned how to schedule Local Notifications and how to customise them.

In Part 2 we will see how Badge Notifications can be even more customised with custom Actions and how to handle App Launches from Notifications.

Werbeanzeigen
Notifications on iOS 1/4: Local Notifications

The ViewModel method template

ViewModels are the DDD equivalent of application level services and have two basic three basic responsibilities:

  • Start and manage domain level workflows and transactions.
  • Keep track of the state of the current view (or event the complete application).
  • Translate domain objects and states into a representation that can be displayed by the view.

Over time, we got to some standard for methods on ViewModels, that resulted in a template methods that handle „everything“, and we can then just pick the things we need.

As many Apps represent some server data, ViewModels tend to have a certain set of methods:

  • LoadData/Activate():
    Called every time, a View loads with the ViewModel as it’s datacontext. It makes sure all data is loaded correctly.
  • UpdataDataItems():
    Loads all data, that is currently available.
  • Refresh():
    Refreshes data from the web.

(Yes, these are quite similar to MvvmCross’s init(), start() and reloadState() methods.)

LoadData()

LoadData() is called when a view loads a ViewModel as it’s data context. This methods fill itself with data and (when required) starts a refresh to get the latest and greatest data from some service.

This is also the place to take on parameters that are submitted by the view loading this model.

public async Task LoadDataAsync()
{
    if (!IsDataLoaded)
        await UpdateDataItemsAsync();

    if (NetworkHelper.IsNetworkConnectionAvailable && UpdateTimerHelper.ShouldUpdate("Employees"))
        await RefreshAsync();
}

UpdateDataItems()

This is the common place to pick up all data that is already available: Locally cached items.

Loading the first data is intended to be fast. Is should be available almost at an instant, so the user has something to view and interact with, while the update operation starts in the background.

private async Task UpdateDataItemsAsync()
{
    // Get items from local cache
    var employees = (await _employeeService.GetEmployeesAsync()).OrderBy(e => e.Sorting).ToList();
    Employees = new ObservableCollection<Employee>(employees);

    IsDataLoaded = true;
}

Refresh

Refresh() is actually the first real operation of the ViewModel. Like most methods on ViewModels, it does not have a return type except Task. Usually, they are called from some event handler (like button clicks) which are void returning and therefore cannot really await anything. Updates happen via the INotifyPropertyChanged anyway.

ViewModel methods should never throw exceptions (see: [The exception barrier]). That’s why most of our ViewModel methods look like this:

public async Task RefreshAsync()
{
    // Precondition check
    if(IsDataLoading) return;
    IsDataLoading = true;

    // Preparations
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
    
    try
    {
        // Run the actual operation
        await _employeeService.RefreshAsync(cts.Token);
        await UpdateDataItemsAsync();

        UpdateTimerHelper.SetUpdate("Employees");
    }
    catch (HttpRequestException)
    {
        _notificationService.Notify(ApplicationConstants.Text_NoInternet);
    }
    catch (OperationCanceledException)
    {
        // Cancelled because of timeout
        if (cts.IsCancellationRequested)
            _notificationService.Notify(ApplicationConstants.Text_Timeout);
    }
    catch
    {
        // Just to annoy developers, until they got all their error cases checked.
        System.Diagnostics.Debugger.Break();
    }
    
    IsDataLoading = false;
}

This is an „all-in“ example:

  • Before running the methods, application level preconditions are validated: Are we updating already?
  • Preparations like task extenders, CancellationTokens, etc. are made.
  • The actual method gets executed.
  • All error cases get handled.
We usually put an extra ´Debugger.Break()´in the last catch clause. This shouldn’t activate at all, but if it does, the developer knows he has some unmatched error cases. Hopefully, it is annoying enough to bring him to handle it. :)
The ViewModel method template