Google Objective-C API 'GTL' with Swift
I eventually managed to do this by following
Stack OverFlow 11370752
and adding a bridging-header.h file with
#import "GTLDrive.h"
#import "GTMOAuth2ViewControllerTouch.h"
To save someone time I include my Objective-C to Swift Translation of the sample included in the Google Quickstart
IOS Quickstart for Google Drive
import UIKit
import MobileCoreServices
class ViewController: UIViewController , UINavigationControllerDelegate ,UIImagePickerControllerDelegate {
var window: UIWindow?
let driveService : GTLServiceDrive = GTLServiceDrive()
let kKeychainItemName : NSString = "Google Drive Quickstart"
let kClientID : NSString = "Your Client ID"
let kClientSecret : NSString = "Your Secret"
func showWaitIndicator(title:String) -> UIAlertView {
// println("showWaitIndicator \(title)")
var progressAlert = UIAlertView()
progressAlert.title = title
progressAlert.message = "Please Wait...."
progressAlert.show()
let activityView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
activityView.center = CGPointMake(progressAlert.bounds.size.width / 2, progressAlert.bounds.size.height - 45)
progressAlert.addSubview(activityView)
activityView.hidesWhenStopped = true
activityView.startAnimating()
return progressAlert
}
override func viewDidLoad() {
super.viewDidLoad()
self.driveService.authorizer = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(kKeychainItemName,
clientID: kClientID,
clientSecret: kClientSecret)
}
override func viewDidAppear(animated: Bool) {
self.showCamera()
}
func showCamera() {
var cameraUI = UIImagePickerController()
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) {
cameraUI.sourceType = UIImagePickerControllerSourceType.Camera
} else {
cameraUI.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
self.showAlert("Error", message: "Ipad Simulator not supported")
return
}
}
cameraUI.mediaTypes = [kUTTypeImage as String]
cameraUI.allowsEditing = true
cameraUI.delegate = self
self.presentModalViewController(cameraUI, animated: true)
println("Show Camera \(self.isAuthorized())")
if (!self.isAuthorized())
{
// Not yet authorized, request authorization and push the login UI onto the navigation stack.
cameraUI.pushViewController(self.createAuthController(), animated:true);
}
}
// Handle selection of an image
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info:NSDictionary) {
println("imagePickerController didFinishPickingMediaWithInfo")
let image = info.valueForKey(UIImagePickerControllerOriginalImage) as UIImage
self.dismissModalViewControllerAnimated(true)
self.uploadPhoto(image)
}
// Handle cancel from image picker/camera.
func imagePickerControllerDidCancel(picker: UIImagePickerController){
self.dismissModalViewControllerAnimated(true)
}
// Helper to check if user is authorized
func isAuthorized() -> Bool {
return (self.driveService.authorizer as GTMOAuth2Authentication).canAuthorize
}
// Creates the auth controller for authorizing access to Google Drive.
func createAuthController() -> GTMOAuth2ViewControllerTouch {
return GTMOAuth2ViewControllerTouch(scope: kGTLAuthScopeDriveFile,
clientID: kClientID,
clientSecret: kClientSecret,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: Selector("viewController:finishedWithAuth:error:"))
}
// “func join(string s1: String, toString s2: String, withJoiner joiner: String)”
// Handle completion of the authorization process, and updates the Drive service
// with the new credentials.
func viewController(viewController: GTMOAuth2ViewControllerTouch , finishedWithAuth authResult: GTMOAuth2Authentication , error:NSError ) {
if error != nil
{
self.showAlert("Authentication Error", message:error.localizedDescription)
self.driveService.authorizer = nil
} else {
println("Authentication success")
self.driveService.authorizer = authResult
}
}
// Uploads a photo to Google Drive
func uploadPhoto(image: UIImage) {
println("uploading Photo")
let dateFormat = NSDateFormatter()
dateFormat.dateFormat = "'Quickstart Uploaded File ('EEEE MMMM d, YYYY h:mm a, zzz')"
let file = GTLDriveFile.object() as GTLDriveFile
file.title = dateFormat.stringFromDate(NSDate())
file.descriptionProperty = "Uploaded from Google Drive IOS"
file.mimeType = "image/png"
let data = UIImagePNGRepresentation(image)
let uploadParameters = GTLUploadParameters(data: data, MIMEType: file.mimeType)
let query = GTLQueryDrive.queryForFilesInsertWithObject(file, uploadParameters: uploadParameters) as GTLQueryDrive
let waitIndicator = self.showWaitIndicator("Uploading To Google Drive")
// self.driveService.executeQuery(query, completionHandler: {(ticket: GTLServiceTicket, insertedFile: AnyObject, error: NSError) in {
//
//
// }
// elf.driveService.executeQuery(<#query: GTLQueryProtocol?#>, completionHandler: <#((GTLServiceTicket!, AnyObject!, NSError!) -> Void)?#>)
self.driveService.executeQuery(query, completionHandler: { (ticket, insertedFile , error) -> Void in
let myFile = insertedFile as? GTLDriveFile
waitIndicator.dismissWithClickedButtonIndex(0, animated: true)
if error == nil {
println("File ID \(myFile?.identifier)")
self.showAlert("Google Drive", message: "File Saved")
} else {
println("An Error Occurred! \(error)")
self.showAlert("Google Drive", message: "Sorry, an error occurred!")
}
})
}
func showAlert(title: String, message: String ) {
let cancel = "OK"
println("show Alert")
let alert = UIAlertView()
alert.title = title
alert.message = message
alert.addButtonWithTitle(cancel)
alert.show()
}
}
Adding Google Objective-C API 'GTL' to iPhone project
I struggled with this error message as well. This is how I solved it:
Make sure you have added the folder for the service that you are using under GTLSource/Common/ (e.g., add the Drive folder for GoogleDrive).
Under GTL.xcodeproj (that you have already added to your workspace) find the GTLSource folder and drag it to your main project (Golf in your case). Done!
Now you can remove references to the GTL.xcodeproj that you have added to the workspace.
With this approach, you don't even need to add the libraries (so remove them from the list of linked libraries if you have added them).
The Google API documentation is nothing like Apple's documentation (it's not good).
I should also mention that I'm building an app for iOS and not MacOSX, but this should work for OSX as well.
Adding Google Objective-C API 'GTL' to iPhone project
I struggled with this error message as well. This is how I solved it:
Make sure you have added the folder for the service that you are using under GTLSource/Common/ (e.g., add the Drive folder for GoogleDrive).
Under GTL.xcodeproj (that you have already added to your workspace) find the GTLSource folder and drag it to your main project (Golf in your case). Done!
Now you can remove references to the GTL.xcodeproj that you have added to the workspace.
With this approach, you don't even need to add the libraries (so remove them from the list of linked libraries if you have added them).
The Google API documentation is nothing like Apple's documentation (it's not good).
I should also mention that I'm building an app for iOS and not MacOSX, but this should work for OSX as well.
iOS Google Drive integration
Code reference library: https://github.com/google/google-api-objectivec-client-for-rest
Method for performing Google Drive Services requests.
- (GTLRDriveService *)driveService {
static GTLRDriveService *service;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
service = [[GTLRDriveService alloc] init];
// Turn on the library's shouldFetchNextPages feature to ensure that all items
// are fetched. This applies to queries which return an object derived from
// GTLRCollectionObject.
service.shouldFetchNextPages = YES;
// Have the service object set tickets to retry temporary error conditions
// automatically
service.retryEnabled = YES;
});
return service;
}
After Google Authentication, Authorise driveService using these lines:
In your case,
if (authState) {
// Creates a GTMAppAuthFetcherAuthorization object for authorizing requests.
GTMAppAuthFetcherAuthorization *gtmAuthorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
// Sets the authorizer on the GTLRYouTubeService object so API calls will be authenticated.
strongSelf.driveService.authorizer = gtmAuthorization;
// Serializes authorization to keychain in GTMAppAuth format.
[GTMAppAuthFetcherAuthorization saveAuthorization:gtmAuthorization
toKeychainForName:kKeychainItemName];
// Your further code goes here
//
[self fetchFileList];
}
Then, You can use below method to fetch files from Google Drive:
- (void)fetchFileList {
__block GTLRDrive_FileList *fileListNew = nil;
GTLRDriveService *service = self.driveService;
GTLRDriveQuery_FilesList *query = [GTLRDriveQuery_FilesList query];
// Because GTLRDrive_FileList is derived from GTLCollectionObject and the service
// property shouldFetchNextPages is enabled, this may do multiple fetches to
// retrieve all items in the file list.
// Google APIs typically allow the fields returned to be limited by the "fields" property.
// The Drive API uses the "fields" property differently by not sending most of the requested
// resource's fields unless they are explicitly specified.
query.fields = @"kind,nextPageToken,files(mimeType,id,kind,name,webViewLink,thumbnailLink,trashed)";
GTLRServiceTicket *fileListTicket;
fileListTicket = [service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket,
GTLRDrive_FileList *fileList,
NSError *callbackError) {
// Callback
fileListNew = fileList;
}];
}
Try DriveSample from the reference library, include GTLRDrive Files in your project and you are ready to use above method.
To get the GTMAppAuthFetcherAuthorization working, you've to include the pod "GTMAppAuth" or include the files manually in your project.
Actually above methods are copied from DriveSample example of referenced library and this example is working fine for Drive requests.
Google Drive API IOS Permissions of GTLRDriveService
try to Change your scope like:
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate
{
// If modifying these scopes, delete your previously saved credentials by
private let scopes = ["https://www.googleapis.com/auth/drive"]
...
}
Objective-C using block when get list file using Google Driver API
Your can try it.
@property (nonatomic, copy) void(^blockHandler)(id data);
// get list file
- (void)listFiles:(NSString *)fileId complete:(void(^)(id data))completion {
self.blockHandler = completion;
GTLRDriveQuery_FilesList *query = [GTLRDriveQuery_FilesList query];
query.fields = @"nextPageToken, files(id, name, thumbnailLink, webViewLink)";
query.pageSize = 1000;
query.q = [NSString stringWithFormat:@"'%@' In parents and trashed=false",fileId];
[self.service executeQuery:query
delegate:self
didFinishSelector:@selector(displayResultWithTicket:finishedWithObject:error:)];
}
- (void)displayResultWithTicket:(GTLRServiceTicket *)ticket
finishedWithObject:(GTLRDrive_FileList *)result
error:(NSError *)error {
if (error == nil) {
NSMutableString *output = [[NSMutableString alloc] init];
if (result.files.count > 0) {
[output appendString:@"Files:\n"];
for (GTLRDrive_File *file in result.files) {
itemGG *temp = [[itemGG alloc] initWithName:file.name linkThumb:file.thumbnailLink fileID:file.identifier];
[self.lstItem addObject:temp];
[output appendFormat:@"%@ (%@)\n", file.name, file.identifier];
}
} else {
[output appendString:@"No files found."];
}
NSLog(@"%@", output);
} else {
NSLog(@"Error getting presentation data: %@\n", error.localizedDescription);
}
if (self.blockHandler) {
self.blockHandler([[NSArray alloc] initWithArray:self.lstItem]);
}
}
__weak typeof(self) w = self;
[self.cloud listFiles:@"root" complete:^(id data) {
w.tableData = data;
[w.myCollection reloadData];
}];
Error after converting objective-c code to swift 3.0.2 Google Drive Rest API
Refer with this SO thread. You may try changing the unwrapping of the parameters in the closure. Maybe this is required to match what the Objective-C implementation of the function is expecting.
You may also check this additional references:
Swift 2 to Swift 3: Cannot convert value of type '(Data?, NSError?) -> Void' to to expected argument type 'GTMSessionFetcherCompletionHandler?'
As per SE-0112,
NSError
is now bridged to Swift as theError
protocol. In fact, if you ⌥ + click on theGTMSessionFetcherCompletionHandler
type in Swift, you'll see exactly how it's bridged:typealias GTMSessionFetcherCompletionHandler = (Data?, Error?) -> Void
Swift error Cannot convert value of type '(AFHTTPRequestOperation?, AnyObject?) -> ()
- Cannot convert value of type (PFUser!, NSError) void to expected argument type PFUserResultBlock
Google GTL Objective-C API
The YouTube XML API has not yet been replaced by a JSON API.
Related Topics
How to Debug an Issue with a Release Mode Build in iOS
How to Dynamically Format a Number to Have Commas in a Uitextfield Entry
How to List (Almost) All Emojis in Swift for iOS 8 Without Using Any Form of Lookup Tables
Titletextattributes Uiappearance Font in iOS 7
Saving Attributes on a User Fetched from a Query (I.E. Not on the Currentuser)
Uitableviewcell Height Resize When Image Is Downloaded
Swiftui Transitions: Scale from Some Frame - Like iOS Homescreen Is Doing When Opening an App
Pass Different Parameters to an Ibaction
Uicontroleventeditingchanged Doesn't Get Fired When Using Settext of Uitextfield
How to Use Mbprogresshud with Swift
How to Tell If Blocks in Loop All Have Completed Executing
Peripheral and Central at the Same Time on iOS
Calling Function from Another Viewcontroller in Swift
How to Activate Tcp Keepalive on Apple iOS Devices
How to Use the Default iOS7 Uianimation Curve