July 15, 2014

Swift to JavaScript and JavaScript to Swift. A round trip.

Posted in Uncategorized tagged , , , , , at 9:12 pm by tetontech

Edit: Since originally writing this post I have Created the SwiftlyHybrid and AndyHybrid libraries and put them on gitHub. If you are interested in the topic discussed here I would suggest also reading the SwiftlyHybrid and AndyHybrid follow up posting.

In a previous post I showed how to make a call from JavaScript to Swift. Take a look there for a more in-depth discussion of the code to accomplish that data transfer. This example expands on what I did in that post by showing how to make a call from JavaScript to Swift, do a calculation, and then pass the results back to JavaScript. The source code is very similar. It uses one method, evaluateJavaScript(), of the WKWebView class that wasn’t available in Apple’s previous Swift beta.

In the view controller’s userContentController method we’ll need to have access to the WKWebView created in the viewDidLoad method. This requires the addition of a WKWebView attribute to the ViewController class. After trying a couple of experimental ideas I remembered the correct, Swifty way to do this. I couldn’t create the WebView on the same line as the attribute was declared since the initializer for the WKWebView isn’t as simple as adding some integer attribute (var someInt = 3). Instead the initializer needs a frame and a WKWebViewConfiguration. Because of this the WKWebView must be initialized in the viewDidLoad method (This may change if Apple adds a WKWebView to Interface Builder).

The Swift way to declare an attribute and initialize it in something other than an init method is to make the attribute optional using the ? operator.

class ViewController: UIViewController, WKScriptMessageHandler {
    
    var theWebView: WKWebView?
}

In the viewDidLoad method the WKWebView can now be created using the WKWebView function. Once theWebView has been set, the ! operator is needed to declare that we know theWebView attribute is not nil.

(Warning: the functionallity of WKWebView has changed since this code was written. You must move all resources (html, JavaScript, Css, images, and other files) into the temp directory or WKWebView will not load the resources. This may change in the future as WebKit changes or it might not.)

    override func viewDidLoad() {
        super.viewDidLoad()
        var path = NSBundle.mainBundle().pathForResource("index", 
                               ofType: "html")
        var url = NSURL(fileURLWithPath: path)
        var request = NSURLRequest(URL: url)

        var theConfiguration = WKWebViewConfiguration()
        theConfiguration.userContentController.addScriptMessageHandler(self, 
                                   name: "interOp")

        theWebView = WKWebView(frame: self.view.frame, 
                             configuration: theConfiguration)
        theWebView!.loadRequest(request)
        self.view.addSubview(theWebView)

    }

As in the previous example, the userContentController() method must be implemented to handle the messages captured by the script message handler that was added to theConfiguration. In this example the message’s body, the data sent from the JavaScript is interpreted as an NSDictionary since the JavaScript sends an associative array.

    func userContentController(userContentController: 
       WKUserContentController!,
       didReceiveScriptMessage message: WKScriptMessage!) {
        
        let sentData = message.body as NSDictionary
        let aCount:Int = Int(sentData["count"] as NSNumber)
        
        theWebView!.evaluateJavaScript("storeAndShow( \(aCount + 1) )", 
                                        completionHandler: nil)
    }

The other change to the previous example is the addition of the call to theWebView’s evaluateJavaScript method. It has two parameters; a string that is the Javascript to be executed, and a function or closure that will be notified once the JavaScript completes. In this example I don’t need to know when the JavaScript completes so I passed nil as the second parameter.

The javascript for this example is strait forward; an onclick listener function to send a message to the Swift message handler, and the storeAndShow function that the Swift code will call to send a message back to the JavaScript.

var count = 0

function sendCount(){
    var message = {"count":count}
    window.webkit.messageHandlers.interOp.postMessage(message)
}

function storeAndShow(updatedCount){
    count = updatedCount
    document.querySelector("#resultDisplay").innerHTML = count
}

