2011-12-30 24 views
5

有沒有人將Google文檔整合到他們的iOS應用中?通過示例代碼,Google Docs的API比我預期的要複雜得多,例子都是MacOS。是的,有iOS支持,但是關於如何使用它的示例代碼明顯缺乏,文檔有點缺乏。適用於Google文檔的iOS Objective-C包裝

我確實在網上找到了一個接口類,但它是基於一個較舊的,不贊成使用的Google Docs API版本,並且它不能用XCode 4.2進行編譯。

什麼,我追求的是一個相對簡單的界面,允許:

  1. 登錄/註銷從谷歌文檔帳戶。
  2. 獲取該帳戶(可選特定類型)中的文檔列表,可能具有導航文件夾結構的功能。
  3. 能夠將特定文檔下載到本地存儲。
  4. 能夠上傳特定文件到谷歌文檔。

我已經開始編寫這樣一個接口,但到目前爲止,它比我所允許的方式更爲複雜。如果任何人有任何建議,或樣本,他們可以指導我,我真的很感激它。

我的偏好是包裝是OS中立的;這意味着我希望能夠在MacOS和iOS中使用相同的界面。再次,這是我開始寫的,但我不禁感到,我必須在這裏重新發明輪子。

感謝

+0

+1,谷歌已經可怕的源代碼。 – CodaFi 2011-12-30 03:11:14

+0

然而,它顯然遵循風格指南... – PKCLsoft 2011-12-31 13:13:38

回答

4

OK,所以在沒有其他人的任何答案,我忍辱負重,寫了一個包裝自己。

我現在擁有適用於Mac OS和iOS的單包裝,可顯着簡化與Google文檔的界面。

下面是實際接口的所有代碼。我要指出的是,這個類作爲一個單身,你需要通過更新線略微定製每個項目:

#define GOOGLE_DATA_CLIENT_ID @"<client id>.apps.googleusercontent.com" 
#define GOOGLE_DATA_SECRET @"<google data secret>" 
#define GOOGLE_DATA_USERNAME @"googleDocsUsername" 
#define GOOGLE_DATA_PASSWORD @"googleDocsPassword" 

與您從谷歌獲得相應的值。

我還指出該類通過NSUserDefaults存儲密碼,並使用單獨的實用程序類以加密的方式執行此操作。而不是所有的額外代碼在這裏堵塞這個答案,我在創建一個版本庫中到位桶:

https://bitbucket.org/pkclsoft/gdatainterface

其中包含構建兩個目標,一個是Mac OS和一個用於整個XCode項目iOS版。我已經在應用程序商店中的應用程序中使用了這些,並取得了很好的效果。這可能是這個項目爲我建立的,你必須爲你的目的調整它。該項目包含一套完整的Google SDK,可用我的代碼構建和運行。我將它包括在內,試圖降低與新版SDK不兼容的風險,以便任何人抓住它。

這裏的接口規範,因爲它目前爲:

// 
// GDataInterface.h 
// GDataInterface 
// 
// Some of the code in this class is from the original GData sample code, but it has been 
// enhanced somewhat and made to work on both iOS and MacOS transparently. 
// 
// Created by Peter Easdown on 19/12/11. 
// Copyright (c) 2011 PKCLsoft. All rights reserved. 
// 

#import <Foundation/Foundation.h> 
#if TARGET_OS_IPHONE 
#import "GDataDocs.h" 
#import <UIKit/UIKit.h> 
#else 
#import "GData/GData.h" 
#endif 

@interface GDataInterfaceTypes 

// This handler is used by methods that have no explicit result. The boolean value indicates 
// the success or failure of the the methods action. 
// 
typedef void (^CompletionHandler)(BOOL successful); 

// This handler is called to update a progress indicator as a file is uploaded. 
// 
typedef void (^UploadProgressHandler)(double min, double max, double value); 

// This handler is called to update a progress indicator as a file is downloaded. 
// 
typedef void (^DownloadProgressHandler)(double min, double max, double value); 

@end 

@interface GDataInterface : NSObject { 

#if TARGET_OS_IPHONE 
    // Needed so that when authenticating under iOS, we can push the google authentication 
    // view, and later pop it. 
    // 
    UIViewController *rootController_; 
#endif 

    GDataFeedDocList *mDocListFeed; 
    GDataServiceTicket *mDocListFetchTicket; 
    NSError *mDocListFetchError; 

    GDataFeedDocRevision *mRevisionFeed; 
    GDataServiceTicket *mRevisionFetchTicket; 
    NSError *mRevisionFetchError; 

    GDataEntryDocListMetadata *mMetadataEntry; 

    GDataServiceTicket *mUploadTicket; 

    id uploadWindow; 
    CompletionHandler uploadCompletionHandler; 

    NSString *username_; 
    NSString *password_; 

} 

// This handler is used when a list of documents has been requested. The results parameter 
// will be nil if the request failed. If successful, then it will contain an array of 
// GDataEntryDocBase objects. 
// 
typedef void (^RetrievalCompletionHandler)(GDataFeedDocList* results, BOOL successful); 

// This handler is used when a document has been downloaded. If something prevented the 
// download from succeeding, then error parameter will be non-nil. 
// 
typedef void (^DocumentDownloadCompletionHandler)(NSData* fileContents, BOOL successful); 

// Initializer that provides the username and password. 
// 
- (id) initWithUsername:(NSString*)username andPassword:(NSString*)password; 

// Returns the shared instance of the class. There will only ever be a single instance 
// of this class. 
// 
+ (GDataInterface*) sharedInstance; 

// Returns YES if currently signed in. 
// 
- (BOOL) isSignedIn; 

// Signs in or out depending on current state, and executes the options completion handler 
// block. The window parameter is used to specify the root viewController object used when 
// displaying login windows via GData, or error dialogs. 
// 
- (void) signInOrOutWithCompletionHandler:(CompletionHandler)handler forWindow:(id)window; 

// Will retrieve a list of documents using the cached connection, and call the specified 
// handler block, providing the list of documents, and a success/fail indication. 
// 
- (void) retrieveDocumentListWithCompletionHandler:(RetrievalCompletionHandler)handler; 

// Will download the file at the specified URL. This is not Google Docs specific and will work 
// for any URL. Be careful not to try and retrieve large files and the result is stored 
// in memory. 
// 
- (void) downloadURL:(NSURL*)url withProgressHandler:(DownloadProgressHandler)progressHandler andCompletionHandler:(DocumentDownloadCompletionHandler)handler; 

// Will download the specified google docs document. 
// 
- (void) downloadDocument:(GDataEntryDocBase*)document withProgressHandler:(DownloadProgressHandler)progressHandler andCompletionHandler:(DocumentDownloadCompletionHandler)handler; 

// Uploads the document entry, optionally updating it with a new revision. 
// 
- (void) uploadEntry:(GDataEntryDocBase*)docEntry asNewRevision:(BOOL)newRevision forWindow:(id)window withProgressHandler:(UploadProgressHandler)progressHandler andCompletionHandler:(CompletionHandler)handler; 

// Uploads the specified file to the authenticated google docs account. 
// 
- (void)uploadFileAtPath:(NSString *)path forWindow:(id)window withProgressHandler:(UploadProgressHandler)progressHandler andCompletionHandler:(CompletionHandler)handler; 

// More for internal use than anything else. Used to determine the mime type based on the google docs class 
// and/or file extension. 
// 
- (void)getMIMEType:(NSString **)mimeType andEntryClass:(Class *)class forExtension:(NSString *)extension; 

// Getter and Setter for username, 
// 
- (void) setUsername:(NSString*)newUsername; 
- (NSString*) username; 

// Getter and Setter for password. The password will be encrypted before storing it in user preferances. 
// 
- (void) setPassword:(NSString*)newPassword; 
- (NSString*) password; 

// Returns the username that google is given for signing in. 
// 
- (NSString *)signedInUsername; 

// Returns a static instance of the docs service. 
// 
+ (GDataServiceGoogleDocs *)docsService; 

