Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write Cordova plugin in Swift?

Tags:

I converted existing custom plugin to Swift language:

(located under Plugins/CustomPluginInSwift.swift)

import Foundation  class CustomPluginInSwift : CDVPlugin {          func getSettings(command: CDVInvokedUrlCommand) {                  println("CustomPluginInSwift :: getSettings is called")                         var pluginResult = CDVPluginResult(status: CDVCommandStatus_OK)         commandDelegate.sendPluginResult(pluginResult, callbackId:command.callbackId)     } } 

And I have two problems:

  • CDVPlugin not found
  • Javascript doesn't see plugin: CustomPluginInSwift:

CDVPlugin class CustomPluginInSwift (pluginName: CustomPluginInSwift) does not exist

I left config.xml the same but it doesn't work as expected.

Where is my problem?

like image 423
Maxim Shoustin Avatar asked Aug 22 '14 14:08

Maxim Shoustin


People also ask

Does Cordova support Swift?

This Cordova plugin adds the Swift support to your iOS project.


2 Answers

As is mentioned you have to add a bridging-header.h file which contains

#import <Cordova/CDV.h> 

Also you need to add the bridging header's path in XCode project properties->Build Settings->Objective-C Bridging Header. For example:

your-app-name/plugins/com.plugin.example/bridging-header.h 

Additionally, in order for Objective-C to see the same plugin class name, you need to add an @objc mapping to the class declaration. It can be the same as the swift class name itself, or something different. In this example, "HWPCustomPluginInSwift" will be what Objective-C (and Javascript) will end up seeing:

@objc(HWPCustomPluginInSwift) class CustomPluginInSwift : CDVPlugin { 

and then your feature node in config.xml file should look like this:

<feature name="CustomPluginInSwift">     <param name="ios-package" value="HWPCustomPluginInSwift" /> </feature> 
like image 63
tsubik Avatar answered Oct 25 '22 18:10

tsubik


CDVPlugin not found

When you created swift file 1st time, Xcode asks you to generate

<your app name>-Bridging-Header.h header with empty content:

// //  Use this file to import your target's public headers that you would like to expose to Swift. // 

In this header add:

#import <Cordova/CDVPlugin.h> 

After that clean your project. If you don't have this header - create it.


CDVPlugin class CustomPluginInSwift (pluginName: CustomPluginInSwift) does not exist

[Step 1]

Right, because Swift uses _TtC (Type To Class) prefix and class index with following template:

_TtC8<AppName><index#><PluginName>    

How to know what is proper index?

[Step 2]

When you initiate instance of CustomPluginInSwift class, like:

var temp:CustomPluginInSwift = CustomPluginInSwift() 

, Swift will add new class name to <AppName>-Swift.h header. The problem is that this header you can't see in your project.

How to find it?

  • Go to xCode -> Window -> Organazer -> "Project Tabs"
  • Select your project
  • copy "Derived Data" path (for me: ~/Library/Developer/Xcode/DerivedData/<AppName>-hbgwavxfqvhwxzagxhgzjvsdrkjk)
  • Go to console and run cd ~/Library/Developer/Xcode/DerivedData/<AppName>-hbgwavxfqvhwxzagxhgzjvsdrkjk
  • run after: cd Build/Intermediates/<App name>.build/Debug-iphoneos/<App name>.build/DerivedSources/

You can find file named: <App name>-Swift.h there with following content:

/* ... */  SWIFT_CLASS("_TtC8Wanameet14CustomPluginInSwift") @interface CustomPluginInSwift : CDVPlugin - (void)getSettings:(CDVInvokedUrlCommand *)command; - (instancetype)initWithWebView:(UIWebView *)theWebView OBJC_DESIGNATED_INITIALIZER; - (instancetype)init OBJC_DESIGNATED_INITIALIZER; @end  /* ... */ 

So we got proper name: _TtC8Wanameet14CustomPluginInSwift

[Step 3]

Now, go to config.xml and change from:

<feature name="CustomPluginInSwift">     <param name="ios-package" value="CustomPluginInSwift" /> </feature> 

to:

<feature name="MeeterCalendar">     <param name="ios-package" value="_TtC8Wanameet14CustomPluginInSwift" /> </feature> 

Thats all,

Hope it will save time,

tested on cordova 3.5 + xCode6.1


examples

Consider you have Plugins folder in your project (generated by Cordova).

We create New swift file MyPlugin.swift with following content:

@objc(HWPMyPlugin) class MyPlugin : CDVPlugin { // see @tsubik answer  /* ... */ } 

method example where we parse javascript request and immediately return answer:

func someMethod(command: CDVInvokedUrlCommand){          println("MyPlugin :: someMethod is called")          let callbackId:String = command.callbackId          var obj:AnyObject = command.arguments[0] as AnyObject!          var eventStructure:AnyObject = obj["eventStructure"]     var eventId:String = eventStructure["_id"] as AnyObject! as String                  println("MyPlugin :: someMethod :: _id:  \(eventId) ")              self.commandDelegate.runInBackground({         // 'jw' is some class                   var data:NSData = jw.toJson()         var str:String = jw.toJsonString(data)                           var obj:JSONObject = jw.getJSONObjectFromNSData(data)         println("sampleList as String: \(str)")                  var pluginResult:CDVPluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAsDictionary: obj)         self.commandDelegate.sendPluginResult(pluginResult, callbackId:command.callbackId)     })  } 

method example where we return empty callabck and after some time return answer:

I used a lot this method form when you try to fetch some data on native side by async way:

protocol AccountLoaderListenerItf {    func onAccountsDone(data:NSData) }   @objc(HWPMyPlugin) class MyPlugin : CDVPlugin, AccountLoaderListenerItf {      var mCalendarAccountsCallbackContext:String?     func getCalendarAccounts( command: CDVInvokedUrlCommand ){     println("MyPlugin :: getCalendarAccounts is called")          self.mCalendarAccountsCallbackContext = command.callbackId          self.commandDelegate.runInBackground({                              var all:AccountLoaderListenerItf = self                  var accounts = MyAccounts(accLoader: all)                  accounts.populateFromCalendars()                              var pluginResult:CDVPluginResult = CDVPluginResult(status:CDVCommandStatus_NO_RESULT)         pluginResult.setKeepCallbackAsBool(true)         self.commandDelegate.sendPluginResult(pluginResult, callbackId:command.callbackId)                 }) } // func   /* .... */  func onAccountsDone(data:NSData){         if self.mCalendarAccountsCallbackContext != nil {                                      var list:JSONArray = WmUtils.getJSONArrayFromNSData(data) // dummy data             var pluginResult:CDVPluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAsArray: list)             pluginResult.setKeepCallbackAsBool(false)             self.commandDelegate.sendPluginResult(pluginResult, callbackId:self.mCalendarAccountsCallbackContext)         }     }   } 
like image 25
Maxim Shoustin Avatar answered Oct 25 '22 18:10

Maxim Shoustin