For my own code I would send a Dictionary back to the JavaScript where it would be used as an associative array. I left that out of this example since I didn’t think it was necessary to show.

Overall, the interaction between Swift and Javascript using the WKWebView is more strait forward than using the old UIWebView. It is nice to have a JavaScript way to directly call to Swift. I also like the selection of JSON as the data transfer format for the JS to Swift communication. It reflects the decision I made to adopt that format for QCHybrid years ago. It would be better if the values in the dictionary knew their type so casting using the as operator wasn’t needed but I can live with it the way it is.

Advertisements

June 13, 2014

Swift, WKWebView, and Calling from Swift to Javascript

Posted in Uncategorized tagged , , , , at 7:34 pm by tetontech

Update: If you are interested in this topic you may find this new post helpful. It describes and updated version of the example code you see here and standardizes it for both Swift and Android.

In a previous post, I showed how to embed Apple’s new WKWebView using Swift to create a ‘full view’, hybrid JavaScript application. An ability that QC Hybrid and PhoneGap, both early hybrid development platforms, had was to allow calls to be made from Javascript down to the native language of the device. In the case of iOS that was, and can still be, Objective-C but now can also be done in Swift. The new WKWebView has new functionality available in both languages that requires much less overhead to accomplish this interoperability than the old iOS UIWebView and Android WebView. The WKWebView currently does have an important limitation I’ll discuss in a moment.

There are two parts needed to achieve iOS’s new interoperability; a ‘listener’ of type WKScriptMessageHandler on the Swift side and a new object that has a postMessage() method on the Javascript side. When the Javascript postMessage() method is passed an array, associative array, object, or other Javascript type, the postMessage() method will JSON what it was passed. It then pushes this JSON string as a message to a queue on the Swift side of the application. At this point the JSON is parsed and the message becomes the matching Swift types, Array, Dictionary, String, Int, Double, etc. Custom Javascript objects become Dictionaries. Now your code in the WKScriptMessageHandler’s didRecieveMessage() method has Swift objects without needing to use JSON libraries in either the Javascript or Swift portions of your code. This is great. Unfortunately, at the time of this posting, there is no matching functionality in the beta release to easily push Swift objects back up to Javascript. Hopefully this will come soon. If not, there will need to be some ‘hack-like’ work-arounds similar to what QC Hybrid and PhoneGap had to do. This would be unfortunate.

The source code for this posting’s example is a modification of the previous post’s source. Rather than repeat the discussion of those portions of the example I’d suggest you review the previous post about how to do the embedding. The example below is the contents of a ViewController.swift file. It includes the changes needed to setup JS -> Swift interoperability (JS->Objective-C would use the same classes and methods) for an iOS single view application. The changes can be seen in lines 4, 14-18, and 29 – 31.

Line 4 declares the ViewController class as conforming to the WKScriptMessageHandler protocol. Notice that unlike Objective-C, Swift protocol declarations look just like object inheritance calls (in this case ViewController inherits from UIViewController). Swift only allows single inheritance, like Java. When doing both inheritance and protocol declarations the inheritance declaration must come first. If no inheritance is being done then a protocol declaration can be the first item after the : operator.

(Note: This code example has been updated for Swift 2.0 on my gitHub repository.)

1 import UIKit
2 import WebKit
3
4 class ViewController: UIViewController, WKScriptMessageHandler {
5    
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        
9        //prepare
10        var path = NSBundle.mainBundle().pathForResource("index", 
                                        ofType: ".html")
11        var url = NSURL(fileURLWithPath:path)
12        var request = NSURLRequest(URL:url)
13        
14        //changes from last post
15        var theConfiguration = WKWebViewConfiguration()
16        theConfiguration.userContentController.addScriptMessageHandler(self, 
                                        name: "interOp")
17        var theWebView = WKWebView(frame:self.view.frame, 
                                        configuration: theConfiguration)
18        //end of changes
19        
20        theWebView.loadRequest(request)
21        self.view.addSubview(theWebView)
22    }
23
24    override func didReceiveMemoryWarning() {
25        super.didReceiveMemoryWarning()
26        // Dispose of any resources that can be recreated.
27    }
28    
29    func userContentController(userContentController: WKUserContentController!, didReceiveScriptMessage message: WKScriptMessage!){
30        println("got message: \(message.body)")
31    }
32
33 }

