People are always looking for ways to dynamically update their business, and there are frameworks for doing so. Since Facebook launched React Native, it has taken the lead in this area due to its good compatibility and performance advantages. Ctrip has also opened the CRN framework based on this.

If it is a new business, using CRN development is very appropriate, high development efficiency, dual platform compatibility. However, if you want to transfer existing Native pages to CRN, the cost of complex core pages can be a little high. It would be too much to do both business iteration and CRN transformation without additional staffing.

If you turn hard, you have a long period. Taking the order details page, one of the main process pages of Ctrip hotel, for example, it took several months to complete 90% function transfer to CRN without additional staff, which was particularly difficult. The order details page is relatively simple in the main process page. If you want to transfer to the hotel details page, just hundreds of lines of ViewModel are already daunting. 563513413, no matter you are big ox or small white are welcome to enter

In this regard, we consider whether we can adopt a way to coexist Native and CRN, so as to preserve the business logic of Native and achieve flexibility at the UI level. Most importantly, it can be developed in modules, rather than having to load the entire page together as with CRN.

Of course, the hybrid solution of Native and CRN has been available for a long time. However, when CRN appears in the Native page as a sub-view, it is not particularly ideal in performance due to the heavyweight framework of CRN, and the interaction with Native is not particularly convenient. So we started thinking about a lighter solution.

After comparing various cross-platform schemes, we first excluded schemes like Lua that rely on third-party libraries and have non-mainstream syntax, and finally decided to adopt JavaScript that is supported by native systems and has a broad base of syntax.

Prior to iOS7, interacting with JavaScript in Native environments was very simple and limited, basically relying on EvaluateJavaScript from Webview to inject and execute a JavaScript script. Starting with iOS7, Apple introduced the JavaScriptCore library, which dramatically changed iOS development.

JavaScriptCore contains two key classes, JSContext and JSValue:

JSContext

JSContext provides an environment for executing JavaScript code in your APP, allowing you to call JavaScript code directly from objective-C or Swift code and get the result back, In turn, you can expose methods and classes for JavaScript to call.

JSValue

JSValue is a wrapper object of JavaScript data type in Objective-C or Swift. With this object, we can transfer values between Native code and JavaScript code. The corresponding relationship between the two is shown in the figure below:

Objective-C (and Swift) Types JavaScript Types
nil undefined
[NSNull](https://developer.apple.com/documentation/foundation/nsnull?language=objc) null
[NSString](https://developer.apple.com/documentation/foundation/nsstring?language=objc) (Swift [String](https://developer.apple.com/documentation/swift/string?language=objc) ) String
[NSNumber](https://developer.apple.com/documentation/foundation/nsnumber?language=objc) and primitive numeric types Number, Boolean
[NSDictionary](https://developer.apple.com/documentation/foundation/nsdictionary?language=objc) (Swift Dictionary) Object
[NSArray](https://developer.apple.com/documentation/foundation/nsarray?language=objc) (Swift [Array](https://developer.apple.com/documentation/swift/array?language=objc) ) Array
[NSDate](https://developer.apple.com/documentation/foundation/nsdate?language=objc) Date
Objective-C or Swift object ( [id](https://developer.apple.com/documentation/objectivec/id?language=objc) or AnyObject) Objective-C or Swift class ( [Class](https://developer.apple.com/documentation/objectivec/class?language=objc) or AnyClass) Object
Structure types: [NSRange](https://developer.apple.com/documentation/foundation/nsrange?language=objc) , [CGRect](https://developer.apple.com/documentation/coregraphics/cgrect?language=objc) , [CGPoint](https://developer.apple.com/documentation/coregraphics/cgpoint?language=objc) , [CGSize](https://developer.apple.com/documentation/coregraphics/cgsize?language=objc) Object
Objective-C block (Swift closure) Function

To sum up, JSContext provides interfaces for JavaScript and Native to call each other, and JSValue provides data type conversion between calls. Such invocation methods are much more powerful and flexible than the previous Webview, and there is much more room for imagination. So we’re going to build on that. 563513413, no matter you are big ox or small white are welcome to enter

The first step is to create a JavaScript object that describes the UIView in iOS, using ES6 as follows:

 Copy code

Class View { constructor() { this.x = 0; this.y = 0; this.width = 0; this.height = 0; this.borderWidth = 0; Enclosing borderColor = ' '; this.cornerRadius = 0; this.masksToBounds = false; this.subviews = []; } initWithFrame(x, y, width, height) {... {} addSubview (v)... {} setOnclick (click)... }... }Copy the code

So these are all properties and methods that UIView in iOS is pretty common, and just like UILabel in iOS is inherited from UIView, we’re going to go ahead and create a Label object in JavaScript that inherits from the View object that we just created above.

 Copy code

Class Label extends View { constructor() { super(); This. The text = ' '; Enclosing textColor = ' '; this.textSize = 14; this.fontStyle = 0; this.textAlignment = 0; this.lineBreakMode = 4; this.numberOfLines = 1; }}Copy the code

By analogy, we continue to create such as Imageview, Button, ScrollView and other components is commonly used in iOS, as long as willing to, all components can be used to describe this way.

With these basic JavaScript components in hand, you can now start laying out with these components just as you would with iOS, as shown in the following code snippet that shows how to lay out an image.

 Copy code

createImage() {       var container = View.initWithFrame(0, 0, 50, 50);       container.backgroundColor = "#FFFFFF";       var image = Image.initWithFrame(0, 0, 50, 50);       image. imageUrl = ‘http://m.ctrip.com/xxxxx.png’;       container.addSubView(image);       return container; }
Copy the code

For those of you familiar with iOS development, this code will look very familiar. Yes, this is a piece of iOS code written in JavaScript, and so on. Slightly more complex layouts can also be done this way.

Finally, let’s take a look at the return value of the layout. For now, let’s use the Image control above as an example:

 Copy code

varcontainer = View.initWithFrame(0, 0, 50, 50); container.backgroundColor = "#FFFFFF"; var image = Image.initWithFrame(0, 0, 50, 50); Image. ImageUrl = "http://m.ctrip.com/xxxxx.png"; container.addSubView(image); Var demoView = the initWithFrame,0,180,180 (0); demoView.addSubView(container) return demoView; }Copy the code

If you run the above code in a browser or JavaScript environment, you will get a custom recursive object. The root object will contain an array of subviews, and each element in the array may be another set of UI objects.

With that said, the JavaScript section comes to a close. So back to Native, remember JSContext? This is a JavaScript execution environment in Native, so if we execute that Demo in Native using JSContext, we get a corresponding JSValue, This JSValue value is converted to an object-c Object using [JSValuetoObjct], resulting in a dictionary, NSDictionary.

Continue to recursively disassemble the dictionary until the end. Each element will eventually be converted into OC Object. Then, according to the pre-defined Type of each Object, it will be instantiated into corresponding Native components, and each component has a corresponding data Model.

Again, take the above Label as an example, and its corresponding OC Label code is as follows:

 Copy code

@implementation Label - (void)setModel:(HTLDynamicLabelModel *)model{   self.dynamicViewModel = model;   self.text = model.text;   self.textColor = model.textColor;   self.font = model.font;   self.lineBreakMode = model.lineBreakMode;   self.numberOfLines = model.numberOfLines;       if(model.richText && model.richText.attributedString) {       self.attributedText = model.richText.attributedString;    }} @end
Copy the code

At this point, all of the controls previously described in JavaScript have been converted to Native. The only thing left to do is to render these Native components, which is not described here.

In general, this idea is the same in principle as RN or CRN, but is lighter and can be used with almost zero configuration. By configuring incremental update, you can download the latest JS file from the server to achieve the effect similar to the online CRN update.

In terms of performance, JS execution costs are negligible because there is no additional framework code to load, so there is almost no lag when mixing with Native.

This solution is ideal for lightweight uIs that need to be updated from time to time, such as festivals or city packs. These UIs are often updated with the holidays, and with this solution you can easily update the UI online without having to control it with a bunch of styles from the server, reducing the stress of service publishing and unnecessary service interactions.

To sum up, these are our team’s discussions and researches on new things. There is no substitute for CRN or other frameworks. Each framework has its own applicable scenarios, and there is no absolute difference between good and bad.

In the process of researching this solution, we also have a deep understanding of some mechanisms of JavaScriptCore. The principles of JavaScriptCore are all the same, but they can be combined with different scenarios to carry out different evolution, depending on how to flexibly use them.

Therefore, this paper is not so much about exploring the solution of dynamic View in iOS, but also about how to use JSContex and JSValue. From the actual exploration, there are infinite possibilities for the flexible use of JavaScriptCore.