Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

iOS5 Abstraction layer for Uploading photo to Web services.

branch: master
Entities Added Metamovics to upload service.
Libraries Added Metamovics to upload service.
Resources Solve conflict between picasa and gdata
Services Added Metamovics to upload service.
Settings Fixed bug, salesforce,Google Drive and Picasa are not concurrent.
UtilityCategories Fixed archiving error caused by RestKit.
.gitignore Added Metamovics to upload service.
.gitmodules Fixed archiving error caused by RestKit.
LICENSE Added LICENSE, described about simulator.
PSLang.h Added initial codes derivered from http://github.com/kent013/tottepost
PSLang.m Added compiler flag to detect arc/non-arc environment.
PhotoSubmitter.h Updated MixiSDK version, Added Google Drive PhotoSubmitter.
PhotoSubmitter.m Added InstagramPhotoSubmitter, Brushed up GDrivePhotoSubmitter.
PhotoSubmitterAccount.h Refactored to handle multiple account for single Service.
PhotoSubmitterAccount.m Fixed bug when unregister submitter after register and save account.
PhotoSubmitterAccountManager.h Refactored to handle multiple account for single Service.
PhotoSubmitterAccountManager.m Solve conflict between picasa and gdata
PhotoSubmitterFactory.h Refactored to handle multiple account for single Service.
PhotoSubmitterFactory.m Refactored to handle multiple account for single Service.
PhotoSubmitterManager.h Solve conflict between picasa and gdata
PhotoSubmitterManager.m Solve conflict between picasa and gdata
PhotoSubmitterOperation.h Implementing video support. mainly, adding submitVideo method to ever…
PhotoSubmitterOperation.m Refactored to handle multiple account for single Service.
PhotoSubmitterProtocol.h Added InstagramPhotoSubmitter, Brushed up GDrivePhotoSubmitter.
PhotoSubmitterSequencialOperationQueue.h Added initial codes derivered from http://github.com/kent013/tottepost
PhotoSubmitterSequencialOperationQueue.m Added compiler flag to detect arc/non-arc environment.
PhotoSubmitterSettings.h Added, Auto Enhance feature…. but its very heavy...
PhotoSubmitterSettings.m Refactored to handle multiple account for single Service.
README.md Added video feature.
strings.sh Fiexd pathes in string.sh.
README.md

PhotoSubmitter

The purpose of the PhotoSubmitter iOS class library is to facilitate the development of photo/video upload application.

There are a lot of Social Network Services and Cloud Storage Services. And each services have their own SDK to connect to their service. Unfortunately SDKs are not compatible each other. Especially between Social Network Services and Cloud Storage Services are completely different.

So, I developed PhotoSubmitter library as an abstraction layer for this situation.

ScreenShot3ScreenShot4

Upper left screenshot is tottepost, and the right one is service setting view of PhotoSubmitter.

Functionality

* Submitting photo
 - Submit photo to services
   Twitter/Facebook/Dropbox/Evernote/Picasa/Fotolife/Minus/Flickr/Mixi.
 - Submit video to services
   Facebook/Dropbox/Minus/Flickr
 - Asynchronous upload
 - Save photo to Camera roll
 - Background uploading
 - Upload resuming
 - Cancel uploading

* Settings
 - Authentication UI
   OAuth1/2 with UIWebView, Application and Safari
   Username and Password
 - Option to toggle GPS tagging
 - Option to toggle commenting
 - Option to toggle auto enhance image quality
 - Dealing with album
   Album listing
   Album selection
   Album creation

Sample Projects

PhotoSubmitter Client Code

PhotoSubmitter supports authentication with code like,

[[PhotoSubmitterManager submitterForType:@"facebook"] login];

This code will brings up Safari or Facebook app in your iPhone for authentication. You can receive messages from PhotoSubmitter while authenticating with implementing PhotoSubmitterAuthenticationDelegate.

There are a lot of supported services, Facebook, Twitter, Dropbox and so on. You can enable submitter with just calling login method.

[[PhotoSubmitterManager submitterForType:@"dropbox"] login];
[[PhotoSubmitterManager submitterForType:@"evernote"] login];

You can use type name like "dropbox" or "Dropbox" or "DropboxPhotoSubmitter". Once PhotoSubmitter is enabled and authenticated, you can submit photo to the service like this,

PhotoSubmitterImageEntity *photo = 
    [[PhotoSubmitterImageEntity alloc] initWithData:data];