Line 15 introduces a new class, WKWebViewConfiguration. This class manages various types of configurations for any and all WKWebViews used in your applications. One of the WKWebViewConfiguration’s properties is a content controller named userContentController. This is the object we need to access in order to setup the ViewController class as the ‘listener’ for messages from Javascript.

Line 16 shows how to assign the ViewController to be the Javascript message handler for any messages generated as part of the named message queue ‘interOp’. You can select any name you want instead of interOp. It just sounded good to me.

Line 17 shows how to initialize the WKWebView with both a frame and the WKWebViewConfiguration instance. This tells the WKWebView to create an opportunity to use the Javascript postMessage() method.

We’re almost done. The only other change needed is to create the ‘listener’ method, userContentController.didRecieveScriptMessage(). One of the parameters to this method will be the WKScriptMessage generated as part of the interoperation between your JavaScript and Swift. The message has a property named body that is the Swift version of the JSON data passed from JavaScript. Line 30 shows this example printing the Swift objects that the example sends from the Javascript side.

I created a simple Javascript function in the project’s main.js file that you can see below. Notice the new window.webkit.messageHandlers object. It has an attribute ‘interOp’ that matches the name we used on line 16 of the Swift code. The JavaScript interOp object is the ViewController since it conforms to the WKScriptMessageHandler protocol and was assigned on line 16 with the interOp name.

function sendMessage(){
    var aMessage = {'command':'hello', data:[5,6,7,8,9]}
    window.webkit.messageHandlers.interOp.postMessage(aMessage)
}

That’s it. Now we can send any object, array, or primitive as a message to Swift. Hopefully Google will not re-invent the wheel when they add this same type of interoperability to Android. They should make their Javascript side look exactly this same way. There is some advantage to copying.

For an example of how to call from JavaScript to Swift and then back to JavaScript see this other post.

June 12, 2014

Swift Hybrid Development

Posted in iPhone development, mac development tagged , , , , , , , , , , , , at 3:02 pm by tetontech

From 2007 until 2012 when my doctoral degree got in the way I created and then supported a hybrid development environment call QC Hybrid. It was designed to allow you to build an installable application in Javascript, HTML, and CSS. It also allowed you to make calls and pass JSON data from Javascript down to the native execution code, Objective-C for iOS and Java for Android.

My doctoral degree is now done. I could begin supporting that code set again but it would be better to implement it in Swift or current Objective-C and gain access to the new WebKit API  common to both iOS 8 and OS X Yosemite. Another reason to ‘start from scratch’ is that the new WKWebView is much more powerful that the old UIWebView. According to Apple, it has all the new HTML 5 abilities and now the same webkit engine as Safari so tools like QC Hybrid, PhoneGap, and the other hybrid tools are not needed by the vast majority of Hybrid developers. That was not true in the past. Never the less, I maintain that this type of project should, in most cases, be a temporary and stop-gap solution while you develop your app in the native languages of the platform. At least for now.

With all that history out of the way, lets get to a Swift code example.

For a hybrid application, the HTML, Javascript, CSS, and image files are included as part of the project just like any other file. This image shows the single-view Swift project I created for this posting. Also make sure you add the WebKit.framework to your project.

project

You can see I have a file called index.html. This is the file that the example code is going to load and trigger the loading of the main.css and main.js files just like any normal web page.

In the project’s ViewController.viewDidLoad() method I inserted added code to create a WKWebView, load index.html, and add the WKWebView to the main view.

(Note: This code example has been updated for Swift 2.0 on my gitHub repository.)

        //get a string path to the file
        var path = NSBundle.mainBundle().pathForResource("index",
                             ofType: ".html")
        //create a URL for the path
        var url = NSURL(fileURLWithPath:path)
        //create a request for the URL
        var request = NSURLRequest(URL:url)
        
        //create the WKWebView and set the size to match 
        //the window's full view 
        var theWebView:WKWebView = WKWebView(frame:self.view.frame)
        //have the web view load the page
        theWebView.loadRequest(request)
        //add the web view to the main view
        self.view.addSubview(theWebView)

The reason I started the load before adding the web view was to get a little buffering going on before the display. The web view will load without being displayed.

I’m going to do another posting on how to communicate between Swift and Javascript in a later post. That has changed dramatically as well.

September 10, 2011

At Last!!!

Posted in Android Development, iPhone development tagged , , , , , , , , , , , , , , , at 10:29 pm by tetontech

At long last (big sigh of relief here by me) QuickConnectFamily 2.1 is finally available for download.  It involved a lot of work by many people and has come together well.  There are some big changes for both the iOS and Android platforms.  These enhancements and changes have been driven by requests from developers like you.

Both:

  • This one is BIG.  The JavaScript functions now exist inside the qc name space.  In other words where you would have used the handleRequest method it is now the qc.handleRequest method.  The old behavior is deprecated.
  • Another BIG one.  In order to make the Control Function code more readable and more easily comprehended for those new to the framework all Control Functions MUST return one of the following three values (see the documentation for more information):
    • qc.STACK_CONTINUE – This instructs the framework to execute the next control function in the stack.
    • qc.STACK_EXIT – This instructs the framework to terminate all further stack execution.
    • qc.WAIT_FOR_DATA – This instructs the framework that a call to database or remote data has been made or a call to device specific behavior such as showing a map, displaying the camera, email editor or other native behaviors.
  • Work has been done to improve the asynchronous call stability in the underlying framework.  (Thank you to the team at affinityAmp).
  • Miscellaneous bug fixes and enhancements.

Android:

  • Bug fixes
  • Expanded database support and fixes
  • A major rework of the underlying Android Java code to make it match the design changes in iOS.  This is in preparation for QC Plugins and some new features such as ‘window-in-window’ that will be part of the next release as a Plugin.  The ‘window-in-window’ code is in there now but not official until it is converted to a plugin and the same behavior is available for iOS.
  • Added a hybrid sqlite database example

iOS:

  • Bug fixes
  • Removed the native footer code since libraries for scrolling and others such as Sencha, JQTouch, etc. are now of good quality.
  • QC Family Hybrid Plugin API and design spec completed.  There is an example of how you can add to QC on your own.  If you thing these additions could be useful to others you are free to charge for them, or not, host them yourself, notify me and I will add them to the plugin list on the QC plugin site.  If you are willing to donate them to the QC community send them to me for review and I will put them into the git repository and list them on the QC plugin site.
  • Updated all the examples to use the new return values and the new qc name space.
Now that this is out I will be creating a Key/Value store for Android and presenting it at the next AnDevCon.  I’m also looking forward to adding Android build support back into the QC Hybrid build process so you can write your app once yet build and run it in the iOS simulator and the Android emulator with one click.
As always I hope this is helpful for you.  I enjoy working on QC and hope to be able to do so long into the future.  If you find any issues please let me know via the google group.
Lee

April 12, 2011

JavaScript fixed length double

Posted in Uncategorized tagged , , , at 5:31 pm by tetontech

For those of you who are using monetary or other fixed length values in your app here is a little function to help you out.  It accepts any numeric or string values and produces a string that can be used for further calculation as a number or displayed.  If the number is shorter than the precision indicated the function will pad it with zero characters until it is the correct length.

 

Enjoy.

 

Call it like this:  var fixedLengthFloat = toFixed(3.2, 10);

 

The function:

 

