Recently I wrote a fun library JSONRnederKit, probably the whole person is in a gap period, can not get down, at the same time recently experienced some things, let write code to fill myself. It takes a long time to update the APP every time because of the need. For example, if you want to make eggs for a holiday, you might want to update it. To solve this pain point, I wondered if I could do some simple single-page applications with JSON. In fact, I could.

The screenshot below





rendering







The core file

SSJSContext.m,SSBaseRenderController.m,NSObject+SSRende.m,SSKit.js,

file role
SSJSContext.m Responsible for receiving JSON to generate a new container View back to the outside world for use
SSBaseRenderController.m Receives the container view returned by SSJSContext and displays it
NSObject+SSRende.m There is one method in total that calls any method of the OC object
SSKit.js Implement JS data structures modeled after UIKit
SSTool.js Provides string parsing help

The flow chart

St =>start: obtain JSON e=>end: display the end op1=>operation: SSJSContext Provides JSON and wrapperView to JS op2=>operation: Op4 => Operation: Setup complete, notify jsContext, and return wrapperView op4=>operation: RenderController receives wrapperView ST -> OP1 -> OP2 -> OP3 -> OP4 -> ECopy the code

How to parse JSON

In order to facilitate interaction, I define oc_invokeWithArgs(ocObj,method,args) for JS in SSJSContext. NSObject->NSObject, Controller->UIViewController, View->UIView…

// call this function to call the OC instance object's method, The argument passed by JS is automatically converted to the corresponding OC type self[@"oc_invokeWithArgs"] = ^id(JSValue *ocPointer, NSString *methodName, JSValue *args){ id ocObj = [ocPointer toObject]; SEL methodSelector = NSSelectorFromString(methodName); NSArray *oc_args = [args toArray]; / / call the method that add NSObject, can call the OC instance object obj method id = [ocObj js_performSelector: methodSelector withObjects: oc_args]; return obj; };Copy the code
Class NSObject{constructor(){constructor(){// This. OcPointer = null; This. OcClsName = 'NSObject'; // Save the class name of the OC object to create an instance of OC reflection. }... / / create OC object creatNative () {/ / call OC method, create an instance, and save the enclosing ocPointer = oc_creatObject (this) ocClsName) firstUpperCase ()); }... // Bind JS object to OC object bindJSValueToOC(){... Oc_invokeWithOneJSArg (this.ocPointer,'setJsValue:',this); } // call OC invokeNative(method,... args){ return oc_invokeWithArgs(this.ocPointer,method,args); }}Copy the code

View generation and hierarchical relationship resolution

JS takes JSON and passes it to the Controller instance, calling the produceSubviews method of the Controller

Ponents produceSubviews () {/ / this.com is access to the components inside the JSON if (! this.components) return; This.components.foreach ((item,index)=>{let view = eval(' new ${item.type}() '); this.components.foreach ((item,index)=>{let view = eval(' new ${item.type}() '); // Pass a single component(item) to view View.initWithjson (item); this.wrapperView.addSubview(view); this.viewStore.set(view.ocIdentify,view); }); }Copy the code

Go to the view. The initWithJSON (item); Then, the view calls view.creatNative to create an OC object and stores it in this.ocPointer. The next step is to create a subview by iterating through the components within the item. This is a recursive call, which resolves the view hierarchy. Add a subview this.addSubView (view), which calls the OC method. So you’ve laid out your view.

After the view is generated and laid out, JS hands the wrapperView to the SSBaseRenderController for display.


JSON string variables and functions

How to change “${ui.screenw} ‘” to “375”, thanks to the template string design of ES6?

let string = "`${UI.screenW}`"; string = "return" + string; value = (new Function(string)).call(); ${ui. screenW} 'becomes "375"Copy the code

So that’s not too hard to do with the Function again, just by changing the string and new Function(string). Since JSON becomes an object directly after being passed to JS, variables can be easily manipulated and data flow is also possible.


Data flow problems

The hard part was how to design the flow of data, and I spent a lot of time thinking about it. Finally, we decided to use the form “Execute Action” to solve the data flow, referring to Redux. Turn the view into a state machine that determines what is displayed on the view.

There are a lot of details, you can see the source code, there are detailed notes, here is just about the principle.


To contact me

If you have any questions, please discuss with me. I will reply you at [email protected] or github.com/cx478815108