Library / SDK
douban/rexxar-ios avatar
douban/rexxar-ios

Rexxar iOS: a hybrid app container where the routes live on a server

Mobile Hybrid Framework Rexxar iOS Container

577 stars97 forksObjective-CMIT

At a glance

What is it?
Rexxar iOS is Douban's Objective-C client container for the Rexxar hybrid framework, where pages are identified by URL, resolved through a routes map fetched from a server, and rendered in a web view with a cache and a pre-bundled resource folder. It supports iOS 7 and later, and it is MIT licensed.
Who is it for?
Adopt Rexxar iOS if you are building a hybrid iOS application where the pages are deployed from a server and you need native capabilities, widget bridges and request decorators around a web view. Skip it if you are starting a new app today, since the framework targets iOS 7 and later and shows its age in Objective-C and in a 2016 dated sample routes map, and skip it if your content does not change after release, because the whole point is remote deployment.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 31 days ago.
What is it written in?
Mainly Objective-C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Pages are URLs, and the URL is the key to everything

Rexxar iOS is the iOS implementation of the Rexxar Container, the client half of a hybrid development framework from Douban that lets you build a mobile application with the usual front-end technologies, javascript, css and html. The container makes no assumption about what the web side uses, and the project says plainly that you can choose your own front-end framework inside the container. The demos in both the iOS and Android projects use React, but that is a choice of the samples rather than a constraint of the framework. The design is worth understanding because the whole API hangs off one idea: Rexxar identifies a page by its URL. Give the container a correct URL and it creates the corresponding RXRViewController, and the container then looks that URL up in the routes map to find the javascript, css and html resources for the page. That indirection is what makes the framework different from an ordinary web view wrapper. The native code does not embed a page, it resolves a URL to a bundle of remote assets, which is what allows the same native application to show different content without a new release.

The routes map is a JSON file on a server you control

The routes map is the contract between the container and the server, and the README shows its shape in a demo that is served from GitHub's raw file service. There are items and partial items, and each entry carries a remote file URL, a deploy time and a uri pattern, with the whole document also carrying a deploy time. The uri value is a regular expression, and the examples use a scheme of douban:// followed by a host and a path, with the partial items matching a nested path. The container asks for the map at a URL you set through RXRConfig, and the demo configuration points at the raw GitHub address of the routes file in the rexxar-web repository.

json
{
    "items": [{
        "remote_file": "https://raw.githubusercontent.com/douban/rexxar-web/master/example/dist/rexxar/demo-252452ae58.html",
        "deploy_time": "Sun, 09 Oct 2016 07:43:47 GMT",
        "uri": "douban://douban.com/rexxar_demo[/]?.*"
    }],
    "partial_items": [{
        "remote_file": "https://raw.githubusercontent.com/douban/rexxar-web/master/example/dist/rexxar/demo-252452ae58.html",
        "deploy_time": "Sun, 09 Oct 2016 07:43:47 GMT",
        "uri": "douban://partial.douban.com/rexxar_demo/_.*"
    }],
    "deploy_time": "Sun, 09 Oct 2016 07:43:47 GMT",
}

The deploy time field is the part that gives the mechanism its name. Because the map carries a deploy timestamp, the container can tell whether the resources it has are older than what the server now offers, and the map controls the update of the resource files, which is the hot deployment capability the framework is built around. The recommended pattern is for the application to check the map periodically, for example when it starts or when it closes, and the call that does it is on the view controller.

swift
  RXRViewController.updateRouteFiles(completion: nil)

The demo serves the map, the html and the javascript resources over GitHub's raw file service, but the README is explicit that a production service would need to handle a larger routes map and the asset files, and that any server-side framework will do, since Rexxar has no requirement on the server side.

Pre-bundled resources, a cache, and the order they are read in

Three configuration calls decide where the container looks for a page, and the lookup order is documented precisely because it determines what the user experiences on a slow or absent network. The first is the routes map URL, set with RXRConfig.setRoutesMapURL. The second is the pre-bundled resource path, set with RXRConfig.setRoutesResourcePath, and its purpose is that a pre-bundled copy of the routes map and the resource files inside the application package reduces downloads and speeds up the first page open, and lets pages work offline when there is no data request to make. The third is the cache path, set with RXRConfig.setRoutesCachePath, whose stated purpose is the same, reducing how many times resource files are downloaded and giving a page load close to a native one.

swift
  RXRConfig.setRoutesResourcePath("rexxar")
swift
  RXRConfig.setRoutesCachePath("com.douban.RexxarDemo.rexxar")

The read order is the interesting part. When entering an RXRViewController the container first reads the html file from the cache folder, and if it is not there it reads the pre-bundled resource folder, and if it is not there either, the controller attempts one update of the routes map, downloads the new resources that the map lists, and tries the cache folder again. Any step that succeeds stops the process and returns the result, and only if all three steps complete without finding the file does a page error appear. The cached resources typically appear after a routes map update, which is how the hot deployment works in practice. One build detail is easy to get wrong, and the README calls it out: if you set the pre-bundled resource path, that folder must be included in the application package as a folder reference, which in Xcode appears as a blue folder icon, and you create it by dragging the folder into the project and choosing the Create folder references option.

RXRViewController, and the three extension points

You can use RXRViewController directly as the hybrid client container, or subclass it to build your own container, and the demo does the latter with a FullRXRViewController. Initialising it takes only a URL, and the activities property is where native pieces attach.

swift
  let controller = RXRViewController(URI: uri)
  let titleWidget = RXRNavTitleWidget()
  let alertDialogWidget = RXRAlertDialogWidget()
  controller.activities = [titleWidget, alertDialogWidget]
  navigationController?.pushViewController(controller, animated: true)

