Sharing and local notifications

This is small tutorial on iOS 6 new feature – UIActivityViewController. This controller allows sharing and some other services to be performed on content of your application. This controller can show system provided services (like Twitter, or Copy To Pasteboard) and your custom actions which are relevant for your application.
We will be extending our previous tutorial app – KeyboardKeys.
Activity View Controller
So, here are the things we’ll add to our application:

  1. Ability to share text using system Twitter/Facebook/Weibo accounts.
  2. Ability to create a local notification which will show entered text after a minute.


We’re going to use UIActivityViewController to give user a choice. This controller shows actions that could be performed on content. Let’s start with presenting this controller. And then we’ll see how to add custom action.

// prepeare item for sharing
NSArray *items = @[ self.textField.text,
                    [NSURL URLWithString:@"http://easyplace.wordress.com"],
                    [UIImage imageNamed:@"KeyboardKeysPad@2x.png"] ];

// prepare sharing controller
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:items
                                                                  applicationActivities:nil ];
avc.excludedActivityTypes = @[ UIActivityTypeAssignToContact, 
                               UIActivityTypePrint, 
                               UIActivityTypeSaveToCameraRoll ];

avc.completionHandler = ^(NSString *activityType, BOOL completed) {
    if (completed)
    {
        NSLog(@"shared to %@", activityType);
    }
    else
    {
        NSLog(@"not shared");
    }
};

// present sharing options to user
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
    [self.popover dismissPopoverAnimated:YES];
    self.popover = [[UIPopoverController alloc] initWithContentViewController:avc];
    [self.popover presentPopoverFromBarButtonItem:sender
                         permittedArrowDirections:UIPopoverArrowDirectionAny
                                         animated:YES];
    self.popover.passthroughViews = nil;
    self.popover.delegate = self;
}
else
{
    [self presentViewController:avc animated:YES completion:NULL];
}

Here in this code we first set up content we’re going to share (or perform actions on). This would be contents of our text field, link to this blog and image from our application.

Then we set up UIActivityViewController. Note, we do not specify application activities yet, we’ll come back to this later.

excludedActivityTypes property removes some of standard actions from the list. In our case, we exclude adding content to contacts, printing and saving to Camera Roll. All other activities are enabled.

completionHandler property replesents a block which will be launched when user is done with activities. Parameters show if user did anything or just cancelled.

Then – we’re presenting controller. Presenting depends on device application is running on. For iPhone/iPod Touch we’re using modal presentation, and for iPad we’re using UIPopoverController (we’re setting its passthroughViews property to nil to allow taps on navigation bar dismiss the popover).

This code will show user list of standard actions to be performed on the items selected. Apple documentation has a lot of information on types of content which is acceptable by standard activities.

Now let’s add our own custom activity – create a local notification. This will bring notification banner after one minute of waiting. For banner to be displayed, user should leave the application.

First, we define UIActivity subclass. We have

- (NSString *)activityType
{
    return @"com.demo.Notification";
}

- (NSString *)activityTitle
{
    return NSLocalizedString(@"Notification", @"Notification activity title");
}

- (UIImage *)activityImage
{
    return [UIImage imageNamed:(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? @"NotificationActivityPad" :
                                                                                       @"NotificationActivityPhone")];
}

First, we need to set up activity metadata – type, title and image to be presented in UIActivityViewController. Image is used similar to tabs – only alpha channel is used. Images should be no larger than 43×43 for iPhone (86×86 for Retina display) and no larger than 55×55 for iPad (again, 110×110 for Retina).

Now, let’s write code to prepare items for activity and actually, perform it.

- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems
{
    for (id item in activityItems)
    {
        if ([item isKindOfClass:[NSString class]])
        {
            return YES;
        }
        else if ([item isKindOfClass:[NSAttributedString class]])
        {
            return YES;
        }
    }
    return NO;
}

- (void)prepareWithActivityItems:(NSArray *)activityItems
{
    NSMutableArray *textItems = [NSMutableArray array];
    for (id item in activityItems)
    {
        if ([item isKindOfClass:[NSString class]])
        {
            [textItems addObject:item];
        }
        else if ([item isKindOfClass:[NSAttributedString class]])
        {
            [textItems addObject:[item string]];
        }
    }
    self.item = [textItems componentsJoinedByString:@" "];
}

- (void)performActivity
{
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    
    notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
    notification.alertBody = self.item;
    notification.soundName = UILocalNotificationDefaultSoundName;
    notification.hasAction = YES;
    notification.alertAction = NSLocalizedString(@"View", @"View notification button");
    
    [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    
    self.item = nil;
    [self activityDidFinish:YES];
}

We’ve implemented -performActivity method, which is good when activity is not interactive (like, copying data to pasteboard). We also could implement -activityViewController, which will be presented to perform activity (using this controller you can ask user for additional information, or preview action before confirming it).

When you’ve done with activity, you should call -activityDidFinish: on self. This will signal iOS that activity is completed and could be removed.

-canPerformWithActivityItems: method is called before displaying UIActivityViewController. If you return NO, this custom activity will not be displayed.

-prepareWithActivityItems: method is called before -performActivity or displaying of -activityViewController. Using this method you could prepare data for actions. Our example constructs a single NSString from all items passed to activity.

Let’s now focus on setting up local notification. Basically, it just UILocalNotification object being created and scheduled by -scheduleLocalNotification: method on [UIApplication sharedApplication]. You could also add numeric badge to application icon to signal that there are some unread messages. Not much more to add here.
Local Notification
When notification fires while application is not active, banner or alert will be shown to user (if he didn’t turn off notifications of your app). Also notification will be in system notification center. User could respond to notification (by tapping on banner, or View button in alert, or by tapping notification in notification center), or forget about it. So, application should take care of it.

Application delegate should have these methods implemented.

-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    // actually, here we should transfer user to approriate place of application
    [[[UIAlertView alloc] initWithTitle:@"Notification"
                                message:notification.alertBody
                               delegate:nil
                      cancelButtonTitle:@"Ok"
                      otherButtonTitles:nil] show];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSArray *notifications = [NSArray arrayWithArray:[UIApplication sharedApplication].scheduledLocalNotifications];
    [UIApplication sharedApplication].scheduledLocalNotifications = notifications;
}

-application:didReceiveLocalNotification: method is called if notification fires when application is active, or if user taps on notification banner, or notification in notification center. Your goal in this method would be to navigate user to relevant controller.

-applicationDidBecomeActive: method has some “magic”. Although this seems to be doing nothing, this code will “purge” all viewed local notifications from notification center. Which is kind of being nice to user.

That’s it for today. Don’t hesitate to ask questions.

And, as always, consult Apple documentation on UIActivityViewController, UIActivity, local and remote notifications.

Also, code for this tutorial is available on GitHub.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s