[PhotoSubmitterManager submitPhoto:photo];

This code is creating photo entity and submitting photo to the authenticated services asynchronously. Also you can upload video with code,

PhotoSubmitterVideoEntity *video = 
    [[PhotoSubmitterVideoEntity alloc] initWithURL:fileurl];
[PhotoSubmitterManager submitVideo:video];

This code will upload video to configured services. You can receive messages from PhotoSubmitter while submitting photo with implementing PhotoSubmitterPhotoDelegate.

Supported Services

Below is the list of supported Social Network and Cloud Storage services.

Service Name Auth Type Requirement Upload Type Album Support Video Support
Facebook OAuth (Safari/FacebookApp) URLScheme: fb[appId] Concurrent YES YES
Twitter iOS - Sequencial*1 NO NO
Dropbox OAuth (Safari/DropboxApp) URLScheme: db-[appId] Concurrent YES YES
Flickr OAuth (Safari) URLScheme: photosubmitter Concurrent YES YES
Evernote OAuth (Safari) URLScheme: photosubmitter Concurrent YES NO
Picasa*2 OAuth (In App WebView) PhotoSubmitterAuthControllerDelegate Concurrent YES NO
Minus OAuth (In App PasswordView) PhotoSubmitterAuthControllerDelegate Concurrent YES YES
Mixi*3 OAuth (In App WebView) PhotoSubmitterAuthControllerDelegate Concurrent YES NO
Fotolife*3 BASIC (In App PasswordView) PhotoSubmitterAuthControllerDelegate Concurrent NO NO
File - - - NO YES

*1 Uploading multiple photo at same time will cause 400 error. *2 Currently Google+ does not permit write access to images. *3 Japanese services.

Custom URL schema setting is needed for Safari or App authentication. See Implementing Custom URL Schemes and Launching Your Own Application via a Custom URL Scheme for more information.

UINavigationController is needed to present built-in WebView and PasswordView. To provide UINavigationController to the PhotoSubmitter, you may implement PhotoSubmitterAuthControllerDelegate's method (UINavigationController *) requestNavigationControllerToPresentAuthenticationView in your client code.

Before using OAuth services, you must submit to their developer program to obtain API-Key and API-Secret. After you've got key and secret pair, copy PhotoSubmitter/Services/[ServiceName]PhotoSubmitter/[ServiceName]APIKey-template.h as PhotoSubmitter/Services/[ServiceName]PhotoSubmitter/[ServiceName]APIKey.h in the same directory and modify appropriate constants with your key and secret. For instance, if you want to use flickr, you may modify

#define PHOTO_SUBMITTER_FLICKR_API_KEY @""
#define PHOTO_SUBMITTER_FLICKR_API_SECRET @""

these constants with your key and secret pair.

Probrem in mixture of ARC and Non-ARC libraries

PhotoSubmitter is ARC project, and some dependent libraries are Non-ARC. Non-ARC Libraries cause build error. You must set '-fno-objc-arc' flag to their source codes to disable ARC. Please check out stackoverflow: How can I disable ARC for a single file in a project?. (Tip: You can set flag to multiple source codes at once with selecting multiple source code and pressing ENTER key)

Library Dependencies of PhotoSubmitter library

AssetsLibrary, CFNetwork, CoreLocation, Foundation, ImageIO, Security and SystemConfiguration.framework are needed to be added in your project. Also libicucore.dylib is required by RegexKitLite and libxml2.dylib is required by KissXML.

libxml2 needs "Header Search Path" to be configured as /usr/include/libxml2(or other path to libxml2's header files are placed.

And other 3rd party libraries that PhotoSubmitter depends on, please see 3RDPARTY.txt in tottepost repository.

Library Dependencies of Service Implementation

PhotoSubmitter's common libraries are stored in Libraries. And libraries for service implementation are stored in Services/[ServiceName]PhotoSubmitter/Libraries. PhotoSubmitter service implementations are designed to be work properly when they added to project independently. Because of this issue, libraries may be duplicated when you add multiple PhotoSubmitter service implementations to your project. If so, please delete all libraries duplicated, leaving only one library.

Service Name SDK Libraries
Facebook Facebook SDK -
Twitter Twitter.framework Accounts.framework
Dropbox Dropbox SDK -
Flickr ObjectiveFlickr -
Evernote EVNConnect1 -
Picasa gdata-objectivec-client -
Minus MinusConnect OAuth2Client, CoreData.framework, MobileCoreServices.framework, libz.dylib2
Mixi Mixi SDK -
Fotolife objc-atompub -
File - AssetsLibrary.framework, ImageIO.framework

*1 EvernoteConnect needs "Header Search Path" to be configured as [submodule path]/Services/EvernotePhotoSubmitter/Libraries/Evernote/thrift and recursive checked. *2 libz.dylib is not provided by MinusPhotoSubmitter please add it manually.

PhotoSubmitter SettingViewController

There are useful setting component for PhotoSubmitter. PhotoSubmitterSetting component provides comment/GPS toggle switch, PhotoSubmitter toggle switches, album listing and creating.

Source codes are stored in Settings.

ScreenShot4ScreenShot5

To add / remove services, add / remove service implementation from project. Or add all services to project and code like,

[PhotoSubmitterManager unregisterAllPhotoSubmitters];
[PhotoSubmitterManager registerPhotoSubmitterWithTypeNames:
    [NSArray arrayWithObjects: @"facebook", @"twitter", @"dropbox", 
                               @"minus", @"file", nil]];
[PhotoSubmitterManager registerPhotoSubmitterWithTypeName:@"mixi"];
[PhotoSubmitterManager unregisterPhotoSubmitterWithTypeName:@"twitter"];

Initially, all PhotoSubmitters are loaded. So before register PhotoSubmitter, unregister all PhotoSubmitter.

Implementing New PhotoSubmitter

Fast way to implement new PhotoSubmitter, you may copy existing PhotoSubmitter's source code. FacebookPhotoSubmitter is suitable for Safari or App authentication. If the service needed to present WebView, copy Mixi or Picasa. And If the service needed to present PasswordView, copy Minus or Fotolife.

Directory structure

PhotoSubmitter service implementation must obey rule of file/directory structure as below.

Services
 |-[ServiceName]PhotoSubmitter
    |- Resources
    |  |- Images                               (Service specific images)
    |  |  |- [lowercase-servicename]_16.png    (16 x 16, icon for progress)
    |  |  |- [lowercase-servicename]_16@2x.png (32 x 32, icon for progress, Retina)
    |  |  |- [lowercase-servicename]_32.png    (32 x 32, icon for setting)
    |  |  |- [lowercase-servicename]_32@2x.png (64 x 64, icon for setting, Retina)
    |  |- Localizations
    |     |- [lang].lproj                      (lang: en, ja etc)
    |         |- [ServiceName].strings         (Localized string)
    |- Libraries                               (Dependent libraries)
    |- [ServiceName]PhotoSubmitter.h
    |- [ServiceName]PhotoSubmitter.m

For example, When you going to implement PhotoSubmitter Facebook, [lowercase-servicename] is facebook, [ServiceName] is Facebook.

PhotoSubmitter Interface declaration

  • Class name must be [Hoge]PhotoSubmitter where Hoge is service name.
  • Extend PhotoSubmitter.
  • Implement PhotoSubmitterInstanceProtocol.

For example,

@interface FacebookPhotoSubmitter : 
    PhotoSubmitter<PhotoSubmitterInstanceProtocol, FBSessionDelegate, 
                   FBRequestWithUploadProgressDelegate>{
    __strong Facebook *facebook_;
}
@end

PhotoSubmitter Implementation

Call configuration method in initialize method.

[self setSubmitterIsConcurrent:YES 
                  isSequencial:NO 
                 usesOperation:YES 
               requiresNetwork:YES 
              isAlbumSupported:YES];
Configuration Name Explanation
isConcurrent indicates photo upload process uses thread.
When the flag is NO, photo upload process will called in main thread.
isSequencial indicates photo upload process uses PhotoSubmitterSequencialOperationQueue.
Flag for services not permit upload multiple photo at same time like Twitter.
usesOperation indicates use NSOperationQueue for upload process.
requireNetwork indicates the PhotoSubmitter needs network.
isAlbumSupported indicates the PhotoSubmitter implements album methods.

Implement PhotoSubmitterInstanceProtocol

Implement login process in -(void)onLogin.
This method will call when [PhotoSubmitterProtocol login] is called. For example,

-(void)onLogin{
    NSArray *permissions = 
    [NSArray arrayWithObjects:@"publish_stream", @"user_location", 
                              @"user_photos", @"offline_access", nil];
    [facebook_ authorize:permissions];
}

When login process is done, usually in the delegate method like fbDidLogin, you must call [PhotoSubmitter completeLogin].

- (void)fbDidLogin {
    [self setSetting:[facebook_ accessToken] forKey:PS_FACEBOOK_AUTH_TOKEN];
    [self setSetting:[facebook_ expirationDate] forKey:PS_FACEBOOK_AUTH_EXPIRATION_DATE];

    [self completeLogin];
    [self getUserInfomation];
}

And if login process is failed, usually in the delegate method like fbDidNotLogin, you must call [PhotoSubmitter completeLoginFailed].

-(void)fbDidNotLogin:(BOOL)cancelled {
    [self completeLoginFailed];
}

Implement logout process in -(void)onLogout
This method will call when [PhotoSubmitterProtocol logout] is called.

- (void)onLogout{
    [facebook_ logout:self];   
}

When the logout process is finished(In the delegate method, if logout process is asynchronous), you must call [PhotoSubmitter completeLogout].

- (void) fbDidLogout {
    [self completeLogout];
}

If there are no specific logout process, you must call [PhotoSubmitter completeLogout] in (void)onLogout. This method clear credentials. For example, FlickrPhotoSubmitter's onLogout is like this,

- (void)onLogout{
    [self completeLogout];
}

Implement upload photo process in -(id)onSubmitPhoto: andOperationDelegate:
This method will call when the [PhotoSubmitter submitPhoto] called. Return value of the method may not be nil (nil means upload is not started), like FBRequest, NSURLConnection or some instance represents individual request.

- (id)onSubmitPhoto:(PhotoSubmitterImageEntity *)photo 
andOperationDelegate:(id<PhotoSubmitterPhotoOperationDelegate>)delegate{
    CGSize size = CGSizeMake(PS_FACEBOOK_PHOTO_WIDTH, PS_FACEBOOK_PHOTO_HEIGHT);
    if(photo.image.size.width < photo.image.size.height){
        size = CGSizeMake(PS_FACEBOOK_PHOTO_HEIGHT, PS_FACEBOOK_PHOTO_WIDTH);
    }

    NSMutableDictionary *params = 
    [NSMutableDictionary dictionaryWithObjectsAndKeys: 
       [photo resizedImage:size], @"source", 
                   photo.comment, @"name", nil];
    NSString *path = @"me/photos";
    if(self.targetAlbum != nil){
        path = [NSString stringWithFormat:@"%@/photos", self.targetAlbum.albumId];
    }
    FBRequest *request = 
       [facebook_ requestWithGraphPath:path 
                             andParams:params 
                         andHttpMethod:@"POST" 
                           andDelegate:self];
    return request;
}

When the upload process is finished(In the delegate method, if upload process is asynchronous), you must call [PhotoSubmitter completeSubmitPhoto:(id)request]. Where request must be same object as Return value.

- (void)request:(FBRequest *)request didLoad:(id)result {
    if([request.url isMatchedByRegex:@"photos$"]){
        [self completeSubmitPhotoWithRequest:request];
    }
}

If the upload process is failed, you must call [PhotoSubmitter completeSubmitPhoto:(id)request andError:(NSError *)error].

- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
    if([request.url isMatchedByRegex:@"photos$"]){
        [self completeSubmitPhotoWithRequest:request andError:error];
    }
}

Implement cancel photo code in -(id)onCancelPhoto:(PhotoSubmitterImageEntity *)photo
This method invoked when the [PhotoSubmitter cancel] called. You can obtain request object calling [self requestForPhoto:photo.photoHash]. Return value of the method is NSURLConnection or some instance represents individual request. And may not be nil(nil means upload is not started).

- (id)onCancelPhotoSubmit:(PhotoSubmitterImageEntity *)photo{
    FBRequest *request = (FBRequest *)[self requestForPhoto:photo.photoHash];
    [request.connection cancel];
    return request;
}

Override PhotoSubmitter's method.

isSessionValid
return your submitter's authentication is valid.

- (BOOL)isSessionValid{
    if ([self settingForKey:PS_FACEBOOK_AUTH_TOKEN]) {
        return YES;
    }
    return NO;
}

License

Copyright (c) 2011, ISHITOYA Kentaro.
Copyright (c) 2011, WATANABE Ken.

New BSD License. See LICENSE file.

Something went wrong with that request. Please try again.