The framework offers three protocols for extending that container, and each solves a different problem. RXRWidget is the protocol for native user interface components that the web side can invoke, such as a Toast or a pull-to-refresh with a native effect, and it has three methods: canPerformWithURL, prepareWithURL and performWithController. The demo implements it in RXRNavTitleWidget, which sets the navigation bar title, and the implementation shows the pattern, with the URL path checked against a widget-specific path, the query dictionary read in the prepare step, and the controller updated in the perform step.

Objective-C
- (BOOL)canPerformWithURL:(NSURL *)URL
{
  NSString *path = URL.path;
  if (path && [path isEqualToString:@"/widget/nav_title"]) {
    return true;
  }
  return false;
}

RXRContainerAPI covers the opposite direction, native code handing data to the web side, and it has three methods: shouldInterceptRequest, responseWithRequest and responseData. The demo implements RXRGeoContainerAPI, which returns the city the device is in and its latitude and longitude, and the README notes that this example returns placeholder data that never changes, and that the same protocol can be implemented with real data. RXRDecorator modifies the requests the web side makes, with shouldInterceptRequest and prepareWithRequest, and the demo uses an RXRRequestDecorator to add login information to the headers and an apikey to the URL parameters. All of these are registered with URLProtocol.registerClass in the viewDidLoad method of the view controller, and the README says the lifetime of the container API and the decorator should match the lifetime of the RXRViewController.

Installing with CocoaPods, and what the project tree says about its age

The install is a CocoaPods setup, since CocoaPods is described as the dependency management tool for Objective-C and Swift, and you install it with a gem command.

bash
$ gem install cocoapods

Then you add the pod to a Podfile, pinning a commit.

ruby
target 'TargetName' do
  pod 'Rexxar', :git => 'https://github.com/douban/rexxar-ios.git', :commit => '0.2.1'
end

And you run pod install. The commit pin rather than a version tag is a signal about how the project expects to be consumed, and it is the pattern that lets you take a specific state of the container. The repository tree shows the usual iOS layout, with an Rexxar.podspec, an Rexxar.xcodeproj directory, the Rexxar source, a RexxarDemo directory, a RexxarTests directory, a docs directory and both a README and a README_EN.md. The primary language is Objective-C, and the README carries badges for a Travis CI test status, Xcode 8 and iOS 7.0, and the project states that Rexxar iOS supports iOS 7 and later. The age of that is the first thing to weigh. The sample routes map carries an October 2016 deploy time, the pod is pinned to commit 0.2.1, and the CI badge points at a service that the project has since been built on. The framework's ideas are still coherent, and the CocoaPods approach is still current, but anyone adopting it today is picking up code written against a much older toolchain than they would write themselves.

Where Rexxar iOS is the wrong tool

Three limits are structural rather than incidental. The first is that the whole mechanism depends on a server you control and a deployment pipeline behind it. If your content ships inside the application bundle and changes only with a release, then the routes map, the hot deployment, the cache folder and the pre-bundled resource path all reduce to a more complicated way of shipping a web view, and a plain WKWebView wrapper would do the job with less to maintain. The second is that the framework is Objective-C and iOS 7 era, and while it supports modern enough deployment through CocoaPods, adopting it means adopting a codebase whose idioms and toolchain assumptions are several years behind current practice, with no sign in the repository of a Swift rewrite. The third is the operational one, and it comes straight from the architecture. Because the container downloads resources at runtime and matches them by URL pattern, the failure modes are server-side and network-side, and a team that has never had to reason about whether its web assets are reachable from a customer's phone has a new class of bug. The approach also hands a lot of power to the server, since the map decides what the application displays, so a compromised or misconfigured server becomes a full content takeover of the app. The framework is best understood as the result of a specific problem, a large content team that needed to update pages without shipping a new build, and judged against that problem it is a coherent answer.

Editorial conclusion

Adopt Rexxar iOS if you are building a hybrid iOS application where the pages are deployed from a server and you need native capabilities, widget bridges and request decorators around a web view. Skip it if you are starting a new app today, since the framework targets iOS 7 and later and shows its age in Objective-C and in a 2016 dated sample routes map, and skip it if your content does not change after release, because the whole point is remote deployment. Verify first that CocoaPods can resolve the pod, that your resource folder is added to the Xcode project as a folder reference rather than a group, and how you will invalidate cached files when you ship a new build.

Frequently asked questions

How does Rexxar iOS decide which resources to load for a page?

Rexxar identifies a page by its URL, looks that URL up in the routes map to find the javascript, css and html resources, and reads them in a documented order: the cache folder first, then the pre-bundled resource folder, then one update of the routes map with a download of the new resources, and only if all of that fails does a page error appear.

How do I install Rexxar iOS?

Install CocoaPods with gem install cocoapods, add the pod to your Podfile with a git source pinned to a commit such as pod 'Rexxar', :git => 'https://github.com/douban/rexxar-ios.git', :commit => '0.2.1', and then run pod install. The framework supports iOS 7 and later.

What are the three extension points in Rexxar?

RXRWidget lets the web side invoke native user interface components, with canPerformWithURL, prepareWithURL and performWithController. RXRContainerAPI lets native code hand computed data to the web side, with shouldInterceptRequest, responseWithRequest and responseData. RXRDecorator modifies requests the web side makes, with shouldInterceptRequest and prepareWithRequest.

Official sources

  1. douban/rexxar-ios on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/douban-rexxar-ios.svg)](https://hysenlabs.com/projects/douban-rexxar-ios)

Was this useful?