F1 API Static Library with Objective-c

Posted By: Chad Meyer

At Dynamic Church 10 Conference I had the opportunity to collaborate with some awesome developers / peeps from around the globe in the church market. Our conversations focused on using objective-c to communicate with the FellowshipOne API in order to build native iPhone applications.

Building an OAuth wrapper no matter what language is not trivial. Remembering all the business rules and connecting all parts together to make properly signed requests can take some time to perfect. Other avenues would be integrating with other third party libraries in hopes that everything their code does works for all scenarios and that it will pass Apples submission tests. Not to mention the fact that iPhone does not take advantage of memory management so the developer is responsible for properly allocating and releasing objects.

After some thought and research, we at Fellowship Technologies decided it would be slick to produce our own static library for objective-c. The purpose of the library is to get out of the developer’s way of spending countless hours implementing OAuth to access the Fellowship One API. It is our hope to take that complexity away so the developer can focus on their job, building great iPhone applications for their ministry.

A Little Tour

The library will come with frameworks that will work both with the iPhone simulator and the iPhone device itself along with header files for each exposed resource from FT. Given the following header file for an FTAddress:

#import 

@class FTError;
@class FTOAuth;
@class PagedEntity;
@class FTParentObject;
@class FTParentNamedObject;

@protocol FTAddressDelegate;

@interface FTAddress : NSObject  {

NSString *url;
NSInteger myId;
FTParentObject *household;
FTParentObject *person;
NSString *street1;
NSString *street2;
NSString *street3;
NSString *city;
NSString *state;
NSString *postalCode;
NSString *county;
NSString *country;
NSString *comment;
NSString *carrierRoute;
NSString *deliveryPoint;
NSDate *addressDate;
BOOL uspsVerified;
NSDate *addressVerifiedDate;
NSDate *lastVerificationAttemptDate;
NSDate *createdDate;
NSDate *lastUpdatedDate;
FTParentNamedObject *addressType;

@private FTOAuth *oauth;
@private id _delegate;
@private NSDictionary *_serializationMapper;
}

@property (nonatomic, assign)	id delegate;
@property (nonatomic, copy) NSString *url;
@property (nonatomic, assign) NSInteger myId;
@property (nonatomic, retain) FTParentObject *household;
@property (nonatomic, retain) FTParentObject *person;
@property (nonatomic, copy) NSString *street1;
@property (nonatomic, copy) NSString *street2;
@property (nonatomic, copy) NSString *street3;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *state;
@property (nonatomic, copy) NSString *postalCode;
@property (nonatomic, copy) NSString *county;
@property (nonatomic, copy) NSString *country;
@property (nonatomic, copy) NSString *comment;
@property (nonatomic, copy) NSString *carrierRoute;
@property (nonatomic, copy) NSString *deliveryPoint;
@property (nonatomic, retain) NSDate *addressDate;
@property (nonatomic, assign) BOOL uspsVerified;
@property (nonatomic, retain) NSDate *addressVerifiedDate;
@property (nonatomic, retain) NSDate *lastVerificationAttemptDate;
@property (nonatomic, retain) NSDate *createdDate;
@property (nonatomic, retain) NSDate *lastUpdatedDate;
@property (nonatomic, retain) FTParentNamedObject *addressType;

/* maps the properties in this class to the required properties and order from an API request. 
This is needed for when the object is saved since the xsd requires a certain order for all fields */
@property (nonatomic, readonly, assign) NSDictionary *serializationMapper;

/* Convienence property for getting the address into a google map URL. */
@property (nonatomic, readonly) NSString *googleMapURL;

/* Convienence property for formatting the address into the typical USPS format */
@property (nonatomic, readonly) NSString *formattedAddress;

/* Gets all the addresses associated with a specific person id -- Thie method is performed asynchronously -- */
- (void) getByPersonID: (NSInteger) personID;

/* Gets all the addresses associated with a specific person id -- Thie method is performed synchronously -- */
+ (NSArray *) getByPersonIDSynchronously: (NSInteger) personID;

/* Gets an FT Address from the F1 API based on the provided address id -- This method is performed asynchronously -- */
- (void) getByAddressID: (NSInteger) addressID;

/* Gets an FT Address from the F1 API based on the provided address id -- This method is performed synchronously -- */
+ (FTAddress *) getByAddressIDSynchronously: (NSInteger) addressID;

/* populates an FTAddress object from a NSDictionary */
+ (FTAddress *)populateFromDictionary: (NSDictionary *)dict;

/* Saves an FTAddress. If the object has an id, it assumes its an update, if no id exists, it will attempt to create the object into the API */
- (void) save;

/* Saves an FTAddress. If the object has an id, it assumes its an update, if no id exists, it will attempt to create the object into the API */
- (void) saveSynchronously;

@end

@protocol FTAddressDelegate

// This method is required to consume the delegate. If an address fails, an error will be returned
- (void) FTAddressFail: (FTError *)error;

@optional
// Implement for when multiple addresses are returned
- (void) FTAddressesReturned: (PagedEntity *)addresses;

// Implement for when one address is returned
- (void) FTAddressReturned: (FTAddress *)address;

@end

You will notice that the file will provide all properties that are being returned from the API for the address along with some convenience methods on how to interact with a specific address. Every convenience method will have the ability to be called synchronously and asynchronously. The reason for this is because some requests could take up to 5 to 10 seconds depending on the user’s carrier network. We wanted to make sure that the user experience of any iPhone application could be seamless so asynchronous calls allows for some kind of waiting animation or the application could continue to work while the request is being made.

Let’s give a real life scenario. Let’s say I wanted to change the city where a person lived. Using the library, I can quickly get the address, change a couple values and update it.

SaveFTAddress.m

// Make sure to import the FTAddress.h file
#import "FTAddress.h"

@implementation SaveFTAddress

- (void) updateAddress {
    // It is assumed that the address id that needs to be updated is known
    FTAddress *currentAddress = [[FTAddress getByAddressIDSynchronously:123] retain];

    // Set the delegate of the address This is only needed if methods will be called asynchronously
    [currentAddress setDelegate:self];

    // Change the city of the address
    [currentAddress setCity:@"Irving"];

    // Save the address
    [currentAddress saveSynchronously];

   // Release the object
   [currentAddress release];
}

@end

That is all that needs to happen in order to successfully get an address and then update it. This metaphor will be used with all exposed F1 API resources. Hopefully by now you can see the power of simplicity of communicating with the F1 API through this library. We can do this for any resource so I can very easily say

FTPerson *ftPerson = [[FTPerson alloc] initWithDelegate:self];
[ftPerson searchForPeople:@"meyer" withSearchIncludes:nil withPage:1];
[ftPerson release];

This will return a PagedEntity with the results of the search.

It is our hope that the library will be available for download shortly. We are in the process of converting F1Touch to use this library. Once it is complete it will be shipped to Apple for verification. We want to make sure that the library will pass Apples tests before exposing it to the community.

Chad Meyer is a Technical Lead for Fellowship Technologies. His programming passions lie in iPhone/iPad development along with enabling developers to work smarter, not harder. He also plays rockstar for a Dallas rock band.

Posted In: Tips,

Comments:
No one has commented yet. Be the first!
Commenting is not available in this channel entry.

Categories:

Previous Posts:


Subscribe to the RSS feed!