@end 

這裏是實現:

// 
// GDataInterface.m 
// GDataInterface 
// 
// Created by Peter Easdown on 19/12/11. 
// Copyright (c) 2011 PKCLsoft. All rights reserved. 
// 

#import "GDataInterface.h" 
#import "Util.h" 
#if TARGET_OS_IPHONE 
#import "GTMOAuth2ViewControllerTouch.h" 
#import "GData.h" 
#else 
#import "GData/GTMOAuth2WindowController.h" 
//#import "GDataServiceGoogleSpreadsheet.h" 
#endif 

#define GOOGLE_DATA_CLIENT_ID @"<client id>.apps.googleusercontent.com" 
#define GOOGLE_DATA_SECRET @"<google data secret>" 
#define GOOGLE_DATA_USERNAME @"googleDocsUsername" 
#define GOOGLE_DATA_PASSWORD @"googleDocsPassword" 

@interface GDataInterface (PrivateMethods) 

- (GDataServiceTicket *) uploadTicket; 
- (void) setUploadTicket:(GDataServiceTicket *)ticket; 

- (GDataFeedDocList *)docListFeed; 
- (void)setDocListFeed:(GDataFeedDocList *)feed; 
- (NSError *)docListFetchError; 
- (void)setDocListFetchError:(NSError *)error; 
- (GDataServiceTicket *)docListFetchTicket; 
- (void)setDocListFetchTicket:(GDataServiceTicket *)ticket; 

@end 

@implementation GDataInterface 

static NSString *const kKeychainItemName = @"GDataInterface: Google Docs"; 

// Initializer that provides the username and password. 
// 
- (id) initWithUsername:(NSString*)username andPassword:(NSString*)password { 
    self = [super init]; 

    if (self != nil) { 
     username_ = [username retain]; 
     password_ = [password retain]; 
     [[GDataInterface docsService] setUserCredentialsWithUsername:username_ password:password_]; 
    } 

    return self; 
} 

- (void) setUsername:(NSString*)newUsername { 
    username_ = [newUsername retain]; 
    [[GDataInterface docsService] setUserCredentialsWithUsername:newUsername password:password_]; 
} 

- (NSString*) username { 
    return username_; 
} 

- (void) setPassword:(NSString*)newPassword { 
    password_ = [newPassword retain]; 
    [[GDataInterface docsService] setUserCredentialsWithUsername:username_ password:newPassword]; 
    [Util setPassword:newPassword forKey:GOOGLE_DATA_PASSWORD]; 
} 

- (NSString*) password { 
    return password_; 
} 


static GDataInterface *shared_instance_; 

// Returns the shared instance of the class. There will only ever be a single instance 
// of this class. 
// 
+ (GDataInterface*) sharedInstance { 
    if (shared_instance_ == nil) { 
     shared_instance_ = [[GDataInterface alloc] initWithUsername:[[NSUserDefaults standardUserDefaults] valueForKey:GOOGLE_DATA_USERNAME] andPassword:[Util getPassword:GOOGLE_DATA_PASSWORD]]; 

     // Load the OAuth token from the keychain, if it was previously saved 
     NSString *clientID = GOOGLE_DATA_CLIENT_ID; 
     NSString *clientSecret = GOOGLE_DATA_SECRET; 

     GTMOAuth2Authentication *auth; 

#if TARGET_OS_IPHONE 
     auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName clientID:clientID clientSecret:clientSecret]; 
#else 
     auth = [GTMOAuth2WindowController authForGoogleFromKeychainForName:kKeychainItemName 
                    clientID:clientID 
                   clientSecret:clientSecret]; 
#endif 

     [[GDataInterface docsService] setAuthorizer:auth]; 
    } 

    return shared_instance_; 
} 

- (NSString *)signedInUsername { 
    // Get the email address of the signed-in user 
    GTMOAuth2Authentication *auth = [[GDataInterface docsService] authorizer]; 
    BOOL isSignedIn = auth.canAuthorize; 

    if (isSignedIn) { 
     return auth.userEmail; 
    } else { 
     return nil; 
    } 
} 

- (BOOL) isSignedIn { 
    return ([self signedInUsername] != nil); 
} 

- (void)runSigninThenInvokeHandler:(CompletionHandler)handler forWindow:(id)window { 
    // Applications should have client ID and client secret strings 
    // hardcoded into the source, but the sample application asks the 
    // developer for the strings 
    NSString *clientID = GOOGLE_DATA_CLIENT_ID; 
    NSString *clientSecret = GOOGLE_DATA_SECRET; 

    // Show the OAuth 2 sign-in controller 
    NSString *scope = [GTMOAuth2Authentication scopeWithStrings: 
         [GDataServiceGoogleDocs authorizationScope], 
         [GDataServiceGoogleSpreadsheet authorizationScope], 
         nil]; 

#if TARGET_OS_IPHONE 
    NSAssert((window != nil), @"window must be a non-nil navigation controller"); 

    GTMOAuth2ViewControllerTouch *viewController; 
    viewController = [GTMOAuth2ViewControllerTouch 
         controllerWithScope:scope 
         clientID:clientID 
         clientSecret:clientSecret 
         keychainItemName:kKeychainItemName 
         completionHandler:^(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error) { 

          [rootController_ dismissModalViewControllerAnimated:YES]; 
          [rootController_ release]; 
          rootController_ = nil; 

          // callback 
          if (error == nil) { 
           [[GDataInterface docsService] setAuthorizer:auth]; 

           username_ = [self signedInUsername]; 

           handler(YES); 
          } else { 
           NSLog(@"Authentication error: %@", error); 
           NSData *responseData = [[error userInfo] objectForKey:@"data"]; // kGTMHTTPFetcherStatusDataKey 
           if ([responseData length] > 0) { 
            // show the body of the server's authentication failure response 
            NSString *str = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease]; 
            NSLog(@"%@", str); 
           } 
           handler(NO); 
          } 
         }]; 

    // Optional: display some html briefly before the sign-in page loads 
    NSString *html = @"<html><body bgcolor=silver><div align=center>Loading sign-in page...</div></body></html>"; 
    viewController.initialHTMLString = html; 

    // For iOS, window is a navigation controller. 
    // 
    rootController_ = [(UIViewController*)window retain]; 
    [rootController_ presentModalViewController:viewController animated:YES]; 

#else 
    NSBundle *frameworkBundle = [NSBundle bundleForClass:[GTMOAuth2WindowController class]]; 
    GTMOAuth2WindowController *windowController; 
    windowController = [GTMOAuth2WindowController controllerWithScope:scope 
                  clientID:clientID 
                 clientSecret:clientSecret 
                keychainItemName:kKeychainItemName 
                 resourceBundle:frameworkBundle]; 

    [windowController signInSheetModalForWindow:window 
           completionHandler:^(GTMOAuth2Authentication *auth, NSError *error) { 
            // callback 
            if (error == nil) { 
             [[GDataInterface docsService] setAuthorizer:auth]; 
             username_ = [auth userEmail]; 
             handler(YES); 
            } else { 
             handler(NO); 
            } 
           }]; 
#endif 
} 

- (void) signInOrOutWithCompletionHandler:(CompletionHandler)handler forWindow:(id)window { 
    if (![self isSignedIn]) { 
     // Sign in 
     [self runSigninThenInvokeHandler:handler forWindow:window]; 
    } else { 
     // Sign out 
     GDataServiceGoogleDocs *service = [GDataInterface docsService]; 

#if TARGET_OS_IPHONE 
     [GTMOAuth2ViewControllerTouch removeAuthFromKeychainForName:kKeychainItemName]; 
#else 
     [GTMOAuth2WindowController removeAuthFromKeychainForName:kKeychainItemName]; 
#endif 

     [service setAuthorizer:nil]; 
     handler(YES); 
    } 
} 

