Tuesday, 6 May 2014

Providing More Items to Share [Networking, the Social Kind]

Now if you want to get what the CMColorView object draws on the screen as an image, you simply fetch its image property. Use this in the -share: method. Select the CMColorViewController.m file and change the first fewstatements with the following code (changes in bold):

-  (IBAction)share:(id)sender
{
NSString   *shareMessage = [NSString stringWithFormat:
@"I wrote  an  iOS app  to share  color!"
@"  RGB=%@"
@"  @LearniOSAppDev",
[self.colorModel rgbCodeWithPrefix:nil]];
UIImage *shareImage  = self.colorView.image;
NSURL  *shareURL  = [NSURL  URLWithString:@"http://www.learniosappdev.com/"];
NSArray *itemsToShare = @[shareMessage,shareImage,shareURL];

Run the app again. This time, you’re passing three items (a string, an image, and a URL) to the
UIActivityViewController. Notice how this changes the interface, as shown in Figure 13-6.


Figure 13-6. Activities with more sharable items

Each activity responds to different kinds of data. Now that you include an image object, activities like Save Image and Assign to Contact appear. Each activity is also free to do what it thinks makes sense for the types of datayou provide. The Mail activity will attach images and documents to a message, Facebook will upload image to the users photo album, while Twitter may upload the picture to a photo-sharing service, and then include the link to that image in the tweet. Its completely automatic.

Excluding Activities

iOSs built-in activities are smart, but they arent prescient; they dont know what the intent of your data is. Activities know when they can do something with a particular type of data, but not if they should. If there are activitiesthat you, as a developer, dont think are appropriate for your particular blend of data, you can explicitly exclude them.

You’ve decided that printing a color example or assigning it to a contact dont make any sense.
(You assume the user has no contacts for Little Red Riding Hood, The Scarlet Pimpernel, The Green Hornet, or other colorful characters.) Return to the -share: method in CMColorViewController.m. Immediately after the statementthat creates the activityViewController, add this statement:

activityViewController.excludedActivityTypes = @[UIActivityTypeAssignToContact, UIActivityTypePrint];

Setting this property excludes the listed built-in activities from the choices. Run the app again. This time, the excluded activities are, well, excluded (see Figure 13-7).


Figure 13-7. Activities with some excluded

The Curse of the Lowest Common Denominator

The activity view controller is a fantastic iOS feature, and its likely to get better with time. About the only negative thing you can say about it is that its too easy to use. Its biggest problem is that theres no obvious way ofcustomizing that data items based on what the user wants to do with it.
Case in point: When I was developing the app for the chapter, I initially added a simple -rgbCode method to the CMColor class that returned the HTML code for the color (#f16c14).The problem with this is Twitter. On Twitter, so-called “hash tags” start with a pound/hash sign and are used to signal keywords in tweets. My color (#f16c14) would be interpreted as an f16c14 tag, which doesnt make any sense.
To avoid this, I rewrote the method so I couldobtain the RGB value with, or without, the hash and 
purposely left it out from the message passed to UIActivityViewController. That way, if the user decided to 
share with Twitter, it wouldnt tweet a confusing message.
 But thats just the tip of the iceberg. Message length for mail and Facebook can be considerably longer than those on Twitter. Why should your text message or Facebook post be limited to 140 characters?


Providing Activity Specific Data

As it happens, the iOS engineers did not ignore this problem. There are several ways of customizing your content based on the type of activity the user chooses. The two tools iOS provides are:

n     UIActivityItemSource

n     UIActivityItemProvider

The first is a protocol, which your class adopts. Any object that conforms to the UIActivityItemSource protocol can be passed in the array of data items to share. The UIActivityViewController will then send your object these two (required) messages:

-  (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
-  (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController

The first method is responsible for converting the content of your object into the actual data you want to share, or act upon. Whats significant about this message is that it includes the activity the user chose in theactivityType parameter. Use this parameter to alter your content based on what the user is doing with it.

For ColorModel, you’re going to turn your CMViewController object into a sharing message proxy object. Select your CMViewController.h file. Adopt the UIActivityItemSource protocol in your CMViewController class (changes in bold): @interface CMViewController  : UIViewController  <UIActivityItemSource>

Switch to CMViewController.m. Add the first of UIActivityItemSources two required methods:

-  (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
CMColor *color = self.colorModel; NSString   *message  = nil;
if ([activityType isEqualToString:UIActivityTypePostToTwitter] || [activityType  isEqualToString:UIActivityTypePostToWeibo])
{
message  = [NSString stringWithFormat:
@"Today's  color is RGB=%@."



@"I wrote  an  iOS app  to do  this!"
@"@LearniOSAppDev",
[color rgbCodeWithPrefix:nil]];
}
else if ([activityType isEqualToString:UIActivityTypeMail])
{
message  = [NSString stringWithFormat:
@"Hello,\n\n"
@"I wrote  an  awesome iOS app  that lets me  share"
@"a color with  my  friends.\n\n"
@"Here's  my  color (see  attachment):  hue=%.0f\u00b0,"
@"saturation=%.0f%%,   "
@"brightness=%.0f%%.\n\n"
@"If  you  like it, use  the   code  %@   in your  design.\n\n"
@"Enjoy,\n\n", color.hue, color.saturation, color.brightness,
[color  rgbCodeWithPrefix:@"#"]];
else
{
message  = [NSString stringWithFormat:
@"I wrote   great iOS app  to share this  color: %@", [color  rgbCodeWithPrefix:@"#"]];
}

return  message;
}

This method performs the conversion from your object to the actual data object that the activity view controller is going to share or use. For this app, your controller will provide the message (NSString object) to post.

Your method examines the activityType parameter and compares it against one of the known activities. (If you provided your own custom activity, the value would be the name you gave your activity.) For Twitter and Weibo, itprepares a short announcement, avoiding inadvertently creating any hash tags, and including a Twitter-style mention. If the user chooses to send an e-mail, you prepare a rather lengthy message, without a mention. ForFacebook, SMS, and any other activity, you create a medium-length message that doesnt worry about hash tags.

Find the -share: method and change the beginning of it so it looks like this (removing shareMessage
and adding the new code in bold):

-  (IBAction)share:(id)sender
{
UIImage *shareImage = self.colorView.image;
NSURL  *shareURL = [NSURL  URLWithString:@"http://www.learniosappdev.com/"]; NSArray *itemsToShare = @[self,shareImage,shareURL];

Instead of preparing a message before the activity view controller is presented, you now pass your
CMViewController object with a promise to provide the message. Once the user has decided what
they want to do (print, Tweet, Message, and so on), your view controller will receive a -activityView Controller:itemForActivityType: message and produces the data.


Promises, Promises

You may have noticed the “chicken and egg” problem here. What activities are available is determined by the kinds of data you pass to the activity view controller. But with UIActivityItemSource, the data isnt produced until theuser chooses an activity. So how does the activity view controller know what kind of activities to offer if it doesnt yet know what kind of data your method plans to produce?

The answer is the second required UIActivityItemSource method, and you need to add that now:

-  (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return @"My  color message  goes  here.";
}

This method returns a placeholder object. While it could be the actual data you plan to share, it doesnt have to be. Its only requirement is that it be the same class of the object that -activityView Controller:itemForActivityType: willreturn in the future. Since your -activityViewController:item ForActivityType: returns an NSString, all this method has to do is return any NSString object.
Run the app again and try out different activities, as shown in Figure 13-8.


Figure 13-8. Activity customized content

Big Data

The alternative technique for providing activity data is to create a custom subclass of UIActivityItemProvider. This class, which already conforms to the UIActivityItemSource protocol, produces your apps data object in thebackground.
When the activity view controller wants your apps data, it sets the activityType property of your provider object and then requests its item property. Your subclass must override the -item method to provide thedesired data, referring to activityType as needed.

UIActivityItemProvider is intended for large or complex data thats time-consuming to create, such as a video or a PDF document. It receives the -item message on a secondary execution thread—not on your apps main thread,which is the thread all of your code in this book has executed on so
far. This allows your provider object to work in the background, preparing the data, while your app continues to run. It also requires an understanding of multi-tasking and thread-safe operations, topics that I visit in Chapter 24.

In short, if the data you need to share isnt particularly large, complicated, or time consuming to construct, or you’re just not comfortable with multi-tasking yet, stick with adopting UIActivityItemSource.


Sharing with Specific Services

I’d like to round off this topic with some notes on other sharing services in iOS, and which ones to use.

The UIActivityViewControlleclass is relatively new, anlargely replaces several older APIs. If you search the iOS documentation for classes that will send e-mail, text messages (SMS), or Tweets, you’re likely to findMFMailComposeViewController, MFMessageComposeViewController, anTWTweetComposeViewController. Each of these view controllers presents an interfacethat lets the usecompose and send an e-mail message, a short text message, or a Tweet, respectively.
The latter two dont offer any significant advantages over UIActivityViewControlleor
SLComposeViewController (which I’ll explain in a moment),and their use in new apps is not recommended.

The MFMailComposeViewController still has a trick or two to offer. Its biggest talent is its ability to create an HTML formatted mail message and/or pre-address the message by filling in the To, CC, and BCC fields.
This allowsyou to create pre-addressed, richly formatted e-mail, with embedded CSS styling, animation, links, and other HTML goodies.

If you want to present your user with an interface to post to a specific social service—rather than asking them to choose—use the SLComposeViewController  class.
You create an SLComposeViewController  object for a specificservice (Twitter, Facebook, or Sina Weibo) using the+composeViewControllerForServiceType: message.
You then configure that view controller with the data you want to share, as you did with UIActivityViewController, and present the view controller to the user. The user edits their message and away it goes.

Other Social Network Interactions

In ColorModel, we’ve only explored the sharing side of social networking. If you want your app to get information from your users social networks, thats another matter altogether. Other types of interactions, like gettingcontact information about a users Facebook friends, are handled by the SLRequest class.

An SLRequest works very similarly to the way an NSURLRequest works. You used NSURLRequest objects in Chapter 3 to send a request to the X.co URL shortening service. To use a social networking system, you prepare anSLRequest object in much the same manner, providing the URL of the
service, the method (POST or GET), and any required parameters. You send the request, providing a
code block that will process the response.

The biggest difference between SLRequest and NSURLRequest is the account property. This property stores an ACAccount object that describes a users account on a specific social networking service. This property allowsSLRequest to handle all of the authentication and encryption required to communicate your request to the servers. If you’ve ever written any OAuth handling code, you’ll appreciate how much work SLRequest is doing for you.

To use other social networking features you must, therefore, prepare the following:

n     Service Type

n     Service URL

n     Request method (POST, GET, DELETE)

n     Request parameters dictionary

n     The users ACAccount object

The service type is one of SLServiceTypeFacebook SLServiceTypeSinaWeibo SLServiceTencentWeibo, or SLServiceTypeTwitter. The URL, method, and parameters dictionary are dictated by whatever
kind of request you’re making. For those details, consult the developer documentation for the specific service. Some places to start reading are listed in Table 13-1.

Table 13-1. Social Services Developer Documentation

Social Service         URL

Facebook               https://developers.facebook.com/docs/

Sina Weibo             http://open.weibo.com/wiki/

Tencent Weibo       http://dev.t.qq.com/

       Twitter                                            https://dev.twitter.com/docs


Finally, you’ll need the ACAccount object for the users account. Account and login information is maintained by iOS for your app, so your app only needs to request it. Whether the user wants to authorize your app to usetheir account, or they need to sign in, its all handled for you.

The basic steps to obtaining an account object are:

1.       Create an instance of the ACAccountStore object.

2.       Send the account store an -accountTypeWithAccountTypeIdentifier:
message to get an ACAccountType object for the service you’re interested
in. An ACAccountType object is your key to the users accounts on a specific service.

3.       Finally, you send the account store a -requestAccessToAccountsWithType: message. If successful (and allowed) your app will receive an array of ACAccount objects for that user.

Services like Facebook allow an iOS user to be logged into only one account at a time. Twitter, on the other hand, permits a user to be connected to multiple accounts simultaneouslyYour app will have to decide if it wantsto use all of the account objects, selected ones, or just one. Once you have an ACAccount object, use it to set the account property of the SLRequest, and you’re ready to get social!


Summary

You’ve learned how to add yet another nifty feature to your app, allowing your users to connect and share content with friends and family around the world—and it only took a smattering of code to get it working. You learnedhow to tailor that content for specific services, or exclude services.
If you want more control over which services your app provides, you learned how to use the SLComposeViewController  to create a specific sharing interface, along with the SLRequest class that provides a conduit for unlimitedsocial networking integration.

During your journey, you also gained some practical experience in drawing into an off-screen graphics context, and using Xcodes refactoring tool. The refactoring command contains a powerful set of code maintenance tools. Ifyou plan to rename or relocate a method or property, you should make friends with the refactoring tools and other global editing commands. To read more about them, search for “Make Projectwide Changes” in XcodesDocumentation and API Reference window.

Sharing posts with your friends and colleagues isnt the only way iOS apps communicate. In Chapter 3 you wrote an app that uses an Internet URL shortening service. In the next chapter, you’re going to write an app that talks toanother iOS device directly, via Wi-Fi or Bluetooth.

No comments:

Post a Comment