function toFixed(value, precision) {

if(isNaN(value)){

value = 0;

}

if(precision < 0){

precision = 0;

}

var power = Math.pow(10, precision || 0);

var result = String(Math.round(value * power) / power);

if(precision > 0){

if(result.indexOf(‘.’) == -1){

result += ‘.’;

for(var i = 0; i < precision; i++){

result += ‘0’;

}

}

else{

var decimalPortion = result.substring(result.indexOf(‘.’)+1, result.length);

var placesMissing = precision – decimalPortion.length;

for(var i = 0; i < placesMissing; i++){

result += ‘0’;

}

}

}

return result;

}

January 6, 2011

QuickConnect and the Mac app store

Posted in misc tagged , , , , , , at 7:50 pm by tetontech

The mac app store is up.  Do you want to put an app up?  QC has supported hybrid mac development for two years.  It is ready for you now.  All you have to do is make your selection as you see in the picture below and start making your app.

I’m currently adding more functionality to QC Mac.  The update should be out next week but you can get stared now.  Develop your app in HTML, CSS, and JavaScript just like your hybrid iOS, Android, Blackberry apps.

Selecting QC for the Mac

QuickConnect 1.6.6 now available

Posted in iPhone development tagged , , , , , , at 3:05 am by tetontech

QC 1.6.6 has a fixed installer but also has an upgrade that should make your user interface more responsive when you use the framework calls to download or upload files, make native database queries, or any other calls you make down the the native layer.  You don’t need to worry about the threading issues or starting the threads.  It is all automatic.  It doesn’t have any impact on your JavaScript code.

January 4, 2011

QuickConnect family Release for Windows, Linux, etc.

Posted in Android Development, erlang development, PHP development tagged , , , , , , , , , at 5:23 pm by tetontech

I have put together a QC 1.6 release for non-Mac users.  It includes a jar file for QCNativeAndroid, files for QCPHP, QCErlang, and the 1.5 release of QCHybridAndroid.  As soon as I can get to it I will be updating the hybrid iOS and hybrid Android environments to include the multi-threading now available in the native iOS and native Android environment.  It will be interesting to see how the available threading effect what we can do in JavaScript.

December 31, 2010

Sencha Touch and QuickConnect Hybrid

Posted in iPhone development tagged , , , , , , , at 10:35 pm by tetontech

Ed Canas has put together a short video on how to use Sencha Touch with QuickConnect for the iPhone and iPad. You can find the video here on the QuickConnect wiki.  In seven minutes he has the Sencha Kitchen sink example working with the QuickConnect JavaScript functions still available.

Thanks Ed.

Go take a look.

December 23, 2010

QuickConnect 1.6.4 Now Available

Posted in Uncategorized tagged , , , , , , , , , , , , , , , at 8:44 am by tetontech

I have just posted the 1.6.4 version of QC on SourceForge.  It includes some defect fixes and some minor additions, and a few big changes.  QC 1.6.4 requires the iOS 4.2 SDK.

The big changes are regarding the native application templates.  You can now use the same design to create Objective-C iPhone, iPad, and Universal iPhone/iPad apps that you have been using to create your hybrid applications.

These native iOS apps come ‘pre-threaded’.  Every time you call handleRequest your command stack is executed on a worker thread.  Any of your ViewControlObjects that you create for your control Stack are executed in the main  thread since it is the only one that is allowed to update the User Interface.  All other behavior is done on a worker thread and you don’t have to worry about how to set it up, make it go, or make it stop.

Just as with the hybrid apps you’ve been creating with QC all of your async calls to HTTP servers, portals, etc. are linearized for you.  You never need to write another callback function!

In addition to making your remote HTTP calls easier all of the templates for native QuickConnect applications also include support for both direct SQLite access and CoreData.

With a little time working in Interface Builder and putting together some CoreData objects your app is up and running.

Examples are already in the download for all of these native iOS templates.  Check them out and see how easy native iOS apps can be.

The next release will have native multi-threaded Android applications as well.

One other change is  that the PHP template has been updated.  Take a look at the example in the download.

Lee

Next page

%d bloggers like this: