Learn how to tap into the power of the Social Framework with this post, taken from Chapter 11 of the Mobiletuts+ book Decoding the iOS 6 SDK!
The introduction of the Twitter framework in iOS 5 was the first step towards the integration of iOS with popular social networks. In iOS 6, Apple introduced the Social framework. Not only does the Social framework effectively replace the Twitter framework, it also extends its functionality by adding support for both Facebook and Sina Weibo.
Even though the Twitter framework has been deprecated as of iOS 6, it is fortunately a trivial task to migrate existing code from the Twitter framework to the Social framework. The API’s of the Social framework do not make mention of specific social networks. The network component has been abstracted away, which makes adding support for new social networks in the future a painless task. Bottom line: the good news for developers is that getting up and running with the Social framework is easy, especially if you are already familiar with the Twitter framework.
In this chapter, I will give you an overview of the Social framework and what it has to offer and I will show you what your options are when you want to add a social component to your applications. While this chapter will only cover the basics of the Social framework, the next chapter will talk about a more thorough integration of your application with the Social framework. As an added bonus, I will also discuss the UIActivityViewController class. This is another addition to UIKit that makes sharing content a breeze!
Theoretical Overview
Prior to iOS 6, sharing content wasn’t all that easy. If you’ve ever integrated Facebook with an iOS application, then you almost certainly have run into a number of hurdles along the way. In iOS 6, however, a number of significant improvements have been made. As of iOS 6, integration with social networks is now baked into the operating system, which makes social sharing much easier for SDK developers.
At the time of writing, the Social framework supports integration with Facebook, Sina Weibo, and Twitter. The support for Twitter is the obvious reason for the deprecation of the Twitter framework. Sina Weibo might be the odd duck in the row for some of you. Sina Weibo is an immensely popular microblogging platform in China, one of the most populous nations on earth, so it isn’t that odd to see its integration in iOS.
Sharing Content
With the above in mind, what options do you have when one of your applications needs to integrate with Facebook, Twitter, or Sina Weibo? The Social framework offers two options, using (1) the SLComposeViewController
class or (2) the SLRequest
class. If you have worked with the Twitter framework in iOS 5 then those class names will most certainly ring a bell.
In this chapter, I will zoom in on the SLComposeViewController
class. It is an easy-to-use and elegant solution for allowing your users to share content with Twitter, Facebook, and Sina Weibo. Let’s take a look at this newcomer.
SLComposeViewController
The easiest way to share content with one of the supported social networks is by making use of the SLComposeViewController
class. Its prefix, SL, indicates that it is part of the Social framework. The SLComposeViewController
class is the equivalent of the TWTweetComposeViewController
class of the Twitter framework. The most important difference is that SLComposeViewController
is not tied to any one social network in particular.
Creating an instance of SLComposeViewController
is as easy as calling composeViewControllerForServiceType:
on the class and passing the service type, that is, the social network you are targeting. At the time of writing, there are three options, SLServiceTypeTwitter
, SLServiceTypeFacebook
, and SLServiceTypeSinaWeibo
.
You can configure the SLComposeViewController
instance by setting the initial text or adding an image or link. Configuring the SLComposeViewController
instance is an optional step since the user will be able to modify the content of the message before sending it to the target service.
Before presenting the compose view controller to the user, it is good practice to set its completion handler (SLComposeViewControllerCompletionHandler
). The latter is a block that is executed when the request to the service is finished, successfully or unsuccessfully. The block takes one parameter, the result of the request. This can be useful if you want to inform the user whether the request was successful or not.
Presenting the SLComposeViewController
to the user is done by sending it a message of presentViewController:animated:
just like you do with any other (modal) view controller. As I mentioned earlier, the user can still edit the message, geotag the message, or add an image or link. The options as well as the user interface of the SLComposeViewController
instance depend on the service that is being targeted. If the user sends a message to Facebook, for example, the modal view of the SLComposeViewController
instance is adorned with the familiar purplish color scheme.
The SLComposeViewController
class has one other class method worth mentioning, isAvailableForServiceType:
. The TWTweetComposeViewController has a similar class method name, canSendTweet
. This method allows your application to ask the operating system whether the service you want to target is available. As with composeViewControllerForServiceType
, the class method will accept a service type that indicates which service is being targeted. Even though isAvailableForServiceType:
returns a YES
or NO
, the operating system performs a number of checks for you behind the scenes. It will verify whether the social network is reachable and, more importantly, it will check if the user has installed a valid account for the target service. Let’s take a closer look at user accounts and how they are managed by the operating system.
Social Network Accounts
The Social framework adopts a concept known as single-sign-on (SSO), and it is important to understand the implications of this design decision. Put simply, it is no longer necessary for every third party application to authenticate itself with the target service, the operating system now takes care of this for you and exposes a number of endpoints to developers through the Social framework. As you may have guessed, this means that a central location for storing the user’s accounts is necessary. That central location is of course the Settings application of iOS, and allowing users to manage their social network details from within Settings makes sharing content from within apps much more efficient.
While the iOS 6 Settings application only provides the option to manage one Facebook account, users are able to control multiple Twitter accounts. By storing the user’s accounts in one central location, third party application developers no longer need to manage this sensitive data themselves.
Figure 1
Figure 2
UIActivityViewController
UIActivityViewController
is a wonderful addition to UIKit. It makes sharing content a breeze. As the class name implies, UIActivityViewController
is a subclass of UIViewController
. UIActivityViewController
makes sharing content trivial by giving the user an array of options to choose from. The user can share content via email, Twitter, or just paste a URL to the clipboard. There are quite a few options to choose from, and the endpoints are not restricted to the three social networks supported by the Social framework. As the UIActivityViewController
name implies, the class is part of UIKit, not the Social framework. In other words, there is no need to link your project against the Social framework if you only want to use the UIActivityViewController
class.
Even though UIActivityViewController
is not part of the Social framework, I have chosen to include it in this chapter as it shares some ground with the Social framework. In fact, you will notice in a few moments that UIActivityViewController
shares quite a bit of functionality with SLComposeViewController. To better understand UIActivityViewController
, you could compare it with a post office. A post office will take your message and make sure it is delivered to the address (the service or endpoint) specified by you.
Some of you may notice that it bears some resemblance with the well known open source library ShareKit. UIActivityViewController
is much more powerful, because it integrates seamlessly with the operating system and its services. However, as with ShareKit, developers can add custom endpoints to the array of endpoints that UIActivityViewController
integrates with and displays to the user.
Using UIActivityViewController
is just as straightforward as using SLComposeViewController. The designated initializer of UIActivityViewController
takes two parameters, (1) activity items and (2) application activities. Both arguments need to be instances of NSArray. The first parameter is an array of data objects you wish to share. The array can include one or more text snippets or just a single image. The type of content you can share not only depends on what type of data your application can provide, but also what type of data the endpoint accepts. The UIActivityViewController
class will make sure that the data objects are processed correctly depending on the service or endpoint the user chooses.
The second parameter, application activities, is an array of services or endpoints that UIActivityViewController
will present to the user. Services include email, Twitter, Facebook, printing, or copying something to the clipboard. As I mentioned earlier, developers have the option to add services as well. If application activities is set to nil, the operating system will use the default set of application activities.
After presenting the UIActivityViewController
instance to the user, UIActivityViewController
will take care of the rest. For example, if you have added Twitter as an application activity, UIActivityViewController
will first check whether Twitter is available and if the user has installed a Twitter account on their device before presenting it as an option to the end user. UIActivityViewController
saves you a lot of headaches, and it just takes of few lines of code to integrate with an application.
Tutorial Project
Project Setup
To illustrate how easy it is to use UIActivityViewController
and SLComposeViewController
, we are going to build a simple shoebox application to share photos. The application allows users to snap an image, add a caption, and share it with friends and family. The sample application will show you how easy it is to dynamically share content in iOS 6.
Start by creating a new project in Xcode. Select the Single View Application template from the list of templates (figure 3). Name your application Sharetastic, enter a company name and identifier, set iPhone for the device family, and check Use Automatic Reference Counting. Uncheck the remaining checkboxes for this project (figure 4). Specify where you want to save the project and click the Create button.
Figure 3
Figure 4
Adding the Social Framework
Since our application will take advantage of the Social framework, we need to add it to our project. Select the project in the Project Navigator and select the target on the right (figure 5). Select the Build Phases tab at the top, open the Link Binary With Libraries drawer, click the plus button, and choose Social.framework from the list (figure 5). Our project is now successfully linked against the Social framework.
Figure 5
Don’t forget to add an import statement at the top of our view controller’s header file in order to import the headers of the Social framework into your project.
#import <UIKit/UIKit.h> #import <Social/Social.h>
Outlets and Actions
Before creating our application’s user interface, we need to add the necessary outlets and actions to the header file of our view controller. A total of four outlets need to be added, a UITextField
for entering the caption of the image, a UIImageView
displaying the snapped image, and two UIButton
instances. Tapping the first button will send the image and caption to the user’s Facebook account. Tapping the second button brings up an instance of UIActivityViewController
, presenting the user with a number of options for sharing the image and caption. As you might have guessed, each button is tied to an IBAction
. The code snippet below shows how the header file of our view controller should look:
#import <UIKit/UIKit.h> #import <Social/Social.h> @interface RPViewController : UIViewController @property (weak, nonatomic) IBOutlet UITextField *captionTextField; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIButton *facebookButton; @property (weak, nonatomic) IBOutlet UIButton *shareButton; - (IBAction)facebook:(id)sender; - (IBAction)share:(id)sender; @end
User Interface
It is time to head over to our view controller’s XIB file. To keep things simple, we will not be using the new iOS 6 Autolayout features in this chapter. With the view controller’s XIB file selected, open the File Inspector on the right and uncheck the Use Autolayout checkbox.
With the view controller’s XIB file selected, start by dragging a text field in the view controller’s view and position it at the very top of the view (figure 6). Configure the text field by opening the Attributes Inspector and change its placeholder text to Enter Caption. With the text field still selected, open the Connections Inspector and connect the text field’s delegate outlet to the File’s Owner object on the left. This means that the view controller will act as the text field’s delegate. The reason for doing so will become clear in just a few minutes. Make sure you have also connected the captionTextField
outlet to the text field object on screen.
Figure 6
Drag an instance of UIImageView
from the library to the view controller’s view and position it below the text field (figure 6). Modify its dimensions to 280 points by 280 points. Open the Attributes Inspector once again and change the image view’s mode (content mode
) property to Aspect Fit and make sure to check User Interaction Enabled. The latter is important as we will add a tap gesture recognizer to the image view shortly. If we do not enable user interaction for the image view, the tap gesture recognizer will not work. Don’t forget to connect the view controller’s outlet with our image view. The easiest way to do this is by pressing the Ctrl
key, dragging from the image view to the File’s Owner object and selecting the appropriate outlet.
To finalize our application’s user interface, add two UIButton
instances to our view controller’s view and position them below the image view (figure 6). Give the top button a title of Facebook and the bottom button a title of Share. Connect the remaining outlets as I described above. In addition, connect the two actions of our view controller by pressing the Ctrl key, dragging from the File’s Owner object to each button, and then selecting the corresponding action. The user interface is ready. Now it’s time to start writing some code to make it all work!
Delegation
There is one more thing we need to add to our view controller’s header file. Since we are going to make use of UIImagePickerController
to snap images, our view controller needs to conform to both the UINavigationControllerDelegate
and UIImagePickerControllerDelegate
protocols. In addition, as I mentioned a few moments ago, our view controller will also act as the delegate of the caption text field. This means that the view controller needs to conform to the UITextFieldDelegate
protocol as well. Take a look at the updated header file of our view controller for clarification.
#import <UIKit/UIKit.h> #import <Social/Social.h> @interface RPViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate> @property (weak, nonatomic) IBOutlet UITextField *captionTextField; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIButton *facebookButton; @property (weak, nonatomic) IBOutlet UIButton *shareButton; - (IBAction)facebook:(id)sender; - (IBAction)share:(id)sender; @end
First Things First
In our view controller’s implementation file we start by extending the class definition and adding a private property. It is good practice to not expose every property of a class by declaring them in the the header file, and one way this is done is by extending the class definition in the implementation file. After the first import statement and before the @implementation
directive, add the below code snippet to declare a new, private property. The property will hold a reference to the image the user snaps with the device’s camera.
@interface RPViewController () @property (strong, nonatomic) UIImage *image; @end
A nice feature of Xcode 4.4+ is that you no longer need to synthesize property accessors. This is done for you behind the scenes and it follows Apple’s naming conventions. In our example, an instance variable will be created with a name of _image
and the accessors for our image property are automatically synthesized for us.
View Lifecycle
I always like to start by implementing the view life cycle of a view controller. In addition, I also prefer to keep the viewDidLoad
method short by adding some helper methods. One method I almost always create is setupView
, which takes care of setting up and configuring the view controller’s view. Let’s take a look at the viewDidLoad
and setupView
methods to get an idea of what I mean.
- (void)viewDidLoad { [super viewDidLoad]; // Setup View [self setupView]; } - (void)setupView { // Add Gesture Recognizer UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(takeImage:)]; [self.imageView addGestureRecognizer:tgr]; // Update View [self updateView]; }
As you can see, the setupView
method isn’t complex. We add a tap gesture recognizer to the image view so the user can snap an image by tapping the image view in our user interface. We first initialize a tap gesture recognizer and subsequently add it to our image view. The tap gesture recognizer sends the message snapImage:
to our view controller when the user taps the image view. We will take a look at the snapImage:
method in just a second.
At the end of the setupView
method, we invoke the updateView method. Again, this is a helper method I use very often as it allows me to group the logic associated with updating the view. Let’s take a quick look at the updateView
method.
- (void)updateView { BOOL sharingEnabled = self.image ? YES : NO; float sharingAlpha = sharingEnabled ? 1.0 : 0.5; self.facebookButton.enabled = sharingEnabled; self.facebookButton.alpha = sharingAlpha; self.shareButton.enabled = sharingEnabled; self.shareButton.alpha = sharingAlpha; }
As the method name implies, the method simply updates the view and its subviews. We begin with verifying whether an image is available by checking if our image property is set. If we have an image to work with, we enable the Facebook and share buttons, and set their alpha values to 1.0. If an image is not available, that is, the user has not yet taken one, we disable both buttons and make them slightly transparent to indicate to the user that the buttons are disabled.
Taking an Image
By taking advantage of UIImagePickerController
, snapping images is a simple and straightforward process. Let’s start by examining the snapImage:
method that is invoked by the tap gesture recognizer when the user taps the image view. First, an instance of UIImagePickerController
is initialized and its delegate is set to self, that is, our view controller. Next, we ask the operating system whether a camera is available by calling isSourceTypeAvailable
on the UIImagePickerController
class and passing it UIImagePickerControllerSourceTypeCamera
as the argument. Based on the return value, we set the source type of our UIImagePickerController
instance. As a failsafe, the source type is set to UIImagePickerControllerSourceTypePhotoLibrary
if no camera is available for whatever reason. This means that the user can select an image from the device’s photo library. Finally, the image picker controller is presented to the user.
- (void)takeImage:(UITapGestureRecognizer *)tgr { // Initialize Image Picker Controller UIImagePickerController *ip = [[UIImagePickerController alloc] init]; // Set Delegate [ip setDelegate:self]; if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { // Set Source Type to Camera [ip setSourceType:UIImagePickerControllerSourceTypeCamera]; } else { // Set Source Type to Photo Library [ip setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; } // Present View Controller [self presentViewController:ip animated:YES completion:nil]; }
Can you guess what the next step is? We need to implement the methods of the UIImagePickerControllerDelegate
protocol. The protocol has two methods, imagePickerController:didFinishPickingMediaWithInfo:
and imagePickerControllerDidCancel:
. Let me walk you through each method.
In imagePickerController:didFinishPickingMediaWithInfo:
, we start by storing a reference to the original, unedited image. In the next few lines of code, we assign the original image to our image property and update the view. Remember that this will enable the Facebook and share buttons. Finally, we dismiss the image picker controller.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage]; if (originalImage) { self.image = originalImage; // Update View [self updateView]; } // Dismiss Image Picker Controller [self dismissViewControllerAnimated:YES completion:nil]; }
The second delegate method contains only one line of code. If the user taps the cancel button, the image picker controller is dismissed without further ado.
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { // Dismiss Image Picker Controller [self dismissViewControllerAnimated:YES completion:nil]; }
Details Matter
Before implementing the facebook:
and share:
actions, I want to take a slight detour. Why haven’t we assigned the newly snapped image to the image view’s image property? Assigning the newly snapped image to the image property of our image view is indeed a perfectly viable option, but I have taken a different approach. Since the introduction of the @synthesize
directive, people often forget how useful setters and getters can be. Let me explain what I mean with an example.
In our sample application, I have overridden the setter of our view controller’s image property. This allows me to (1) group related code in one location and (2) only update the image view when the image changes. This might seem like a detail with very little use at this point, but it shows you how we can already start to optimize our code in subtle ways, which will impact code readability and even performance. In this particular example, it won’t affect the performance of our application, but it is good to use best practices whenever you see fit.
Add the following to the implementation file now:
- (void)setImage:(UIImage *)image { if (_image != image) { _image = image; // Update Image View self.imageView.image = _image; } }
UITextFieldDelegate
Before we can start sharing images, we need to implement one delegate method of the UITextFieldDelegate
protocol, that is, textFieldShouldReturn:
. In this delegate method, we resign the text field as the first responder if the user taps the keyboard’s return button. This will dismiss the keyboard and reveal the share buttons beneath it.
- (BOOL)textFieldShouldReturn:(UITextField *)textField { // Resign First Responder [textField resignFirstResponder]; return YES; }
Let’s Share
It is time to get our hands dirty with the Social framework and UIActivityViewController
. Let’s start by implementing the facebook:
method. It will illustrate how to use SLComposeViewController
. Take a look at its implementation below and let’s break it down step by step.
- (IBAction)facebook:(id)sender { if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) { // Initialize Compose View Controller SLComposeViewController *vc = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook]; // Configure Compose View Controller [vc setInitialText:self.captionTextField.text]; [vc addImage:self.image]; // Present Compose View Controller [self presentViewController:vc animated:YES completion:nil]; } else { NSString *message = @"It seems that we cannot talk to Facebook at the moment or you have not yet added your Facebook account to this device. Go to the Settings application to add your Facebook account to this device."; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oops" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } }
As I mentioned earlier, it is necessary to start by asking the operating system whether the service we are targeting is available. If isAvailableForServiceType:
returns NO
, we display an alert view telling the user what the problem might be. If the service is available, however, we initialize an instance of SLComposeViewController
by invoking the class method composeViewControllerForServiceType:
. The argument we pass to the class method is SLServiceTypeFacebook
, which indicates that we are targeting the Facebook platform.
Even though our user can modify the message before sending it to Facebook, we populate the message with the contents of the caption text field and attach the snapped image to the message. Finally, we present the compose view controller to the user. It is then up to the user to edit, cancel, or post the message. The user is in control. This is important to remember especially with the next chapter in mind where the user will not always know what our application does behind the scenes. At this stage, our application is ready for testing. Build and run your application on a test device and try it out for yourself!
Twitter, Facebook, or Sina Weibo
As I mentioned earlier, one of the strengths of the Social framework is how easy it is to replace one social network with another by changing the service type. Let’s try this by replacing the two instances of SLServiceTypeFacebook
with SLServiceTypeTwitter
. With this simple change, we can now share our images with our Twitter followers.
Sharing on Steroids
Our application isn’t quite finished yet. We still need to implement the share:
method. In this method, we make use of the UIActivityViewController
class to share the snapped image and associated caption. Take a look at the method’s implementation below.
- (IBAction)share:(id)sender { // Activity Items UIImage *image = self.image; NSString *caption = self.captionTextField.text; NSArray *activityItems = @[image, caption]; // Initialize Activity View Controller UIActivityViewController *vc = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil]; // Present Activity View Controller [self presentViewController:vc animated:YES completion:nil]; }
We first store the image and caption in an array. The image and caption are the so-called activityItems
, which are passed as an argument to the designated initializer of UIActivityViewController
. It is important to first pass the image and then the caption. Can you guess why?
The beauty of the UIActivityViewController
class is that it knows how to handle the data objects in the activity items array. This will become more clear when you test the share functionality. The second parameter of the initialization method is an array containing the services we want to make available to the user. If you pass nil
, the operating system will show the user every service UIActivityViewController
has at its disposal. It is often better to only display the services appropriate for a particular use case.
As I mentioned in the introduction, UIActivityViewController
will check for the availability of each service behind the scenes. The name Application Services might sound a bit odd or abstract, but it covers a broad array of service endpoints, such as, email, text messaging as well as social networks and printing.
At the end of our share:
method, we present the instance of UIActivityViewController
to the user. Build and run your application and try it out for yourself.
Additional Configuration
UIActivityViewController
has two properties, completionHandler
and excludedActivityTypes
. By setting a completion handler, you can execute a block if the user taps the cancel button or when the sharing operation is completed. As you might have guessed, the completion handler is a block (UIActivityViewControllerCompletionHandler
) and takes two parameters, (1) the activity type and (2) a boolean indicating whether the service was performed or not. The boolean value will also be set to NO
if the user cancels or dismisses the UIActivityViewController
instance.
SLComposeViewController
has a completion handler that works much in the same way. Instead of two parameters, it accepts only the result of the operation.
Wrap Up
You should now have a good understanding of what the Social framework can offer you. We have taken a close look at SLComposeViewController
as well as UIActivityViewController
. Both classes are incredibly useful and yet easy to use. Sharing content used to be cumbersome and painful, but these two classes make the process much easier. I am very pleased with the introduction of the Social framework in iOS 6 as well as with the addition of UIActivityViewController
to UIKit.
Hopefully I’ll see the Social framework used in your applications soon!
More iOS 6 Content?
If you enjoyed this post, be sure to check out the upcoming book Decoding the iOS 6 SDK, an anthology of iOS 6 tutorials written by top Mobiletuts+ contributors.