- (void) retrieveDocumentListWithCompletionHandler:(RetrievalCompletionHandler)handler { 

    [self setDocListFeed:nil]; 
    [self setDocListFetchError:nil]; 
    [self setDocListFetchTicket:nil]; 

    GDataServiceGoogleDocs *service = [GDataInterface docsService]; 
    GDataServiceTicket *ticket; 

    // Fetching a feed gives us 25 responses by default. We need to use 
    // the feed's "next" link to get any more responses. If we want more than 25 
    // at a time, instead of calling fetchDocsFeedWithURL, we can create a 
    // GDataQueryDocs object, as shown here. 

    NSURL *feedURL = [GDataServiceGoogleDocs docsFeedURL]; 

    GDataQueryDocs *query = [GDataQueryDocs documentQueryWithFeedURL:feedURL]; 
    [query setMaxResults:1000]; 
    [query setShouldShowFolders:NO]; 

    ticket = [service fetchFeedWithQuery:query 
         completionHandler:^(GDataServiceTicket *ticket, GDataFeedBase *feed, NSError *error) { 
          // callback 
          [self setDocListFeed:(GDataFeedDocList *)feed]; 
          [self setDocListFetchError:error]; 
          [self setDocListFetchTicket:nil]; 

          if (handler != nil) { 
           handler((GDataFeedDocList *)feed, (error == nil)); 
          } 
         }]; 

    [self setDocListFetchTicket:ticket]; 
} 

- (void) downloadDocument:(GDataEntryDocBase*)document withProgressHandler:(DownloadProgressHandler)progressHandler andCompletionHandler:(DocumentDownloadCompletionHandler)handler { 

    // the content src attribute is used for downloading 
    NSURL *exportURL = [[document content] sourceURL]; 

    if (exportURL != nil) { 
     GDataQuery *query = [GDataQuery queryWithFeedURL:exportURL]; 
     [query addCustomParameterWithName:@"exportFormat" 
            value:@"txt"]; 
     NSURL *downloadURL = [query URL]; 
     // Read the document's contents asynchronously from the network 

     // requestForURL:ETag:httpMethod: sets the user agent header of the 
     // request and, when using ClientLogin, adds the authorization header 
     NSURLRequest *request = [[GDataInterface docsService] requestForURL:downloadURL 
                     ETag:nil 
                   httpMethod:nil]; 

     GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request]; 
     [fetcher setAuthorizer:[[GDataInterface docsService] authorizer]]; 

     __block double maxSize = 10240.0; 

     if (progressHandler != nil) {    
      [fetcher setReceivedDataBlock:^(NSData *dataReceivedSoFar) { 
       if ([[fetcher response] expectedContentLength] > 0) { 
        maxSize = [[fetcher response] expectedContentLength]; 
       } else if ([dataReceivedSoFar length] > maxSize) { 
        maxSize += 5120.0; 
       } 

       progressHandler(0.0, maxSize, (double)[dataReceivedSoFar length]); 
      }]; 
     } 

     [fetcher setCommentWithFormat:@"downloading \"%@\"", [[document title] stringValue]]; 
     [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { 
      if (progressHandler != nil) { 
       // Update the progress handler with a "complete" progress. 
       // 
       progressHandler(0.0, (double)[data length], (double)[data length]); 
      } 

      // callback 
      if (error == nil) { 
       // Successfully downloaded the document 
       //     
       if (handler != nil) { 
        handler(data, YES); 
       } 
      } else { 
       if (handler != nil) { 
        handler(nil, NO); 
       } 
      } 
     }]; 
    } 
} 

- (void) downloadURL:(NSURL*)url withProgressHandler:(DownloadProgressHandler)progressHandler andCompletionHandler:(DocumentDownloadCompletionHandler)handler { 

    NSURL *downloadURL = [url copy]; 
    // Read the document's contents asynchronously from the network 

    NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; 

    GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request]; 

    __block double maxSize = 10240.0; 

    if (progressHandler != nil) {    
     [fetcher setReceivedDataBlock:^(NSData *dataReceivedSoFar) { 
      if ([[fetcher response] expectedContentLength] > 0) { 
       maxSize = [[fetcher response] expectedContentLength]; 
      } else if ([dataReceivedSoFar length] > maxSize) { 
       maxSize += 5120.0; 
      } 

      progressHandler(0.0, maxSize, (double)[dataReceivedSoFar length]); 
     }]; 
    } 

    [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { 
     if (progressHandler != nil) { 
      progressHandler(0.0, (double)[data length], (double)[data length]); 
     } 

     // callback 
     if (error == nil) { 
      // Successfully downloaded the document 
      //     
      if (handler != nil) { 
       handler(data, YES); 
      } 
     } else { 
      if (handler != nil) { 
       handler(nil, NO); 
      } 
     } 
    }]; 

    // Block, waiting for 60 seconds for the download. 
    // 
    [fetcher waitForCompletionWithTimeout:60.0]; 

    if ([fetcher isFetching] == YES) { 
     // OK, so this looks like we've timed out waiting for the download to complete. Cancel the 
     // fetch. 
     // 
     [fetcher stopFetching]; 

     if (handler != nil) { 
      handler(nil, NO); 
     } 
    } 
} 

#pragma mark Upload 

- (void)getMIMEType:(NSString **)mimeType andEntryClass:(Class *)class forExtension:(NSString *)extension { 

    // Mac OS X's UTI database doesn't know MIME types for .doc and .xls 
    // so GDataEntryBase's MIMETypeForFileAtPath method isn't helpful here 

    struct MapEntry { 
     NSString *extension; 
     NSString *mimeType; 
     NSString *className; 
    }; 

    static struct MapEntry sMap[] = { 
     { @"csv", @"text/csv", @"GDataEntryStandardDoc" }, 
     { @"doc", @"application/msword", @"GDataEntryStandardDoc" }, 
     { @"docx", @"application/vnd.openxmlformats-officedocument.wordprocessingml.document", @"GDataEntryStandardDoc" }, 
     { @"ods", @"application/vnd.oasis.opendocument.spreadsheet", @"GDataEntrySpreadsheetDoc" }, 
     { @"odt", @"application/vnd.oasis.opendocument.text", @"GDataEntryStandardDoc" }, 
     { @"pps", @"application/vnd.ms-powerpoint", @"GDataEntryPresentationDoc" }, 
     { @"ppt", @"application/vnd.ms-powerpoint", @"GDataEntryPresentationDoc" }, 
     { @"rtf", @"application/rtf", @"GDataEntryStandardDoc" }, 
     { @"sxw", @"application/vnd.sun.xml.writer", @"GDataEntryStandardDoc" }, 
     { @"txt", @"text/plain", @"GDataEntryStandardDoc" }, 
     { @"xls", @"application/vnd.ms-excel", @"GDataEntrySpreadsheetDoc" }, 
     { @"xlsx", @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", @"GDataEntrySpreadsheetDoc" }, 
     { @"jpg", @"image/jpeg", @"GDataEntryStandardDoc" }, 
     { @"jpeg", @"image/jpeg", @"GDataEntryStandardDoc" }, 
     { @"png", @"image/png", @"GDataEntryStandardDoc" }, 
     { @"bmp", @"image/bmp", @"GDataEntryStandardDoc" }, 
     { @"gif", @"image/gif", @"GDataEntryStandardDoc" }, 
     { @"html", @"text/html", @"GDataEntryStandardDoc" }, 
     { @"htm", @"text/html", @"GDataEntryStandardDoc" }, 
     { @"tsv", @"text/tab-separated-values", @"GDataEntryStandardDoc" }, 
     { @"tab", @"text/tab-separated-values", @"GDataEntryStandardDoc" }, 
     { @"pdf", @"application/pdf", @"GDataEntryPDFDoc" }, 
     { nil, nil, nil } 
    }; 

    NSString *lowerExtn = [extension lowercaseString]; 

    for (int idx = 0; sMap[idx].extension != nil; idx++) { 
     if ([lowerExtn isEqual:sMap[idx].extension]) { 
      *mimeType = sMap[idx].mimeType; 
      *class = NSClassFromString(sMap[idx].className); 
      return; 
     } 
    } 

    *mimeType = nil; 
    *class = nil; 
    return; 
} 

- (void) uploadEntry:(GDataEntryDocBase*)docEntry asNewRevision:(BOOL)newRevision forWindow:(id)window withProgressHandler:(UploadProgressHandler)progressHandler andCompletionHandler:(CompletionHandler)handler { 

    uploadWindow = [window retain]; 
    uploadCompletionHandler = [handler copy]; 

    NSURL *uploadURL; 

    if (newRevision == YES) { 
     GDataQueryDocs *query = [GDataQueryDocs queryWithFeedURL:[[docEntry 
                    uploadEditLink] URL]]; 
     [query setShouldCreateNewRevision:YES]; 
     uploadURL = [query URL]; 
    } else { 
     uploadURL = [GDataServiceGoogleDocs docsUploadURL]; 
    } 

    // make service tickets call back into our upload progress selector 
    GDataServiceGoogleDocs *service = [GDataInterface docsService]; 
    [service setServiceUploadProgressHandler:^(GDataServiceTicketBase *ticket, unsigned long long numberOfBytesRead, unsigned long long dataLength) { 
     if (progressHandler != nil) { 
      progressHandler(0.0, (double)dataLength, (double)numberOfBytesRead); 
     } 
    }]; 

    // insert the entry into the docList feed 
    // 
    // to update (replace) an existing entry by uploading a new file, 
    // use the fetchEntryByUpdatingEntry:forEntryURL: with the URL from 
    // the entry's uploadEditLink 
    GDataServiceTicket *ticket; 

    if (newRevision == YES) {   
     ticket = [service fetchEntryByUpdatingEntry:docEntry 
             forEntryURL:uploadURL 
              delegate:self 
            didFinishSelector:@selector(uploadFileTicket:finishedWithEntry:error:)]; 
    } else { 
     ticket = [service fetchEntryByInsertingEntry:docEntry 
              forFeedURL:uploadURL 
              delegate:self 
            didFinishSelector:@selector(uploadFileTicket:finishedWithEntry:error:)]; 
    } 

    [ticket setUploadProgressHandler:^(GDataServiceTicketBase *ticket, unsigned long long numberOfBytesRead, unsigned long long dataLength) { 
     // progress callback 
     if (progressHandler != nil) { 
      progressHandler(0.0, (double)dataLength, (double)numberOfBytesRead); 
     } 
    }]; 

    // we turned automatic retry on when we allocated the service, but we 
    // could also turn it on just for this ticket 

    [self setUploadTicket:ticket]; 
    [service setServiceUploadProgressHandler:nil]; 
} 

- (void)uploadFileAtPath:(NSString *)path forWindow:(id)window withProgressHandler:(UploadProgressHandler)progressHandler andCompletionHandler:(CompletionHandler)handler { 

    NSString *errorMsg = nil; 

    // make a new entry for the file 

    NSString *mimeType = nil; 
    Class entryClass = nil; 

    NSString *extn = [path pathExtension]; 
    [self getMIMEType:&mimeType andEntryClass:&entryClass forExtension:extn]; 

    if (!mimeType) { 
     // for other file types, see if we can get the type from the Mac OS 
     // and use a generic file document entry class 
     mimeType = [GDataUtilities MIMETypeForFileAtPath:path 
             defaultMIMEType:nil]; 
     entryClass = [GDataEntryFileDoc class]; 
    } 

    if (mimeType && entryClass) { 

     GDataEntryDocBase *newEntry = [entryClass documentEnt 
+0

嗨,有沒有任何引用或URL,我可以得到更多關於實施的信息?我也需要這樣的包裝或SDK從iOS的Google文檔。 Thx – SkyEagle888 2012-03-29 13:29:43

+0

我必須重新測試MacOS的一面,但除此之外它運行得非常好。我會把它打磨一下,放在github上。 – PKCLsoft 2012-03-29 22:02:55

+0

我對此也很感興趣**! – lnafziger 2012-04-04 22:42:03