Building Native iOS Apps with the Salesforce Mobile SDK: Adding Push Notifications and File Sharing

This guest article by salesforce.com is a sponsored post.

Introduction

In this tutorial, we are going to utilize a couple of the latest features in the Salesforce Mobile SDK 2.1, which includes file sharing. Sign up for a Force.com account prior to going through this exercise. The benefit of file sharing for your iOS App is to share quotes, documents and overall images and information with members of your group within salesforce.com. Salesforce Mobile SDK provides you with a great framework for starting your iOS app, which includes auto-creation of a connected app. We will go through installation of the Salesforce Mobile SDK, app creation, accessing salesforce.com data and handling files using methods in Salesforce REST API.

When logging into your environment, you have the option to modify shared settings as well as upload PDFs and images. To have it accessible on a native iOS app is even more critical for sharing quotes, documents, and other information immediately on your phone.

Step 1: Sign up for a Force.com account and sign in

  1. Go to https://developer.salesforce.com/signup and sign up for your Developer Edition (DE) account. For the purposes of this example, I recommend signing up for a Developer Edition even if you already have an account. This ensures you get a clean environment with the latest features enabled.
  2. Navigate to https://login.salesforce.com to log into your developer account.

Step 2: Install the Salesforce Mobile SDK 2.1 using npm

There are two ways to install the Salesforce Mobile SDK. In this tutorial, we are going to use npm, otherwise known as the package manager for Node.js.

  1. In order to use npm, go to the following site to download nps and Node.js: http://www.nodejs.org/download.
  2. Run the download installer provided.
  3. Verify npm was installed correctly:
    1. Launch a terminal window.
    2. Type npm at the prompt.
    3. You should see Usage information related to npm similar to what is listed below; if not, please run through the installation steps of Node.js and npm.
  4. Install the forceios package:
     

    At the terminal window, enter the following: sudo npm install forceios -g

    This command will allow you to run npm install from any directory. The package will be installed in /usr/local/lib/node_modules, and links binary modules in /usr/local/bin.

Step 3: Create a Native iOS Project utilizing the Salesforce Mobile SDK 2.1M

  1. Set up a working directory:
    • Navigate to the directory where you will be creating your project.
  2. Create your iOS project. At the terminal window, enter the following: forceios
    This utility will prompt you for the following information: 

    1. The application type is native.
    2. The application name is FileShareTutorialApp.
    3. For company identifier, enter com.testApp.
    4. For organization name, enter TestApp, Inc.
    5. For output directory, the default is the current directory.
    6. For the connected app ID, retrieve this from your Force.com account.
    7. For the Callback URI, retrieve this from your Force.com account.
  3. Upon successful completion of creation of the iOS project, you will receive a notification at the command line:
  4. Open your Project. Cd to the FileSharingTutorial directory and open the *.xcodeproj file.This will launch XCode with the newly generated project you have created. The following is a screenshot of your project files in XCode. Note that the frameworks and Salesforce SDKs have already been integrated into your project.
  5. 5. Run the Project. Select the iOS Simulator 7.1, iPhone Retina (or other iPhone simulators), and press the compile button. The simulator will launch and you will see the following layout:
     

    Login to your Force.com account. An authentication screen will appear.

    Select “Allow.” You will then be brought to the home screen, which will include minimal functionality displaying your first and last name.

    Congratulations! You have built your first salesforce.com connected app.

Step 4: Modifying the iOS App

Overview of the iOS App

A majority of changes that will take place during the course of the tutorial will be done in the RootViewController.

In our AppDelegate, the SFAccountManager settings have been added in our init method. Please note that these are basic minimum requirements for identifying the Connected App. Also, notifications have been added for login and logout features. After creating a Force.com connected application, the RemoteAccessConsumerKey and the OAuthRedirectURI are populated in the AppDelegate.

After the user logs in, the control is passed to the RootViewController. Overall, the flow consists of displaying the InitialViewController prior to connecting, logging into salesforce.com, and displaying the response in the RootViewController.

