iOSSocial is a collection of classes that makes it easy to integrate with one or more popular social networks. Includes OAuth authentication support for each service out-of-the box via view controllers and saving of OAuth tokens in the keychain. The code supports authenticating multiple accounts for a given service and also the concept of setting a particular service as the primary service to be used for authentication.
Supported services:
- OAuth 2: Instagram, Foursquare, Facebook
- OAuth 1: Twitter, Flickr, Tumblr
Supported iOS platforms:
- iOS 4
- iOS 5
Hopefully coming soon!
Prerequisites: XCode 4.2 with iOS 5 and ARC support
iOSSocial is intended to be used as drop-in code for your project. To use it in your project, do the following (refer to the InstaBeta sample application in the iOSSocial workspace to see all of the files in place and all of the correct project settings):
Fork this repo and add your fork as a submodule to your git repository. Perform the following commands from a terminal in the base directory of your repository:
- 'git submodule add [email protected]:[your username]/iOSSocial.git iOSSocial'
- 'git submodule update --init'
- 'cd iOSSocial'
- 'git submodule update --init'
Drag the iOSSocial source files into your project.
- add submodule for iOSSocial and drag iOSSocial folder to project
- make group facebook-ios-sdk and drag src code to group
- make asi-http-request group and drag src to group (main files + oath + reachability)
- make gtm-oauth group and drag src to group
- make gtm-http-fetcher group and drag src to group
- make gtm-oauth2 group and drag src to group
- drag prp folder to project
Link your application against Security.framework, SystemConfiguration.framework, CFNetwork.framework, MobileCoreServices.framework, Twitter, Accounts, libz.1.2.5.dylib
Add '-ObjC' linker flag
Add '-fno-objc-arc' compile flag. The compiler will spit out ARC errors for some files from third party libraries so you have to turn off ARC for these files.
- In Xcode, select your project and go to Build Phases > Compile Sources
- You will have to add it individually for each file. Hopefully Apple will eventually make it possible to add the flag to multiple files at once.
Special Considerations:
Facebook: iOSSocial makes use of the facebook-ios-sdk which uses Facebook's Single Sign On (SSO) feature. In order for the Facebook SDK to redirect back to your app after authorization, you will have to specify two URL Schemes in your application's main plist file (for instructions on adding these to your plist file go here https://github.com/facebook/facebook-ios-sdk OR look at the main plist file in the InstaBeta sample application in the iOSSocial workspace 'InstaBeta-Info.plist'.). In the example below, replace [appID] with the ID that Facebook assigned to the application that you created on their site and set [URLSuffix] to a useful name/identifier for your app. The URLSuffix allows the Facebook SDK to properly redirect back to the correct application even if more than one application on the device uses the same Facebook appID (for example, you have a trial and full version of the same app available within the app store):
- fb[appID][URLSuffix]://authorize (refirect back from Safari)
- fb[appID][URLSuffix] (redirect back from Facebook app via Single Sign On)
iOSSocialViewControllers
If you want to use the iOSSocialViewControllers, fork the repo and add a submodule for it and drag the source files into your project:
- 'git submodule add [email protected]:[your username]/iOSSocialViewControllers.git iOSSocialViewControllers'
- 'git submodule update --init'
iOSSServicesViewController is a stand along class that can be used in conjunction with iOSSocial. It is shown here to give you an idea of how you can easily use iOSSocial to build a UI that presents a choice of services to a user to authenticate against. It lives in a separate repo here: https://github.com/chris124/iOSSocialViewControllers
Use iOSSServicesViewController to present a list of supported services to the user. The controller is evolving, but for now, it shows a list of available services to connect to (based on the services you choose to configure. Read below on iOSSocialServiceProtocol.):
When a service is chosen, the OAuth page for the service is shown:
If the user authenticates for the service, their user account is shown as connected under the accounts section:
Selecting an already connected account logs the user out of that account (ie, clears out their locally stored OAuth access token for their account on the given service). Selected a disconnected account will let the user log back in:
Here is the code in action:
iOSSServicesViewController *iossServicesViewController = [[iOSSServicesViewController alloc] initWithServicesFilter:nil];
[iossServicesViewController presentModallyFromViewController:self
withServiceConnectedHandler:^(id localUser) {
}
withCompletionHandler:^{
[self dismissModalViewControllerAnimated:YES];
}
];
If you want to filter which services are displayed in the view controller, pass in an array of service names.
NSArray *filter = [NSArray arrayWithObjects:@"Twitter", nil];
iOSSServicesViewController *iossServicesViewController = [[iOSSServicesViewController alloc] initWithServicesFilter:filter];
[iossServicesViewController presentModallyFromViewController:self
withServiceConnectedHandler:^(id localUser) {
}
withCompletionHandler:^{
[self dismissModalViewControllerAnimated:YES];
}
];
Which services you want to use is up to you. The underlying code will automatically pick-up which services are available based on what you configure. It is recommended that the services be configured in your application delegate before calling any other code. Here is an example of how the Instagram, Twitter, and Foursquare services are configured in an application delegate:
Note: For Instagram, when setting the object for the kSMOAuth2RedirectURI key in the OAuth params dictionary, the URL specified MUST match the callback URL you specified when you created your Instagram application on their site. It doesn't matter what the URL actually points to, but the value specified here HAS to match what you specified when you created your Instagram app. For example, http://www.mycompanyurl.com/instagram/callback for the Instagram oauth settings.
Note: Be passing YES to the asPrimary parameter, we are stating that Instagram is going to be the primary service for authentication.
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:@"instagram app client id" forKey:kSMOAuth2ClientID];
[params setObject:@"instagram app client secret" forKey:kSMOAuth2ClientSecret];
[params setObject:@"instagram app callback" forKey:kSMOAuth2RedirectURI];
[params setObject:@"unique name for keychain item" forKey:kSMOAuth2KeychainItemName];
[params setObject:@"https://api.instagram.com/oauth/authorize" forKey:kSMOAuth2AuthorizeURL];
[params setObject:@"https://api.instagram.com/oauth/access_token" forKey:kSMOAuth2AccessTokenURL];
[params setObject:@"Instagram Service" forKey:kSMOAuth2ServiceProviderName];
[params setObject:@"basic comments relationships likes" forKey:kSMOAuth2Scope];
[[Instagram sharedService] assignOAuthParams:params asPrimary:YES];
[params removeAllObjects];
[params setObject:@"twitter app client id" forKey:kSMOAuth1ClientID];
[params setObject:@"twitter app client secret" forKey:kSMOAuth1ClientSecret];
[params setObject:@"twitter app callback" forKey:kSMOAuth1RedirectURI];
[params setObject:@"unique name for keychain item" forKey:kSMOAuth1KeychainItemName];
[params setObject:@"https://api.twitter.com/oauth/request_token" forKey:kSMOAuth1RequestTokenURL];
[params setObject:@"https://api.twitter.com/oauth/access_token" forKey:kSMOAuth1AccessTokenURL];
[params setObject:@"https://api.twitter.com/oauth/authorize" forKey:kSMOAuth1AuthorizeURL];
[params setObject:@"Twitter Service" forKey:kSMOAuth1ServiceProviderName];
[[Twitter sharedService] assignOAuthParams:params asPrimary:NO];
[params removeAllObjects];
[params setObject:@"foursquare app client id" forKey:kSMOAuth2ClientID];
[params setObject:@"foursquare app client secret" forKey:kSMOAuth2ClientSecret];
[params setObject:@"foursquare app callback" forKey:kSMOAuth2RedirectURI];
[params setObject:@"unique name for keychain item" forKey:kSMOAuth2KeychainItemName];
[params setObject:@"https://foursquare.com/oauth2/authorize" forKey:kSMOAuth2AuthorizeURL];
[params setObject:@"https://foursquare.com/oauth2/access_token" forKey:kSMOAuth2AccessTokenURL];
[params setObject:@"Foursquare Service" forKey:kSMOAuth2ServiceProviderName];
[[Foursquare sharedService] assignOAuthParams:params asPrimary:NO];
Local user account objects are used to represent an authenticated user account for a given service. All of the local user account classes adhere to iOSSocialLocalUserProtocol. The available classes are LocalInstagramUser, LocalTwitterUser, LocalFoursquareUser, LocalFlickrUser, and LocalTumblrUser, (more to come as more services are supported). These derived classes can be created directly or if you just want to get an instance of a local user object for whichever service was set at the primary service you can call [[iOSSocialServicesStore sharedServiceStore] defaultAccount]. Once you have a local user account object, you can authenticate it, query its authentication state, log it out, get its OAuth access token and more. See the code below:
// The defaultAccount method returns an local user object for the service that was set as the primary service.
id localUser = [[iOSSocialServicesStore sharedServiceStore] defaultAccount];
BOOL isAuthenticated = [localUser isAuthenticated];
// The OAuth View Controller is shown modally from the current view controller so pass in 'self' for the 'fromViewController' param.
[localUser authenticateFromViewController:self
withCompletionHandler:^(NSError *error){
if (error) {
} else {
NSString *accessToken = [localUser oAuthAccessToken];
}
}];
// Logging out is easy peasy. Note that this does NOT revoke the user's access to the actual service on their site. It just clears out
// all of the local OAuth goodies.
[localUser logout];
iOSServicesDataSource is the underlying data source (UITableViewDataSource) that you want to use for your UI (a UITableView) to present the list of available services to the user. For your convenience, there is an initializer that takes no params that will make all of the configured services available from the data source and there is a initWithServicesFilter: initializer that takes an array of iOSSocialServiceProtocol objects to let you specify which specific services you want to display at a given time.
iOSSServicesViewController is powered by iOSServicesDataSource. See iOSSServicesViewController's initWithServicesFilter: initializer to see how to filter which services are provided by the data source.
NSArray *filter = [NSArray arrayWithObjects: [[iOSSocialServicesStore sharedServiceStore] serviceWithType:@"Facebook"], [[iOSSocialServicesStore sharedServiceStore] serviceWithType:@"Twitter"], [[iOSSocialServicesStore sharedServiceStore] serviceWithType:@"Foursquare"], [[iOSSocialServicesStore sharedServiceStore] serviceWithType:@"Tumblr"], nil];
iOSServicesDataSource *dataSource = [[iOSServicesDataSource alloc] initWithServicesFilter:filter];
If you want to use your own UITableViewController, here is example tableView:didSelectRowAtIndexPath: code:
switch (indexPath.section) {
case 0:
{
//get back a local user object here.
id localUser = [[iOSSocialServicesStore sharedServiceStore].accounts objectAtIndex:[indexPath row]];
[[iOSSocialServicesStore sharedServiceStore] unregisterAccount:localUser];
[localUser logout];
[self.tableView reloadData];
}
break;
case 1:
{
//get back a service object here.
id service = [[iOSSocialServicesStore sharedServiceStore].services objectAtIndex:[indexPath row]];
id localUser = [service localUser];
[localUser authenticateFromViewController:self withCompletionHandler:^(NSError *error) {
if (!error) {
NSLog(@"authorized");
[[iOSSocialServicesStore sharedServiceStore] registerAccount:localUser];
[self.tableView reloadData];
} else {
NSLog(@"authorization failed");
}
}];
}
break;
default:
break;
}
There are a lot of services to support and this is where you come in. :) To add support for a new service, fork the repo, create a branch and give it a good name (one that matches the service you want to integrate with would be perfect), create a sub folder and name it after the service and then create the three following files (You can copy and paste the content from one of the other services. Please bare with me for now. Lots of duplicate code. Need to do a major code cleanup.):
- ServiceName.m/.h: This class must implement iOSSocialServiceProtocol and be derived from iOSSocialServiceProtocol or iOSSocialServiceOAuth2Provider depending on if its an OAuth1 or OAuth2 service. See Instagram.m/.h.
- ServiceNameUser.m/.h: This class must implement iOSSocialUserProtocol. See InstagramUser.m/.h.
- LocalServiceNameUser.m/.h: This class must implement toiOSSocialLocalUserProtocol. See LocalInstagramUser.m/.h.