Use an Objective C library in NativeScript for Angular iOS app

Nathan Walker
InstructorNathan Walker

Share this video with your friends

Send Tweet
Published 5 years ago
Updated a year ago

We will learn how to install an Objective C library distributed via a CocoaPod into our project and expose its native api to our app. Specifically, we will cover how to immediately start working with the library even when a NativeScript plugin does not exist for it. Take for example Filestack, a really powerful file uploading api that provides an SDK written in JavaScript for the web, but also offers native SDK's for iOS and Android. Let's take a look at how to code right against this native iOS SDK.

[00:00] If we view the installation instructions, we'll see mention of CocoaPods. This is probably new to most web developers, but CocoaPods are like NPM for native Objective-C libraries. We can see that this references the Filestack CocoaPod.

[00:13] You can actually browse and search CocoaPods much like you could NPM. Let's search for Filestack. Let's just make sure we're using the latest version of the pod. You can actually see 035, the version reference in the documentation, but also FS Picker.

[00:27] This is a newer reimplementation. We can view its GitHub here. For this lesson, we'll use the latest development of this pod. Let's copy this pod line here. At the root of our project here, we're just going to add a folder.

[00:40] We're just going to call it NativeScript Filestack, and we'll add a platforms directory inside of that. This will be specifically for iOS. Lastly, we'll add a pod file so we can install that pod, and drop in this pod declaration.

[00:56] Next, let's add a filestack.ios.typescript file here to represent the API we're going to expose. We'll just export a class, Filestack. In our constructor, we'll code right against the Objective-C library. If we scroll down on the GitHub's README, we'll see some example Objective-C code. Let's copy and paste this as-is.

[01:20] Now, we just want to go through and convert this to JavaScript-like method calls. Let's create a local reference to hold onto our config. To convert this, we'll end up getting rid of the brackets, since the brackets are Objective-C syntax.

[01:35] Instead, we'll use the FSConfig class, which is going to call a method called allocate. We'll execute that like a JavaScript method call, and that will give us this initWithApiKey method. This @ symbol is just Objective-C's way of declaring a string. We can omit that, and just pass along a direct string here.

[01:56] Since there are no typings that ship with his library, we'll just declare this class at the top, and move onto the next line. How you choose to write a library and expose an API is completely up to you, and is certainly a case-by-case type of thing.

[02:11] I'm just going to create another local reference here to these storage options, and convert this line in the same way we did above. This FSStoreOptions will be the class. It's calling a method called allocate, and that gives us this init method.

[02:28] Then we'll just declare this class at the top. For this next line, we can see location is being set to a standalone reference, FS Store Location S3. Beware of these. Sometimes these will be exposed to NativeScript, and sometimes they won't.

[02:44] For these, I often find it easier and safer to actually grab its value from the library itself. If you go back to the GitHub repo, we can search for that value. If you do not see that value being defined, it's often that CocoaPod will reference another pod that actually defines that value.

[03:01] We can see that down here, this has a dependency on the Filestack. We can go into that repo, and search for the same value. Right away, we can see this define line that sets it as an S3 string. We can actually just use the raw string value instead of this static enumerable, and this will work just as well.

[03:24] We'll continue converting. This will reference our store options. Here, we'll just set a local variable, theme. That will be the FS Theme class that references the Filestack theme method. We'll declare this at the top.

[03:37] We have our controller here we'll define as another local variable. We'll remove the brackets. This will call this allocate method. This is where our conversion gets rather interesting. Objective-C has these parameterized method calls.

[03:53] When we convert this to NativeScript, we just want to collapse these parameters here. initWithConfig is the beginning of the function, and theme is the parameter for the next argument. We're going to take that out, and actually collapse it into the method call.

[04:10] That becomes initWithConfigTheme. Every time we collapse the parameterized method call into each other, we capitalize the first character in that parameter. Then everything else just becomes an argument passed to the method.

[04:26] It's very important that every time you combine a method in such a way that you pass these arguments in in the right order, as it's been collapsed. This, of course, is our reference to the config, and we'll just declare FSPickerController at the top.

[04:44] We're going to ignore handling delegates in this lesson, but let's go back to the code sample. This line right here, where that controller is actually presented in the view, we want to work with that.

[04:54] Whenever you're dealing with a reference to self in Objective-C, that's often referencing a standard UI view controller. In this case, we just want this view to show over the main view controller. We can take advantage of those TNS platform declarations that we have set up.