In our RootViewController, there is no associated xib file. It inherits from UITableViewController and uses the Salesforce RestDelegate. In our viewDidLoad method, we are sending a request to the server to retrieve the name from the user with the Salesforce RestAPI delegate methods already populated. This interface is critical for acceptance of REST responses. There are 4 methods which address each response type. Add to these methods for additional customization upon a successful request, failed request, cancelled request, or timeout request. The default functionality will print a message out to the console.

1. - (void)request:(SFRestRequest *)request didLoadResponse:(id)jsonResponse
2. - (void)request:(SFRestRequest*)request didFailLoadWithError:(NSError*)error {
3. - (void)requestDidCancelLoad:(SFRestRequest *)request
4. - (void)requestDidTimeout:(SFRestRequest *)request

The delegate methods for the UITableViewController have defaulted to returning 1 section, as well as the number of data rows returned from the initial request. In each of the table view cells, the default image provided will display as well as the data which consists of the user’s name. The accessory disclosure indicator has also been included in the cell.

Modifying the UI

In order to include groups and share and upload files capabilities, we are going to create simple buttons on the top to demonstrate this functionality. Note that the RootViewController, where a majority of the implementation will take place does not have an associated xib file.

Let’s create a separate method to load the view and implement the following bar button items to include these features.

For our button handlers, add the following functionality to your RootViewController file.

Here, we are adding Navigation bar button items. There is also a TableView as part of the template embedded in the RootViewController. We will also change the title of the app in the RootViewController to “File Sharing App”:

- (void)loadView {
[super loadView];
self.title = @"File Sharing App";

ownedFilesButton = [[UIBarButtonItem alloc] initWithTitle:@"Owned" style:UIBarButtonItemStylePlain target:self action:@selector(showOwnedFiles)];
groupsFilesButton = [[UIBarButtonItem alloc] initWithTitle:@"Groups" style:UIBarButtonItemStylePlain target:self action:@selector(showGroupsFiles)];
sharedFilesButton = [[UIBarButtonItem alloc] initWithTitle:@"Shared" style:UIBarButtonItemStylePlain target:self action:@selector(showSharedFiles)];

addFileShareButton = [[UIBarButtonItem alloc] initWithTitle:@"Upload" style:UIBarButtonItemStylePlain target:self action:@selector(showSharedFiles)];
sharedFilesButton = [[UIBarButtonItem alloc] initWithTitle:@"Shared" style:UIBarButtonItemStylePlain target:self action:@selector(addSharing)];

self.navigationItem.rightBarButtonItems = @[ownedFilesButton, groupsFilesButton, sharedFilesButton, uploadFilesButton, addFileShareButton

Include the following declarations in the RootViewController implementation file:

#import <SalesforceSDKCore/SFAuthenticationManager.h>
#import <SalesforceNativeSDK/SFRestAPI+Blocks.h>
#import <SalesforceNativeSDK/SFRestAPI+Files.h>
#import <SalesforceNativeSDK/SFRestRequest.h>

typedef void (^ThumbnailLoadedBlock) (UIImage *thumbnailImage);

@interface RootViewController () {
}
@property (nonatomic, strong) UIBarButtonItem* ownedFilesButton;
@property (nonatomic, strong) UIBarButtonItem* sharedFilesButton;
@property (nonatomic, strong) UIBarButtonItem* groupsFilesButton;
@property (nonatomic, strong) UIBarButtonItem* uploadFilesButton;
@property (nonatomic, strong) UIBarButtonItem* addFileShareButton
;

@property (nonatomic, strong) NSMutableDictionary* thumbnailCache;

- (void) downloadThumbnail:(NSString*)fileId completeBlock:(ThumbnailLoadedBlock)completeBlock;
- (void) showOwnedFiles;
- (void) showGroupsFiles;
- (void) showSharedFiles;
- (void) uploadFiles;
- (void) addSharing;

@end

Step 5: Getting Thumbnail of File to Share

When we display shared files in our TableView, we want to display the thumbnails. In your loadView method within RootViewController, we created a dictionary object– thumbnailCache.

- (void)loadView {
[super loadView];
self.thumbnailCache = [NSMutableDictionary dictionary];

Our dictionary object will be used in getting and downloading our thumbnails for the file to be shared. Therefore, if the image is already cached, return the image. If it is not cached, then resize it and cache it.

Add the following 2 methods to our RootViewController file.

- (void) getThumbnail:(NSString*) fileId completeBlock:(ThumbnailLoadedBlock)completeBlock {
// cache hit
if (self.thumbnailCache[fileId]) {
completeBlock(self.thumbnailCache[fileId]);
}
// cache miss
else {
[self downloadThumbnail:fileId completeBlock:^(UIImage *image) {
// size it
UIGraphicsBeginImageContext(CGSizeMake(120,90));
[image drawInRect:CGRectMake(0, 0, image.size.width, 90)];
UIImage *thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// cache it
self.thumbnailCache[fileId] = thumbnailImage;
// done
completeBlock(thumbnailImage);
}];
}
}

- (void) downloadThumbnail:(NSString*)fileId completeBlock:(ThumbnailLoadedBlock)completeBlock {
SFRestRequest *imageRequest = [[SFRestAPI sharedInstance] requestForFileRendition:fileId version:nil renditionType:@"THUMB120BY90" page:0];
[[SFRestAPI sharedInstance] sendRESTRequest:imageRequest failBlock:nil completeBlock:^(NSData *responseData) {
NSLog(@"downloadThumbnail:%@ completed", fileId);
UIImage *image = [UIImage imageWithData:responseData];
dispatch_async(dispatch_get_main_queue(), ^{
completeBlock(image);
});
}];
}

Step 6: Implement File Sharing Methods

For file sharing methods, in this tutorial, we will demonstrate how to share an image. For future enhancements, we will incorporate other file types supported by the SDK which include PDF and other document types.

File Sharing Methods

Add the following methods to your RootViewController file:

  1. In the showOwnedFiles method, we are sending a request to the server to retrieve the list of files owned by the user currently logged in.
  2. - (void) showOwnedFiles
    {
    NSLog(@"Show Owned Files Pressed");
    SFRestRequest *request = [[SFRestAPI sharedInstance] requestForOwnedFilesList:nil page:0];
    [[SFRestAPI sharedInstance] send:request delegate:self];
    }
  3. In the showGroupsFiles methods, the request we are sending to the server will provide a list of files accessibility to groups.
  4. - (void) showGroupsFiles
    {
    NSLog(@"Show Group Files Pressed");

    SFRestRequest *request = [[SFRestAPI sharedInstance] requestForFilesInUsersGroups:nil page:0];
    [[SFRestAPI sharedInstance] send:request delegate:self];
    }
  5. In the showSharedFiles method, we need to display what files are shared by others for that user, or shared as a group.
  6. - (void) showSharedFiles
    {
    NSLog(@"Show Shared Files Pressed");

    SFRestRequest *request = [[SFRestAPI sharedInstance] requestForFilesSharedWithUser:nil page:0];
    [[SFRestAPI sharedInstance] send:request delegate:self];
    }
  7. In the shareFiles method, we will utilize the requestForAddFileShare method to share the file to the group. For the shareType, we can use V for viewing or C for collaboration. In this example we will use V.
  8. - (void) shareFiles
    {
    NSLog(@"Share Files Pressed");

    NSString *id = [NSString stringWithString:@"some_file_id"]; NSString *entId = [NSString stringWithString:@"some_entity_id"]; SFRestRequest *request =
    [[SFRestAPI sharedInstance] requestForAddFileShare:id
    entityId:entId
    shareType:@"V"];

    SFRestRequest *request = [[SFRestAPI sharedInstance] requestForFilesSharedWithUser:nil page:0];
    }

Upload Image and Share

Let’s start with implementing functionality to upload an image to salesforce.com and share with others:

In order to implement uploading capabilities, we will implement the UIImagePickerController class to handle access to the photo library. In order to use this, we need to conform to its delegate.

  1. Include the UIImagePickerControlDelegate Protocol in your header file:
  2. @interface RootViewController : UIViewController
  3. Add this code to our uploadFile method & set the source type to UIImagePickerControllerSourceTypePhotoLibrary. (If you want to capture an image, use the following source type: UIImagePickerControllerSourceTypeCamera).
  4. UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    [self presentViewController:picker animated:YES completion:NULL];
  5. Show the PhotoLibrary display by calling the method presentViewController:
    [self presentViewController:picker animated:YES completion:NULL];
  6. Add the following Delegate methods. In the didFinishPickingMediaWithInfo, we need to convert the image into NSData and also include the compression quality. We then call the requestForUploadFile method, which will generate a request that can upload a local file to the server.
    (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
    self.imageView.image = chosenImage;
    NSData *imageData= UIImageJPEGRepresentation(chosenImage,0.0); 

    SFRestRequest *request =
    [[SFRestAPI sharedInstance] requestForUploadFile:imageData
    name:@"TestP.png"
    description:@"Share Img"
    mimeType:@"image/png"];
    [[SFRestAPI sharedInstance] send:request delegate:self];
    [picker dismissViewControllerAnimated:YES completion:NULL];

    }

    If the user cancels selection, include the following method to dismiss the Photo Library view.

    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

    [picker dismissViewControllerAnimated:YES completion:NULL];

    }

Modify UITableView in RootViewController

Next, we will modify our tableView. We want to change the cell contents to include the thumbnail as well as the name of the file. In our celLForRowAtIndexPath method, replace the contents:

//if you want to add an image to your cell, here's how
UIImage *image = [UIImage imageNamed:@"icon.png"];
cell.imageView.image = image;

// Configure the cell to show the data.
NSDictionary *obj = [dataRows objectAtIndex:indexPath.row];
cell.textLabel.text =  [obj objectForKey:@"Name"];

//this adds the arrow to the right hand side.
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

We want to obtain the file id as well as customize the cell to include the title, owner and name. We will then get the thumbnail of the shared file and display it:

// Configure the cell to show the data.
NSDictionary *obj = [dataRows objectAtIndex:indexPath.row];
NSString *fileId = obj[@"id"];
NSInteger tag = [fileId hash];

cell.textLabel.text =  obj[@"title"];
cell.detailTextLabel.text = obj[@"owner"][@"name"];
cell.tag = tag;
[self getThumbnail:fileId completeBlock:^(UIImage* thumbnailImage) {
// Cell are recycled - we don't want to set the image if the cell is showing a different file
if (cell.tag == tag) {
cell.imageView.image = thumbnailImage;
[cell setNeedsLayout];
}
}];

Step 7: Test your App

  1. Access your Force.com account. Upload a new file and create a group called “TestGroup.” Create dummy data for testing purposes and modify the data to include sharing the file within the group.
  2. Run your File Sharing iOS App in the emulator.
  3. Login to your account.
  4. Upon successful login, select “Groups.” You will see the group you created from the site.
  5. Select “Owned.” You will see the file uploaded from the website.
  6. Select “Upload.” You will be prompted to upload an image from your camera roll.

Resources

For a comprehensive set of resources, check out:

https://developer.salesforce.com/en/mobile/resources


About the Author

Jeanine Swatton develops mobile apps and web applications. She teaches iOS development and Ruby on Rails at a few universities and also works as a Software Product Manager focusing on education apps for the K-12 market. She is a member of Women in Technology International, the Society of Women Engineers as well as Computer Users Educator Organization. She is a huge advocate for STEM (Science, Technology, Engineering and Math) programs. She organizes the Women in Technology meetup and the Silicon Valley Ruby on Rails Meetup.

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *