FlexHybrid simplifies the interface between WKWebView and Native, including asynchronous processing.
And it offers several convenient features to use WKWebView.
Add in Podfile
pod 'FlexHybridApp'
iOS Deployment Target is 10.0
- It has similar development rules and functions to the Android version.
- The interface between wkwebview and native works asynchronously.
- Call and return Promise on the Web.
- Native operates on a separate Concurrent Queue.
- Define the operation of the interface using Swift Closure.
- Data can be transferred to Model Object.
- By specifying an Url capable of interface operation, you can prevent Native calls from unwanted sites.
and include other features...
To utilize the features of FlexWebView, you must use FlexComponent.
FlexComponent is created with FlexWebView, and you can also set it up when you declare FlexWebView.
FlexComponent also includes the WKWebViewConfiguration of the WebView as a factor.
Basically, the interface can be registered and used in the following pattern.
All interfaces operate asynchronously.
On the Web, it becomes pending state until a response occurs.
The interface must be set before the page is loaded into FlexWebView.
// in swift
var component = FlexComponent()
component.setInterface("funcName") { args in
return "received from web - \(args[0]?.toString() ?? "no value"), return to web - \(100)"
}
var flexWebView = FlexWebView(frame: self.view.frame, component: component)
flexWebView.load(someUrl)
// in js
const test = async (req) => {
const res = await $flex.funcName(req);
console.log(res); // received from web - 200, return to web - 100
};
test(200);
After the $flex object is loaded (the window.onFlexLoad
function can be checked on the $flex Load time), register the interface.
// in js
window.onFlexLoad = () => {
$flex.web.funcName = async (req) => {
return await new Promise((resolve) => {
setTimeout(() => resolve(`received from web - ${req}`), 100);
});
};
};
// in swift
component.evalFlexFunc("funcName", "sendData") { response in
print(response.toString()!) // received from web - sendData
}
Data received from the Web is converted into FlexData objects for type-safe use.
Upon web-to-native interface, Arguments delivered from a function in Web are forwarded to Array<FlexData>.
// in js
$flex.funcName("test1", 2, 3.1, true, [0, 1, 2], { test: "object" }, "reified");
component.setInterface("funcName") { args in
if (args == nil) return
var first = args[0].asString() // "test"
var second = args[1].asInt() // 2
var third = args[2].asDouble() // 3.1
var fourth = args[3].asBool() // true
var fifth = args[4].asArray() // array of FlexData(0), FlexData(1), FlexData(2)
var sixth = args[5].asDictionary() // map of first key - test, value - FlexData("object")
var seventh: String? = args[6].reified() // "reified"
}
Data to be used for interfaces can be used as Model Obejct.
At this time, the following rules apply.
- Model Objects must inherit Codable.
- In the Web, it is converted into an object form.
- When receiving Model Objects from Native as Arguments, you must deliver only one object corresponding to that model on the Web.
// in swift
struct TestModel: Codable {
var name: String
var data2: TestModel2
}
struct TestModel2: Codable {
var testInt: Int
}
struct ArgsTestModel: Codable {
var testModel: TestModel
}
component.setInterface("modelTest") { args in
return TestModel("test", TestModel2(2000))
}
component.setInterface("modelArgsTest") { (req: ArgsTestModel?) -> Void in
print(req?.testModel.data2.testInt) // 2000
}
// in js
const test = async () => {
const model = await $flex.modelTest(); // model is { name: 'test', data2: { testInt: 2000 } }
await $flex.modelArgsTest({ testModel: model });
};
test();
On the web-to-native interface, in a code other than the specified closure code block, It's a way to return the value.
Action objects allow you to return values from the code at the desired location.
At this time, the Web is in pending state until a response occurs.
Also, action objects that have responded once cannot be used again.
var mAction: FlexAction? = null
struct LocationResult: Codable {
var latitude: Double?
var longtitude: Double?
}
component.setAction("actionTest") { (action, _) in
self.mAction = action
self.getLocation()
}
func getLocation() {
let status = CLLocationManager.authorizationStatus()
var locationResult = LocationResult();
switch status {
case .authorizedAlways, .authorizedWhenInUse :
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
let coor = self.locationManager.location?.coordinate
mAction?.promiseReturn(
LocationResult(latitude: Double(coor?.latitude ?? 0), longtitude: Double(coor?.longitude ?? 0))
)
break
default:
mAction?.promiseReturn()
break
}
}
It is a feature to easily declare and organize closures that define interfaces behavior.
You can create a closure through a predefined function in the FlexClosure class.
var closureVar = FlexClosure.interface { args in
... some job
}
var closureActionVar = FlexClosure.action { (action, args) in
... come job
}
component.setInterface("closureVarTest", closureVar)
component.setAction("closureActionVarTest", closureActionVar)
Since the interface operates asynchronously, there is a risk of falling into an infinite pending state on the Web.
Therefore, timeout of the interface can be set, and when timeout occurs, it is switched to reject.
If timeout is not set, the default value is 60000 ms.
You can specify the default value for timeout. If timeout for each interface is not set, the corresponding value is set by default.
Setting zero does not cause timeout and pending indefinitely.
The unit is ms.
var timeout = 1000
component.setInterfaceTimeout(timeout)
When setting up an interface, you can specify the timeout of that interface.
Setting zero does not cause timeout and pending indefinitely.
The unit is ms.
var timeout = 200
component.setInterface("funcName", timeout) { args in }
Initialization of interface module, interface success, failure, and events for the timeout are listened to.
// Listen for specific events
component.addEventListener(FlexEvent.EXCEPTION) { (type, url, funcName, msg) in
print("type: \(type.name) url: \(url) funcName: \(funcName) msg: \(msg)")
}
// Listen to all events
var AllListener = { (type, url, funcName, msg) in
print("type: \(type.name) url: \(url) funcName: \(funcName) msg: \(msg)")
}
flexWebView.addEventListener(AllListener)
// Remove specific EventListener
flexWebView.removeEventListener(AllListener)
// Remove all EventListeners
flexWebView.removeAllEventListener()
It is a feature that prevents loading to unintended sites and sets whether to allow URL-specific interfaces.
This feature was implemented using the WKContentRule, so caution is required as it can be removed through the removeAllContentRuleList function in the WKWebViewConfiguration.
BaseUrl is a feature that can be used when setting only interface availability without url restrictions.
FlexWebView allows access to all sites if both AllowUrlList and BaseUrl are not set, but the interface functionality is not available.
If only BaseUrl is set, access to all sites is allowed, and the interface opens only to URLs that match BaseUrl.
component.setBaseUrl("www.myurl.com")
Regular expressions can be used when setting url.
component.setBaseUrl(".*.myurl.com")
Only available for iOS 11.0 and above.
Setting the AllowUrlList blocks access to all url except for the set url and BaseUrl.
component.addAllowUrl(".*.myurl.com")
To allow an interface when setting up a URL, add true to the second canFlexLoad property of the addAllowUrl function.
component.addAllowUrl(".*.myurl.com", canFlexLoad: true)
Only available for iOS 11.0 and above.
This feature is a very basic feature, so please implement and use cookie-related features directly in case of a problem.
It's a feature that automatically maintains cookies.
The default value is inactive and operates automatically when the feature is activated.
FlexWebViews with that feature enabled in the app share all cookies.
component.setAutoCookieManage(true) // activate
component.setAutoCookieManage(true, clearAll: true) // activate and delete all cookies
Displays messages from web console.log, debug, error, warn, and info in the output window of xcode.
It is activated by default.
This output may not be the same as the console message on web.
component.setShowWebViewConsole(true)
Ability to set allowAccessFromFileURLs, allowUniversalAccessFromFileURLs at once.
component.setAllowsUrlAccessInFile(true)
$flex Object is an object composed of interfaces between FlexWebView and Promise.
$flex is declared in the webpage at runtime when the webpage is loaded in the webview.
When $flex is finished loading, you can check the window.onFlexLoad function.
$flex can also be used in any accessible frames. (Ex) iframe that does not violate Cross-Origin)
The components of $ flex Object are as follows.
window.onFlexLoad; // $flex is called upon completion of loading.
$flex; // Object that contains functions that can call Native area as WebToNative
$flex.version; // get Library version
$flex.web; // Object used to add and use functions to be used for NativeToWeb
$flex.device; // Current Device Info
$flex.isAndroid; // false
$flex.isiOS; // true
$fles.isScript; // false
Apply async awit from Swift 5.5.
Flutter version FlexHybirdApp.