[05:09] We want to first get a reference to the app Window using UI application. We'll use the shared application instance, and grab key window. That will give us a reference to the root view controller.

[05:22] Then because we may want to reference this easily from other parts of the code, we'll just refactor that to a convenient constant function. We'll just call this root VC, we'll drop this here, and we'll just make sure that returns our instance here.

[05:34] Instead of using self here, we're going to use this root view controller to present the view controller. Because we have the typings installed, and it's using inference on TypeScript here, it will give us options of the Objective-C methods that we have.

[05:49] We'll find that there is one similar. You can see below, it was calling presentViewController. Then there's an animated param, and a completion param. You can see collapsed, this is exactly how that call would look -- presentViewController, then animated as the first param, and completion at the end.

[06:10] Then to pass our arguments in order, you can see the controller is the first argument. Animated is just a Boolean, and then for completion, we could add a callback function, but for now, we're just going to pass null. Then we can rid of this call, since we now are doing the same thing.

[06:28] Since this library requires an API key, we'll just make this a parameter we can pass in, API key here, which'll be a string. That way, when we use this library, we can just pass in the API key. We can register a free demo account to a get an API key here.

[06:42] Now, let's add a package file to define our internal plugin here. We'll give it a name, version, we'll specify the main to reference just Filestack, and we'll provide the NativeScript key, and for right now, just let NativeScript know that this supports iOS.

[06:56] If you were working against a specific version of NativeScript, you could specify that version here. Now, we are ready to compile our plugin using TSC.

[07:05] We'll reference the Filestack iOS, then we'll specify types to include our TNS platform declarations for iOS, and ensure the ES2016 lib is included. As an added nicety, we'll ensure that declarations are generated for our internal plugin.

[07:21] We can see the JavaScript that it generated, and the declaration file, which we're going to rename, and keep up with as index. Then we can reference that as the typings on our package here.

[07:33] That way, when we use the API, we will actually get IntelliSense on it. Now, we can install in the plugin using TNS plugin add, NativeScript Filestack. NativeScript will find it locally in your project, and add it to your package.

[07:45] Now, we can just use it. Let's open up our app component, and we can import directly from our internal plugin. We had a class called Filestack. Inside the components constructor, we'll just create an instance of this class.

[08:01] It accepts, of course, the API key. I'll just use one I had set up from a free demo account. If we look at the API we exposed, upon construction of this class, it should create this Filestack file picker, and present it automatically in the view.

[08:17] Now, let's run our project to check it out. You'll notice a CocoaPod error. Some CocoaPods you use may require a minimum deployment target. We can remedy this by checking what the target is from the pod spec file.

[08:33] If we go down through here, we can see that iOS has a deployment target of 8.4. Back in our pod file, we can actually just add that declaration here, that for iOS, we want to make sure that 8.4 is at least targeted.

[08:49] Since we changed the pod file, we want to make sure that that plugin is removed, and added back. We'll add a convenient script to our package that will ensure that our plugin gets built fresh every time, and removes and adds the plugin back.

[09:03] If we refer back to the command that we used to build our plugin, we'll just copy that. Then we'll add a TNS plugin remove, and ensure that it is properly added back. This will save us each time we run our project to make sure we get any latest changes we make to this internal plugin.

[09:20] Then we'll just add a convenient start script that will run that build, and then lastly, launch our app. Now, let's give our new script a shot. We'll see this time that the CocoaPods do install by these green lines.

[09:36] Our app launches, and we see right away this beautiful Filestack file picker appear, offering a wide array of cloud services to upload files into your app from, including even a simple web search, in addition to, for instance, just grabbing a file from Dropbox.

Vance
Vance
~ 5 years ago

You have to specify a target when installing cocoapods now. This requires an XCode project....This is the new syntax

project "../../../platforms/ios/ngnative.xcodeproj" target "ngnative" do pod "FSPicker"

end

http://stackoverflow.com/questions/34556991/pod-install-displaying-error-in-cocoapods-version-1-0-0-beta-1

Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

You actually don't need to specify the target, it works as shown in the lesson with no target. Just make sure you have at least 1.2.0 of Cocoapods installed, find out with:

pod --version

Vance
Vance
~ 5 years ago

Hm, I'm on 1.2 and still says target required. doing it this way fixed it. I also had to add path to project.

project "../../../platforms/ios/ngnative.xcodeproj" target "ngnative" do pod "FSPicker" end

Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

It might just be because you need to specify the version number and the target platform. Try clearing your platform with rm -rf platforms, then replace your Podfile with this:

platform :ios, '8.4'
pod "FSPicker", "~> 1.1.3"

And try that.

Vance
Vance
~ 5 years ago

P.S. love that cursive font. Wish IntelliJ had that-- maybe it does, so many plugins!

Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

Oh yes thanks loooove it!! It's just the Operator Mono font, purchase only - but worth it!

Vance
Vance
~ 5 years ago

thanks. Bear with me. So I was able to add the pod fine, but when I compile my main project via tns I get this error: [!] The target ngnative is declared twice for the project ../../../platforms/ios/ngnative.xcodeproj.

The project was made with the CLI tool from the intro to NS tutorial on egghead. When I open it in XCode, I see that all targets match, and there's only one item in the dropdown under "targets" called ngnative (matching the name of the project). My target in XCode is also 10.2, matching my podfile. I was not able to install the pod without specifying project path and name...

project "../../../platforms/ios/ngnative.xcodeproj" target "ngnative" do

platform :ios, '10.2' pod "FSPicker", "~> 1.1.3"

end

Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

Vance, your Podfile should look like this exactly:

platform :ios, '8.4'
pod "FSPicker", "~> 1.1.3"

No more, you don't need target or project line. The error you are receiving sounds directly related to your incorrect Podfile. Clear everything:

rm -rf hooks node_modules platforms

Then after you have corrected your Podfile as seen above, then:

tns run ios --emulator
Vance
Vance
~ 5 years ago

Don't mean to be a broken record here... but I still get "[!] The dependency FSPicker (~> 1.1.3) is not used in any concrete target." if I leave out the target. Here, I created a brand new project with tns create. I put in a brand new platforms/ios/ folder with a new podfile.

platform :ios, '8.4'
pod "FSPicker", "~> 1.1.3"
$ pod install
Analyzing dependencies
[!] The dependency `FSPicker (~> 1.1.3)` is not used in any concrete target.
$ pod --version
1.2.0
Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

Are you manually running 'pod install'?

Vance
Vance
~ 5 years ago

DUUUUUHHHHHHHHH. OMG I must have conflated that step with some other tutorial project. just doing a tns add from the root solves it. facepalm thanks for calling out the obvious!

Vance
Vance
~ 5 years ago

woohoo! success. thanks! Now, I have a question that might be more interesting to you than my thrashing... http://stackoverflow.com/questions/43057786/how-to-convert-a-cocoapod-to-typescript-when-theres-no-viewcontroller-exposed

Nathan Walker
Nathan Walkerinstructor
~ 5 years ago

Hooray! Great to hear you got it working now :) Also sounds like you got your SO question answered yourself as well, that correct?

i was passing the viewController instead of the view. so pass rootVC().view

:)

Sandesh
Sandesh
~ 4 years ago

Hi Nathan, Do you have an example video where in you create a Nativescript app using Native iOS library without using Cocoapods? Say, I have to include 'myTest.framework' library into a Nativescript app without using Cocoapods. Any help is greatly appreciated

Ricky Brown
Ricky Brown
~ 4 years ago
Marian
Marian
~ 3 years ago

Hi Nathan, When only watching this, I thought it's so clear... but then, when trying to actually do it... Trying to create a TwilioChat plugin, and looking in their docs to get the Objective C code... it looks completely different and seems like nothing from this apply ( https://www.twilio.com/docs/chat/tutorials/chat-application-ios-obj-c ) Looks to be a case-by-case thing, and you can't possibly do a tutorial for all pods out there :( Watched the course 2 times and I still have no idea how to write this (plus "requestTokenWithCompletion" and "initializeClientWithToken"):

(void)connectClientWithCompletion:(StatusWithErrorHandler)completion {
  if (self.client) {
    [self logout];
  }

  [self requestTokenWithCompletion:^(BOOL succeeded, NSString *token) {
    if (succeeded) {
      [self initializeClientWithToken:token];
      if (completion) completion(succeeded, nil);
    }
    else {
      NSError *error = [self errorWithDescription:@"Could not get access token" code:301];
      if (completion) completion(succeeded, error);
    }
  }];
}

P.S. Also, not sure what's with this "self", but it doesn't look like it's the root view controller, like in the video, because rootVC doesn't have the "client" property. I guess that writing plugins is only for those that also know Objective C or Swift. Although, one of the main advantages of NativeScript should be to not have to learn that, to simply use TypeScript/JavaScript and even a framework of your choice (Angular/Vue)