4

I'm trying to follow Google's tutorial on making their QuickStart app to learn how to make API calls with Swift. I followed the tutorial completely and ended up with this code

import GoogleAPIClient
import GTMOAuth2
import UIKit

class ViewController: UIViewController {

    private let kKeychainItemName = "Drive API"
    private let kClientID = "592019061169-nmjle7sfv8i8eahplae3cvto2rsj4gev.apps.googleusercontent.com"

    // If modifying these scopes, delete your previously saved credentials by
    // resetting the iOS simulator or uninstall the app.
    private let scopes = [kGTLAuthScopeDriveMetadataReadonly]

    private let service = GTLServiceDrive()
    let output = UITextView()

    // When the view loads, create necessary subviews
    // and initialize the Drive API service
    override func viewDidLoad() {
        super.viewDidLoad()

        output.frame = view.bounds
        output.editable = false
        output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
        output.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]

        view.addSubview(output);

        if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
            kKeychainItemName,
            clientID: kClientID,
            clientSecret: nil) {
            service.authorizer = auth
        }

    }

    // When the view appears, ensure that the Drive API service is authorized
    // and perform API calls
    override func viewDidAppear(animated: Bool) {
        if let authorizer = service.authorizer,
            let canAuth = authorizer.canAuthorize, canAuth {
            fetchFiles()
        } else {
            presentViewController(
                createAuthController(),
                animated: true,
                completion: nil
            )
        }
    }

    // Construct a query to get names and IDs of 10 files using the Google Drive API
    func fetchFiles() {
        output.text = "Getting files..."
        let query = GTLQueryDrive.queryForFilesList()
        query.pageSize = 10
        query.fields = "nextPageToken, files(id, name)"
        service.executeQuery(
            query,
            delegate: self,
            didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
        )
    }

    // Parse results and display
    func displayResultWithTicket(ticket : GTLServiceTicket,
                                 finishedWithObject response : GTLDriveFileList,
                                 error : NSError?) {

        if let error = error {
            showAlert("Error", message: error.localizedDescription)
            return
        }

        var filesString = ""

        if let files = response.files(), !files.isEmpty {
            filesString += "Files:\n"
            for file in files as! [GTLDriveFile] {
                filesString += "\(file.name) (\(file.identifier))\n"
            }
        } else {
            filesString = "No files found."
        }

        output.text = filesString
    }


    // Creates the auth controller for authorizing access to Drive API
    private func createAuthController() -> GTMOAuth2ViewControllerTouch {
        let scopeString = scopes.joinWithSeparator(" ")
        return GTMOAuth2ViewControllerTouch(
            scope: scopeString,
            clientID: kClientID,
            clientSecret: nil,
            keychainItemName: kKeychainItemName,
            delegate: self,
            finishedSelector: "viewController:finishedWithAuth:error:"
        )
    }

    // Handle completion of the authorization process, and update the Drive API
    // with the new credentials.
    func viewController(vc : UIViewController,
                        finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {

        if let error = error {
            service.authorizer = nil
            showAlert("Authentication Error", message: error.localizedDescription)
            return
        }

        service.authorizer = authResult
        dismissViewControllerAnimated(true, completion: nil)
    }

    // Helper for showing an alert
    func showAlert(title : String, message: String) {
        let alert = UIAlertController(
            title: title,
            message: message,
            preferredStyle: UIAlertControllerStyle.Alert
        )
        let ok = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Default,
            handler: nil
        )
        alert.addAction(ok)
        presentViewController(alert, animated: true, completion: nil)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

My problem is that for

import GoogleAPIClient

I get the error "No such module GoogleAPIClient", which seems weird to me since GTMOAuth2 doesn't get an error, even though it's part of the same Pod I think (I'm new to this, so I'm probably butchering the terminology).

From researching the problem, I found that GoogleAPIClientForREST should be substituted for GoogleAPIClient. This document on GitHub says to just use GoogleAPIClientForREST in the code instead of GoogleAPIClient, but I get the same error with that as well.

Then I thought maybe I could re-install the pods with some changes to Google's tutorial. In the tutorial, it says to execute this code in Terminal

$ cat << EOF > Podfile &&
> platform :ios, '7.0'
> use_frameworks!
> target 'QuickstartApp' do
>     pod 'GoogleAPIClient/Drive', '~> 1.0.2'
>     pod 'GTMOAuth2', '~> 1.1.0'
> end
> EOF
> pod install &&
> open QuickstartApp.xcworkspace

So I thought maybe I could replace GoogleAPIClient for GoogleAPIClientForREST in the terminal code, but that landed me with the same error

enter image description here

As you can see in the screenshot, the framework is there on the left-hand side, but I'm still getting the "No such module" error.

Embedded Binaries and Linked Frameworks

enter image description here

Search Paths

enter image description here

enter image description here

I also found some suggestions here that I tried to follow, but I didn't completely understand the explanation. Nevertheless, I tried, and did this (if I did it wrong please tell me): enter image description here

So I'm trying to get either GoogleAPIClient or GoogleAPIClientForREST to work. Thank you for your help

BJ Myers
  • 6,617
  • 6
  • 34
  • 50
Theodore.K
  • 384
  • 2
  • 7
  • 21
  • The link you gave notes to change the framework search path, but the image you have attached isn't showing that. I've attached an image of the correct section in my answer. – Hod Jan 10 '17 at 09:04
  • Did you open QuickStart.xcworkspace (e.g. `open QuickStart.xcworkspace/`) from the command line to get into XCode? – Hod Jan 10 '17 at 09:07
  • Yes I opened it via the command line. Why? Also I rebuilt the project, leaving alone the search paths, so they're default now. – Theodore.K Jan 10 '17 at 17:42
  • Just checking on the open. Trying to track down what's different or perhaps unusual about your project. What version of XCode and what OS are you using? Since the code is small, you might try deleting the project and starting over with the new Podfile and import. – Hod Jan 10 '17 at 19:30
  • I'm using Xcode 8.2, OS X 10.11.6, and iOS 10.1. I've started over multiple times with the same issue. Don't you think it's significant that GTMOAuth2 imports just fine, but not GoogleAPIClientForREST? – Theodore.K Jan 10 '17 at 19:44
  • Yes, that is puzzling. You're using the same versions I'm using. Some people report needing to clean up some cached information, which could explain the difference in the two imports. Here's a more comprehensive list of things to try: http://stackoverflow.com/questions/29500227/getting-error-no-such-module-using-xcode-but-the-framework-is-there?noredirect=1&lq=1 – Hod Jan 10 '17 at 20:01
  • @Hod Can you simplify the first answer to the question in the link you shared? http://stackoverflow.com/questions/29500227/getting-error-no-such-module-using-xcode-but-the-framework-is-there?noredirect=1&lq=1 – Theodore.K Jan 14 '17 at 00:15
  • @Hod I found someone with an almost identical problem. After reading his question, I realized that GTMOAuth2 gets the same error as GoogleAPIClientForREST when I comment it out. So it's apparently a problem with Cocoapods??? Here's a link http://stackoverflow.com/q/39048078/7120487 – Theodore.K Jan 14 '17 at 00:41
  • Let's continue here: http://chat.stackoverflow.com/rooms/info/133135/discussion-between-theodore-k-and-hod – Hod Jan 14 '17 at 05:59

4 Answers4

2

Use this for your Podfile:

platform :ios, '7.0'
use_frameworks!
target 'QuickstartApp' do
    pod 'GoogleAPIClientForREST/Drive', '~> 1.1.1'
    pod 'GTMOAuth2', '~> 1.1.0'
end

Change your import to

import GoogleAPIClientForREST

Then follow the instructions here to migrate the project: Migrating from GoogleAPIClient to GoogleAPIClientForREST

This mostly involves changing GTL calls to GTLR calls with some word swapping. For example, GTLServiceDrive becomes GTLRDriveService.

Regarding framework search paths, this image shows the section you might need to change (note it works for me using the default):

enter image description here

Search paths can be per target, too. Here's an image showing the application target and the framework search paths:

enter image description here

Hod
  • 2,236
  • 1
  • 14
  • 22
  • I followed your instructions, and I'm getting the same error "No such module". – Theodore.K Jan 09 '17 at 19:20
  • And you reran pod install? Do you see GoogleAPIClientForREST in the Pods subdirectory of your project? – Hod Jan 09 '17 at 19:45
  • Yes, I reran pod install, and GoogleAPIClientForREST is indeed in the Pods subdirectory. I updated my original question with screenshots. Please see the second to last screenshot. You can see the subdirectory in the screenshot. – Theodore.K Jan 10 '17 at 00:28
  • I just added a couple of screen shots to the original question, namely my search path and framework settings – Theodore.K Jan 10 '17 at 20:28
  • Not seeing anything there. Just posted an image to show how you can find target-specific settings. Take a look and see how those compare. – Hod Jan 11 '17 at 00:14
  • Mine looks different from yours. I just added an image to my question. Please take a look. Thank you – Theodore.K Jan 11 '17 at 16:49
  • 1
    That looks interesting. All these entries we're looking at define where to look for original files and files generated during the build. As you say, it's odd that GTMOAuth2 imports fine, but GoogleAPIClientForREST doesn't. The framework search path lines get cutoff, so I can't see if GoogleAPIClientForREST is included. Click in the Debug or Release sections and make sure you see it in there. Also, see if you have xconfig files like in this post: http://stackoverflow.com/questions/38020903/xcode-8-swift-pod-headers-empty-after-pod-install and that the entries are consistent. – Hod Jan 11 '17 at 17:58
  • 1
    So, in short, you want to look through these various search paths and make sure any entry you see for GTMOAuth2 has a matching entry for GoogleAPIClientForREST. – Hod Jan 11 '17 at 18:02
  • This is what it says in the "Debug" section: $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/GTMOAuth2" "$PODS_CONFIGURATION_BUILD_DIR/GTMSessionFetcher" "$PODS_CONFIGURATION_BUILD_DIR/GoogleAPIClientForREST" – Theodore.K Jan 11 '17 at 23:06
  • I don't understand what I'm supposed to do in regard to this post http://stackoverflow.com/questions/38020903/xcode-8-swift-pod-headers-empty-after-pod-install – Theodore.K Jan 11 '17 at 23:20
  • That post shows a Pods-Myproject.debug.xconfig file, which defines many of the parameters, like PODS_CONFIGURATION_BUILD_DIR. You might want to look at yours to make sure it makes sense, although, again, what you listed for the Debug framework path suggests the problem is elsewhere. – Hod Jan 12 '17 at 01:43
  • Without access to your full build configuration (which is too much information to post here), all I see to suggest at this point is to make sure you've cleaned out all the caches like some of the linked information suggests and walk through all your build options and look for any difference in how GTMOAuth2 and GoogleAPIClientForREST are treated. – Hod Jan 12 '17 at 17:48
1

So I followed the Quickstart tutorial exactly as well and was able to get it working. I moved the GoogleAPIClientForRest in Framework Search Paths above GTMOAuth2:

Screenshot

I ran into an error in the code after successfully including the module and had to change this line to get it to build and run: if (result.files!.count to if (result.files!.count > 0).

Of course now, Google has deprecated GTMOAuth2 and replaced it with GTMAppAuth, which renders this app useless.

jaerodyne
  • 11
  • 3
0

Although the solution towards which I am pointing you might be for other library, but it will help you for sure. https://stackoverflow.com/a/25874524/5032645 . Please try and let me know, if I should simplify it more for you.

Community
  • 1
  • 1
0

First, look at the Pods_QuickstartApp.framework in the Frameworks group of your Quickstart project. If it is still red, as it is on your screenshot, then Xcode didn't build it. If Xcode didn't build the framework, Xcode can't import it for you.

Cocoapods builds a workspace including your app project, plus another project that assembles your individual pod frameworks into a larger framework.

It seems cocoapods built your workspace, and you did open the workspace instead of the project. That's good.

Check the contents of the file named "Podfile". It should match:

platform :ios, '7.0'
use_frameworks!
target 'QuickstartApp' do
    pod 'GoogleAPIClient/Drive', '~> 1.0.2'
    pod 'GTMOAuth2', '~> 1.1.0'
end

If it doesn't, fix it, exit Xcode, delete the .xcodeworkspace file, and then run

pod install

from the console. That may fix your dependencies so that Xcode builds the frameworks.

If you do get it to compile, your problems have just begun. Google has deprecated the OAAuth authorization from an embedded user-agent.

OAAuth authorization from embedded user-agent deprecated

Bob Wakefield
  • 814
  • 9
  • 16
  • He's trying to build QuickstartApp, but can't because the import of GoogleAPIClientForREST isn't working. This doesn't address the problem. – Hod Jan 12 '17 at 05:40
  • I walked through the tutorial myself. If he follows the tutorial successfully he'll wind up where I did. – Bob Wakefield Jan 12 '17 at 15:23
  • It won't import because the framework didn't build. He needs to get CocoaPods to set up the workspace so it will build. After that, ... – Bob Wakefield Jan 12 '17 at 15:32
  • Sorry, I don't mean to be rude, but I don't see that you're reading his question or the answers. He's retried the project several times, used the correct Podfile, rerun pod install, and much more. It seems pretty clear he followed the steps (repeatedly) and isn't getting the expected results. What am I missing in your answer that hasn't already been said? (Aside from Google deprecating OAuth.) – Hod Jan 12 '17 at 17:29
  • I followed your instructions and got the same error. I reverted back to the GoogleAPIClientForREST pod file that @Hod provided though, because I think that's the one I need to use. – Theodore.K Jan 12 '17 at 18:39
  • @Theodore.k: I think the GoogleAPIClientForREST is a red herring. You aren't importing the framework because it isn't being built. The dependencies aren't right. I'm running Xcode 8.2.1 with the latest Cocoapods. – Bob Wakefield Jan 13 '17 at 18:36
  • @Theodore.K GoogleAPIClientForREST is probably a fix for the problem I ran into once I got it to compile and run. You haven't gotten that far yet. There's something wrong with the dependencies. I had a few problems with Cocoapods after I upgraded Xcode to version 8, quite apart from the transition to Swift 3. – Bob Wakefield Jan 13 '17 at 18:42
  • Are you still on Xcode 7? I can think of a number of reasons you'd want to stay with it. Most of the examples are still in Swift 2, for one. You might try updating Cocoapods. Another thing to try is to manually build the missing frameworks. The Pods project has a number of targets. – Bob Wakefield Jan 13 '17 at 22:03
  • More generally, the problem is that the workspace isn't set up correctly. It does not build the framework project before it builds QuickstartApp. – Bob Wakefield Jan 13 '17 at 22:13
  • Check the framework search paths http://stackoverflow.com/questions/39573184/frameworks-added-to-cocoapod-getiing-import-issue-framework-not-found – Bob Wakefield Jan 13 '17 at 22:50