Social Framework in Xamarin.iOS
The Social Framework provides a unified API for interacting with social networks including Twitter and Facebook, as well as SinaWeibo for users in China.
Using the Social Framework allows applications to interact with social networks from a single API without having to manage authentication. It includes a system provided view controller for composing posts as well as an abstraction that allows consuming each social network’s API over HTTP.
Connecting to Twitter
Twitter Account Settings
To connect to Twitter using the Social Framework, an account needs to be configured in the device settings as shown below:
Once an account has been entered and verified with Twitter, any application on the device that uses the Social Framework classes to access Twitter will use this account.
Sending Tweets
The Social Framework includes a controller called SLComposeViewController
that presents a system provided view for
editing and sending a tweet. The following screenshot shows an example of this
view:
To use an SLComposeViewController
with Twitter, an instance of
the controller must be created by calling the FromService
method
with SLServiceType.Twitter
as shown below:
var slComposer = SLComposeViewController.FromService (SLServiceType.Twitter);
After the SLComposeViewController
instance is returned, it can
be used to present a UI to post to Twitter. However, the first thing to do is to
check the availability of the social network, Twitter in this case, by calling IsAvailable
:
if (SLComposeViewController.IsAvailable (SLServiceKind.Twitter)) {
...
}
SLComposeViewController
never sends a tweet directly without
user interaction. However, it can be initialized with the following methods:
SetInitialText
– Adds the initial text to show in the tweet.AddUrl
– Adds a Url to the tweet.AddImage
– Adds an image to the tweet.
Once initialized, calling PresentVIewController
displays the
view created by the SLComposeViewController
. The user can then
optionally edit and send the tweet, or cancel sending it. In either case, the
controller should be dismissed in the CompletionHandler
, where the
result can also be checked to see if the tweet was sent or cancelled, as shown
below:
slComposer.CompletionHandler += (result) => {
InvokeOnMainThread (() => {
DismissViewController (true, null);
resultsTextView.Text = result.ToString ();
});
};
Tweet Example
The following code demonstrates using the SLComposeViewController
to present a view used to send a tweet:
using System;
using Social;
using UIKit;
namespace SocialFrameworkDemo
{
public partial class ViewController : UIViewController
{
#region Private Variables
private SLComposeViewController _twitterComposer = SLComposeViewController.FromService (SLServiceType.Twitter);
#endregion
#region Computed Properties
public bool isTwitterAvailable {
get { return SLComposeViewController.IsAvailable (SLServiceKind.Twitter); }
}
public SLComposeViewController TwitterComposer {
get { return _twitterComposer; }
}
#endregion
#region Constructors
protected ViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
// Update UI based on state
SendTweet.Enabled = isTwitterAvailable;
}
#endregion
#region Actions
partial void SendTweet_TouchUpInside (UIButton sender)
{
// Set initial message
TwitterComposer.SetInitialText ("Hello Twitter!");
TwitterComposer.AddImage (UIImage.FromFile ("Icon.png"));
TwitterComposer.CompletionHandler += (result) => {
InvokeOnMainThread (() => {
DismissViewController (true, null);
Console.WriteLine ("Results: {0}", result);
});
};
// Display controller
PresentViewController (TwitterComposer, true, null);
}
#endregion
}
}
Calling Twitter API
The Social Framework also includes support for making HTTP requests to social
networks. It encapsulates the request in an SLRequest
class that is
used to target the particular social network’s API.
For example, the following code makes a request to Twitter to get the public timeline (by expanding on the code given above):
using Accounts;
...
#region Private Variables
private ACAccount _twitterAccount;
#endregion
#region Computed Properties
public ACAccount TwitterAccount {
get { return _twitterAccount; }
}
#endregion
#region Override Methods
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
// Update UI based on state
SendTweet.Enabled = isTwitterAvailable;
RequestTwitterTimeline.Enabled = false;
// Initialize Twitter Account access
var accountStore = new ACAccountStore ();
var accountType = accountStore.FindAccountType (ACAccountType.Twitter);
// Request access to Twitter account
accountStore.RequestAccess (accountType, (granted, error) => {
// Allowed by user?
if (granted) {
// Get account
_twitterAccount = accountStore.Accounts [accountStore.Accounts.Length - 1];
InvokeOnMainThread (() => {
// Update UI
RequestTwitterTimeline.Enabled = true;
});
}
});
}
#endregion
#region Actions
partial void RequestTwitterTimeline_TouchUpInside (UIButton sender)
{
// Initialize request
var parameters = new NSDictionary ();
var url = new NSUrl("https://api.twitter.com/1.1/statuses/user_timeline.json?count=10");
var request = SLRequest.Create (SLServiceKind.Twitter, SLRequestMethod.Get, url, parameters);
// Request data
request.Account = TwitterAccount;
request.PerformRequest ((data, response, error) => {
// Was there an error?
if (error == null) {
// Was the request successful?
if (response.StatusCode == 200) {
// Yes, display it
InvokeOnMainThread (() => {
Results.Text = data.ToString ();
});
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", response.StatusCode);
});
}
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", error);
});
}
});
}
#endregion
Let's look at this code in detail. First, it gains access to the Account Store and gets the type of a Twitter account:
var accountStore = new ACAccountStore ();
var accountType = accountStore.FindAccountType (ACAccountType.Twitter);
Next, it asks the user if your app can have access to their Twitter account and, if access is granted, the account is loaded into memory and the UI updated:
// Request access to Twitter account
accountStore.RequestAccess (accountType, (granted, error) => {
// Allowed by user?
if (granted) {
// Get account
_twitterAccount = accountStore.Accounts [accountStore.Accounts.Length - 1];
InvokeOnMainThread (() => {
// Update UI
RequestTwitterTimeline.Enabled = true;
});
}
});
When the user requests the timeline data (by tapping a button in the UI), the app first forms a request to access the data from Twitter:
// Initialize request
var parameters = new NSDictionary ();
var url = new NSUrl("https://api.twitter.com/1.1/statuses/user_timeline.json?count=10");
var request = SLRequest.Create (SLServiceKind.Twitter, SLRequestMethod.Get, url, parameters);
This example is limiting the returned results to the last ten entries by including ?count=10
in the URL. Finally, it attaches the request to the Twitter account (that was loaded above) and performs the call to Twitter to fetch the data:
// Request data
request.Account = TwitterAccount;
request.PerformRequest ((data, response, error) => {
// Was there an error?
if (error == null) {
// Was the request successful?
if (response.StatusCode == 200) {
// Yes, display it
InvokeOnMainThread (() => {
Results.Text = data.ToString ();
});
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", response.StatusCode);
});
}
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", error);
});
}
});
If the data was successfully loaded, the raw JSON data will be displayed (as in the example output below):
In a real app, the JSON results could then be parsed as normal and the results presented to the user. See Introduction Web Services for information on how to parse JSON.
Connecting to Facebook
Facebook Account Settings
Connecting to Facebook with the Social Framework is nearly identical to the process used for Twitter shown above. A Facebook user account must be configured in the device settings as shown below:
Once configured, any application on the device that uses the Social Framework will use this account to connect to Facebook.
Posting to Facebook
As the Social Framework is a unified API designed to access multiple social networks, the code remains nearly identical regardless of the social network being used.
For example, the SLComposeViewController
can be used exactly as
in the Twitter example shown earlier, the only different is switching to the Facebook-specific settings and options. For example:
using System;
using Foundation;
using Social;
using UIKit;
namespace SocialFrameworkDemo
{
public partial class ViewController : UIViewController
{
#region Private Variables
private SLComposeViewController _facebookComposer = SLComposeViewController.FromService (SLServiceType.Facebook);
#endregion
#region Computed Properties
public bool isFacebookAvailable {
get { return SLComposeViewController.IsAvailable (SLServiceKind.Facebook); }
}
public SLComposeViewController FacebookComposer {
get { return _facebookComposer; }
}
#endregion
#region Constructors
protected ViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
// Update UI based on state
PostToFacebook.Enabled = isFacebookAvailable;
}
#endregion
#region Actions
partial void PostToFacebook_TouchUpInside (UIButton sender)
{
// Set initial message
FacebookComposer.SetInitialText ("Hello Facebook!");
FacebookComposer.AddImage (UIImage.FromFile ("Icon.png"));
FacebookComposer.CompletionHandler += (result) => {
InvokeOnMainThread (() => {
DismissViewController (true, null);
Console.WriteLine ("Results: {0}", result);
});
};
// Display controller
PresentViewController (FacebookComposer, true, null);
}
#endregion
}
}
When used with Facebook, the SLComposeViewController
displays a
view that looks nearly identical to the Twitter example, showing Facebook as the title in this case:
Calling Facebook Graph API
Similar to the Twitter example, the Social Framework’s SLRequest
object can be used with Facebook’s graph API. For
example, the following code returns information from the graph API about the
Xamarin account (by expanding on the code given above):
using Accounts;
...
#region Private Variables
private ACAccount _facebookAccount;
#endregion
#region Computed Properties
public ACAccount FacebookAccount {
get { return _facebookAccount; }
}
#endregion
#region Override Methods
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
// Update UI based on state
PostToFacebook.Enabled = isFacebookAvailable;
RequestFacebookTimeline.Enabled = false;
// Initialize Facebook Account access
var accountStore = new ACAccountStore ();
var options = new AccountStoreOptions ();
var options.FacebookAppId = ""; // Enter your specific Facebook App ID here
accountType = accountStore.FindAccountType (ACAccountType.Facebook);
// Request access to Facebook account
accountStore.RequestAccess (accountType, options, (granted, error) => {
// Allowed by user?
if (granted) {
// Get account
_facebookAccount = accountStore.Accounts [accountStore.Accounts.Length - 1];
InvokeOnMainThread (() => {
// Update UI
RequestFacebookTimeline.Enabled = true;
});
}
});
}
#endregion
#region Actions
partial void RequestFacebookTimeline_TouchUpInside (UIButton sender)
{
// Initialize request
var parameters = new NSDictionary ();
var url = new NSUrl ("https://graph.facebook.com/283148898401104");
var request = SLRequest.Create (SLServiceKind.Facebook, SLRequestMethod.Get, url, parameters);
// Request data
request.Account = FacebookAccount;
request.PerformRequest ((data, response, error) => {
// Was there an error?
if (error == null) {
// Was the request successful?
if (response.StatusCode == 200) {
// Yes, display it
InvokeOnMainThread (() => {
Results.Text = data.ToString ();
});
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", response.StatusCode);
});
}
} else {
// No, display error
InvokeOnMainThread (() => {
Results.Text = string.Format ("Error: {0}", error);
});
}
});
}
#endregion
The only real difference between this code and the Twitter version presented above, is Facebook's requirement to get a Developer/App specific ID (which you can generate from Facebook's Developer Portal) which must be set as an option when making the request:
var options = new AccountStoreOptions ();
var options.FacebookAppId = ""; // Enter your specific Facebook App ID here
...
// Request access to Facebook account
accountStore.RequestAccess (accountType, options, (granted, error) => {
...
});
Failure to set this option (or using an invalid key) will result in either an error or no data being returned.
Summary
This article showed how to use the Social Framework to interact with Twitter
and Facebook. It showed where to configure accounts for each social network in
the device settings. It also discussed how to use the SLComposeViewController
to present a unified view for posting to
social networks. Additionally, it examined the SLRequest
class that
is used to call each social network’s API.