communication using a
from the native side on iOS.
I would generally recommend writing the iOS code in pure native Swift or Objective-C, however this was a requirement for a job.
Communicate with the WebView without Cordova. Most of the app will be iOS native, but just “borrow” some existing logic that is too complex to spend time rewriting and execute it from a hidden iOS webview in the app.
The resulting code from my testing can be found on GitHub. For anyone who is curious to see the full project. I may convert it to a pod later if it proves useful.
Part of the task involved taking the existing apple APIs and wrapping them so that the implementation could be swapped out at a later date in case things change.
How Does it Work?
In my Swift project there are two main classes that abstract the heavy lifting:
NativeMethodManager abstracts registering new methods, more properly known as message handlers, with the WebView so that the JS can call them and also acts as a delegate to handle the callback response from JS.
Let me demonstrate with pure swift vs. my
NativeMethodManager wrapper I created will hide some of the details
and get us set up quicker, but does the exact same as the example above under
the hood, with some extra functionality.
As the name suggests the
NativeMethodManager also manages these methods as well.
sayQuack the closure provided gets executed.
In the pure Swift example you’d have to implement checks yourself to see if
message.name matches the method you are expecting.
The purpose of the
handle the response from the web view. The response could be anything from
a function’s return value, an exception or any other kind of error
in sending the JS to the web view.
Executing JS in Pure Swift:
As I’m writing this article it’s actually making me wonder if I shouldn’t have
a function and passing parameters for you instead of requiring you type type in
I also encapsulated the
error in a single object. I’m thinking
it may make more sense to have different closures to pass for error and success.
I’ll also be doing an implementation of this using PromiseKit, at which point I’ll make use of the promise’s resolve and reject functions to handle that in a cleaner fashion.
What I’ve found is that it’s possible to send information back and forth between
the WebView. Swift receives the result as an
The Swift native can then cast the object to a
Dictionary or something more
usable from native, from
Allowed types are
See also: WKScriptMessageHandler
Snags I Ran Into
Along the way I hit a few bumps. The only one worth mentioning was trying to
WKWebView on the ViewController for my example app. Now in production
it will most likely be hidden, but for the demo I needed to be able to interact
with both native and web directly for testing.
WKWebView to your ViewController:
Unfortunately the only way I could figure out that makes sense to add the
WKWebView to the view controller was to make a containing controller and
add it as a subview:
UIViewto your view controller on Storyboard (if you use it). This view will contain our webview.
Create the outlet for the view:
@IBOutlet weak var container: UIView!
Then connect the outlet.
To set up the web view’s frame, we can steal some information from the container itself (height, width):
var webFrame = self.container!.frame //set origin to 0, 0 or we will have some extra padding inside our container. webFrame.origin.x = 0 webFrame.origin.y = 0
Create the webview, and add it as a sub view:
//configuration is used elsehwere for adding message handlers to the webview let config = WKWebViewConfiguration() //... in my demo, the code that adds message handlers would come here ... //instantiate webview self.webView = WKWebView(frame: webFrame, configuration: config) //add it as a subview to container: self.container!.addSubview(webView!)