This is the first public technical article published by didi App architecture group. This article will introduce DynamicCocoa, a self-developed iOS dynamic solution. As a technology navigator, the App architecture team not only reserves technology for Didi client, but also undertakes the responsibility of sharing technology with everyone. DDApp, a public account, will push other iOS and Android development dry goods in the future, please pay attention to ~



Plan was born

Dynamic has always been the dream of App development. However, in the iOS environment, Apple prohibits its own dynamic library loaded and executed outside the Main Bundle, so the scheme of delivering native code like Android is blocked.

Later, cross-end solutions based on Web standards such as ReactNative and Weex appeared, and various companies tried them, but they may not be suitable for didi’s current situation:

  • Didi App is highly interactive, map-based and highly specific

  • There are plenty of people on the client side, and learning and development across technology stacks are costly

  • A large number of solidified Native code, high rewrite cost

So we thought, can we make a solution that keeps the iOS native tech stack and magically has the ability to be dynamic without rewriting the code?

So we designed and implemented a landmark ios-specific DynamicCocoa solution

DynamicCocoa met

DynamicCocoa can convert existing Objective-C code to generate intermediate code (JS), which is executed dynamically after delivery. Compared with other dynamic solutions, DynamicCocoa has the following advantages:

  • Using a native technology stack: The user doesn’t have to touch JS or any intermediate code at all, keeping the native Objective-C development and debugging approach intact

  • No need to rewrite existing code: Existing native modules can easily become dynamic plug-ins

  • High syntax support completeness: support most of the grammar used in daily development, do not worry about support

  • Support HotPatch: after modifying the bug directly from the source patch, one-stop solution to dynamic and hot repair needs

Whether it’s dynamic or HotPatch, we can tell developers: “Write Cocoa, Run Dynamically”



Syntax support

DynamicCocoa supports most everyday Objective-C/C syntax, pick a few special ones like:

  • Complete Class definition: interface, category, Class Extension, method, property. The most important thing is to support complete IVAR definition and maintain the same instance memory structure as native

  • ARC: can correctly handle the reference count of strong, weak, and unsafe_unretained objects, and release the IVAR of the object

  • C function: support C function definition and C function call, inline function call

  • Variable parameter: supports C and OC variable parameter method calls, such as NSLog

  • Struct: Supports the use of arbitrary structures without additional processing

  • Block: Supports creation and invocation of blocks of any parameter type

  • Other OC features: such as @selector, @protocol, @encode, for.. In etc.

  • Other C features: macros, static variables, global variables, address fetching, etc

For example, you can safely use the following notation, which is executed dynamically correctly:



Resources to support

A functional module, in addition to code, resources are also essential, DynamicCocoa’s dynamic bundle support:

  • Xib and storyboard

  • xcassets

  • Image resources that are not in xcassets

  • Other Resource files

This will be a great development experience for those used to developing UIs using IB.

Toolchain support

We developed a command line tool in Ruby (analogous to XCodeBuild) that greatly simplifies the complexity of configuring the development environment, OC code conversion, resource handling, and packaging. It can:

  • Parse Xcode Project: read Project compilation options, keeping them consistent with native compilation parameters

  • Incremental compilation: Caches JS conversion results and only re-converts modified files, greatly increasing build speed

  • Link: analyze class dependencies, combine multiple JS in order of dependencies, and improve file reading speed

  • Resource compilation: Xib, storyboard, and Xcassets used for compilation

  • Package: Package JS and resources into bundles

For developers, just like the POD command, everything can be done through this command.

Dynamic plug-in development processes

The DynamicCocoa Engine SDK needs to be integrated into the App to execute the delivered bundle

The development to release process is shown below:



DynamicCocoa only provides command line tools and Engine SDK, which can be packaged, run, and tested locally, while online publishing background, server, CDN, etc.

Within Didi, we have built a closed-loop system of development, Review, online regression test, gray scale, release, rollback and statistics, providing internal access in the form of services.

HotPatch process

HotPatch is dynamic in method granularity in nature, so it is not difficult to implement HotPatch after the whole framework is built. The biggest advantage of using DynamicCocoa to do hot repair is that the developer is still only responsible for the source code. After modifying the bug, make a patch package. After the fix is successful, push the source code changes directly to the repository.

Suppose we found the following bug:



Then repair and self-test in Native:



Once the self-test is complete, add a magic Annotation to the end of this method:



By using the command line tool to package in patch mode, all marked methods can be extracted, converted into JS representation, and published together.

In addition to modifying a method, patch mode also supports:

  • Call the original method

  • Add a method

  • Add a property to help fix the bug

  • Add a new Class

Finally, developers can feel free to git push their modified code (and even leave annotations on) and do a hot fix.

Open the black box



Just as Objective-C is implemented by the Clang compiler and objective-C Runtime, DynamicCocoa is made up of two corresponding parts:

  1. Based on Clang, an OC source code to JS code converter is implemented

  2. DynamicCocoa SDK to implement the oc-js intermodulation engine

As we know, the standard compilation process of Clang-llVM is to generate the syntax tree from the source code through preprocessing, lexical parsing and syntax parsing. Llvm-ir is generated by CodeGen, and optimized and assembled at the back end of compiler, and finally the object file is generated (Mach-O).



We want Clang to help complete the steps of source code processing, and we want the result to be JS representation, so after Clang generates abstract syntax tree (AST), we take over and implement an OC2JS CodeGen, which iterates through each specific syntax node and outputs JS representation:



Since the converter is built in the same way as the Clang front-end standard compilation process, the converter can be built as long as native code can be built, which is a prerequisite for DynamicCocoa to keep dynamic packages and native strictly consistent.

The other part is DynamicCocoa SDK to be integrated into App. Its responsibility is to provide Runtime environment for JS intermediate code, realize oc-JS intertuning engine, load dynamic bundle, and provide convenient API. The overall architecture is as follows:



Some interesting points:

  • Libffi is used to process the calling Conventions in each architecture. It constructs the caller call stack and parses the Callee call stack for implementing OC/C function calls, dynamic IMP, and blocks.

  • Due to the weak typing of JS, it is easy to lose the type information of numeric variables when doing calculations, such as int a = 1/2; In OC, it stands for divisible, and the result is 0, but when entering JS, it will be calculated as double, and the result is 0.5, causing an inconsistency. So DynamicCocoa takes over the type information in JS, and strongholds or operators require special handling.

  • In order to realize block, we constructed the same memory structure as Native block. No matter the block created by JS or the block passed into JS by Native, it can be called without distinction.

  • The runtime runtime provides an API for dynamically creating classes, but only the MRC Class can be created. As a result, the IVAR will not be released by ARC. We have extended the ARC capability to dynamically creating classes by delving into the real memory structure of classes and instances. The non-Fragile ABI simulates real IVAR memory layout and ivar layout encoding. If you override the Dealloc method, DynamicCocoa can even automatically call super as native does.

Changes made by DynamicCocoa

DynamicCocoa dynamic technology brings a lot of imagination to App development:

  • Low-cost dynamics: You can quickly make existing modules dynamic without extra learning or rewriting code

  • Collaboration: For large teams, releases no longer need to be tied to each other

  • Fast iteration: No need for review and App Store release, just like the H5

  • App slimming: Native only needs to leave the entry of the plug-in to realize network delivery and reduce the size of App

  • AB Test: it is not limited to the AB function Test embedded in native, and can dynamically deliver various tests after the release

Compared with cross-terminal solution, it also brings a new idea: iOS and Android both retain the native development mode and directly dynamic the native code in their own ways to maintain the differences of each platform.

Q&A

How is it different from JSPatch?

The idea of both is to achieve JS and OC intermodulation: DynamicCocoa focuses on dynamic ability, the advantage lies in the complete need to write JS and more syntax features support; JSPatch is a smaller, lightweight solution for HotPatch.

Is this framework used online in Didi App?

Yes, Didi App has been launched and several versions have been used. For example, Didi Minibus and Didi Shuttle have both had 10K-level dynamic modules launched.

Is there a significant performance degradation in dynamic package running?

The operation of dynamic JS code needs to go through frequent switching between JSCore and OC. The performance of Native is bound to be lost, but after optimization, it has reached the degree of no perception now: In our case, without a specific flag on the page, neither developers nor QA can tell if the page is running native or dynamic packages… Detailed performance analysis will be shared later.

How about dynamic package sizes?

Depending on the size of the resource and the amount of native source code, the magnitude of dynamic package is about 10,000 lines of code and 100KB without considering the resource.

Is multithreading supported?

GCD is now simply supported to handle multithreading, you can use dispatch_async to place a block in another queue for execution.

How to locate a dynamic packet crash?

Dynamic JS code runs in JSCore, and there is no way to directly obtain the call stack. We provide stack trace function, which records every JS to OC/C intercall in the recent call stack. When a crash occurs, it can be retrieved as additional information and reported to the statistical platform along with crash logs, which facilitates problem location.

Will it fail apple’s review?

Many dynamic and HotPatch schemes in the market are based on JS distribution and run on the native JSCore. I believe that Apple will not reject the dynamic function as long as it is not delivered during the review period.

Is it possible to support Swift direct dynamics?

Compared with OC, dynamic and HotPatch of Swift are more difficult, but we have a feasible plan, which can be done. However, for the current situation of Didi (most of them are developed with OC), the urgency is not high. We will consider support later.

Is there an open source plan?

Yes, we are actively preparing for open source in early 2017.

Where do you follow up?

Please pay attention to the wechat official account DDApp of Didi App development technology. We will release the latest progress of DynamicCocoa on it, and also share the dry technical articles of Didi iOS and Android development with you: