{"id":13610833,"url":"https://github.com/douban/rexxar-ios","last_synced_at":"2025-04-04T10:07:22.007Z","repository":{"id":37475096,"uuid":"69011655","full_name":"douban/rexxar-ios","owner":"douban","description":"Mobile Hybrid Framework Rexxar iOS Container","archived":false,"fork":false,"pushed_at":"2023-05-25T03:30:34.000Z","size":688,"stargazers_count":581,"open_issues_count":9,"forks_count":96,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-03-28T09:06:35.704Z","etag":null,"topics":["hybrid-apps","mobile-framework","rexxar-web"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/douban.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-09-23T09:44:29.000Z","updated_at":"2025-02-15T18:57:53.000Z","dependencies_parsed_at":"2024-01-16T15:40:15.440Z","dependency_job_id":"298461d1-c262-4c0c-8b6a-fadc3c344759","html_url":"https://github.com/douban/rexxar-ios","commit_stats":{"total_commits":190,"total_committers":11,"mean_commits":"17.272727272727273","dds":0.6,"last_synced_commit":"7057921111eb97eee85431a0cd6052326fbfa7da"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douban%2Frexxar-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douban%2Frexxar-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douban%2Frexxar-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/douban%2Frexxar-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/douban","download_url":"https://codeload.github.com/douban/rexxar-ios/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157108,"owners_count":20893215,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hybrid-apps","mobile-framework","rexxar-web"],"created_at":"2024-08-01T19:01:48.446Z","updated_at":"2025-04-04T10:07:21.978Z","avatar_url":"https://github.com/douban.png","language":"Objective-C","funding_links":[],"categories":["Index","Objective-C"],"sub_categories":["[Rexxar](https://github.com/douban/rexxar-android)"],"readme":"# Rexxar iOS\n\n[![Test Status](https://travis-ci.org/douban/rexxar-ios.svg?branch=master)](https://travis-ci.org/douban/rexxar-ios)\n[![IDE](https://img.shields.io/badge/XCode-8-blue.svg)]()\n[![iOS](https://img.shields.io/badge/iOS-7.0-green.svg)]()\n[![Language](https://img.shields.io/badge/language-ObjC-blue.svg)](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html)\n\n**[README in English](https://github.com/douban/rexxar-ios/blob/master/README_EN.md)**\n\n**Rexxar** 是一个针对移动端的混合开发框架。现在支持 Android 和 iOS 平台。`Rexxar-iOS` 是 Rexxar 在 iOS 系统上的客户端实现。\n\n通过 Rexxar，你可以使用包括 javascript，css，html 在内的传统前端技术开发移动应用。Rexxar 的客户端实现 Rexxar Container 对于 Web 端使用何种技术并无要求。我们现在的 Rexxar 的前端实现 Rexxar Web，以及 Rexxar Container 在两个平台的实现 Rexxar-iOS 和 Rexxar-Android 项目中所带的 Demo 都使用了 [React](https://facebook.github.io/react/)。但你完全可以选择自己的前端框架在 Rexxar Container 中进行开发。\n\nRexxar-iOS 现在支持 iOS 7 及以上版本。\n\n\n## Rexxar 简介\n\n关于 Rexxar 的整体介绍，可以看看这篇博客：[豆瓣的混合开发框架 -- Rexxar](http://lincode.github.io/Rexxar-OpenSource)。\n\n关于 Rexxar 的设计思想，可以看看这篇问答：[Infoq 采访：豆瓣混合开发框架 Rexxar](http://lincode.github.io/Rexxar-Interview)。\n\nRexxar 包含三个库：\n\n- Rexxar Web ：[https://github.com/douban/rexxar-web](https://github.com/douban/rexxar-web)。\n- Rexxar Android：[https://github.com/douban/rexxar-android](https://github.com/douban/rexxar-android)。\n- Rexxar iOS：[https://github.com/douban/rexxar-ios](https://github.com/douban/rexxar-ios)。\n\n## 安装\n\n### 安装 Cocoapods\n\n[CocoaPods](http://cocoapods.org) 是一个 Objective-c 和 Swift 的依赖管理工具。你可以通过以下命令安装 CocoaPods：\n\n```bash\n$ gem install cocoapods\n```\n\n### Podfile\n\n```ruby\ntarget 'TargetName' do\n  pod 'Rexxar', :git =\u003e 'https://github.com/douban/rexxar-ios.git', :commit =\u003e '0.2.1'\nend\n```\n\n然后，运行以下命令：\n\n```bash\n$ pod install\n```\n\n\n## 使用\n\n你可以查看 Demo 中的例子。了解如何使用 Rexxar。Demo 给出了完善的示例。\n\nDemo 中使用 github 的 raw 文件服务提供一个简单的路由表文件 routes.json，demo.html 以及相关 javascript 资源的访问服务。在你的线上服务中，当然会需要一个真正的生产环境，以应付更大规模的路由表文件，以及 javascript，css，html 资源文件的访问。你可以使用任何服务端框架。Rexxar 对服务端框架并无要求。`RXRConfig` 提供了对路由表文件地址的配置接口。下一节描述了配置方法。\n\n### 配置 RXRConfig\n\n#### 设置路由表文件 api：\n\n```Swift\n  RXRConfig.setRoutesMapURL(NSURL(string:\"https://raw.githubusercontent.com/douban/rexxar-web/master/example/dist/routes.json\")!)\n```\n\nRexxar 使用 url 来标识页面。提供一个正确的 url 就可以创建对应的 RXRViewController。路由表提供了每个 url 对应的 html 资源的下载地址。Demo 中的路由表如下：\n\n```json\n{\n    \"items\": [{\n        \"remote_file\": \"https://raw.githubusercontent.com/douban/rexxar-web/master/example/dist/rexxar/demo-252452ae58.html\",\n        \"deploy_time\": \"Sun, 09 Oct 2016 07:43:47 GMT\",\n        \"uri\": \"douban://douban.com/rexxar_demo[/]?.*\"\n    }],\n    \"partial_items\": [{\n        \"remote_file\": \"https://raw.githubusercontent.com/douban/rexxar-web/master/example/dist/rexxar/demo-252452ae58.html\",\n        \"deploy_time\": \"Sun, 09 Oct 2016 07:43:47 GMT\",\n        \"uri\": \"douban://partial.douban.com/rexxar_demo/_.*\"\n    }],\n    \"deploy_time\": \"Sun, 09 Oct 2016 07:43:47 GMT\",\n}\n```\n\n#### 设置预置资源文件路径\n\n```Swift\n  RXRConfig.setRoutesResourcePath(\"rexxar\")\n```\n\n使用 Rexxar 一般会预置一份路由表，以及资源文件在应用包中。这样就可以减少用户的下载，加快第一次打开页面的速度。在没有网络的情况下，如果没有数据请求的话，页面也可访问。这都有利于用户体验。\n\n注意，如果设置了预置资源文件路径，即意味着在应用包内预置一份资源文件。这个文件夹需要是 folder references 类型，即在 Xcode 中呈现为蓝色文件夹图标。创建方法是将文件夹拖入 Xcode 项目，选择 Create folder references 选项。\n\n#### 设置缓存路径\n\n```Swift\n  RXRConfig.setRoutesCachePath(\"com.douban.RexxarDemo.rexxar\")\n```\n\n缓存文件夹存在的目的也是减少资源文件的下载次数，加快打开页面的速度。使得用户可以得到近似原生页面的页面加载体验。\n\n缓存资源文件一般会出现在 Rexxar 部署了一次路由表的更新之后。这也是 Rexxar 支持`热部署`的方法：路由表控制资源文件的更新。一般可以让应用定期访问路由表。比如，在开启应用时，或者关闭应用时更新路由表。更新路由表的方法如下：\n\n```Swift\n  RXRViewController.updateRouteFiles(completion: nil)\n```\n\n如果，新的路由表中出现了 html 文件的更新，或者出现了新的 url。也就是说这些文件并不存在于预置资源文件夹中，Rexxar Container 就会在下载完路由表之后，主动下载新资源，并将新资源储存在缓存文件夹中。\n\n#### 预置资源文件和缓存文件关系\n\n正常程序逻辑下，预置资源文件夹存在的资源，就不会再去服务器下载，也就不会有缓存的资源文件。\n\n在进入一个 RXRViewController 时，会读取资源文件。在读取时，Rexxar Container 先读取缓存文件，如果存在就使用缓存文件。如果缓存文件不存在，就读取预置资源文件。如果，预置资源文件也不存在。RXRViewController 会尝试更新一次路由表，下载路由表中新出现的资源，并再次尝试读取缓存资源文件。如果仍然不存在，就会出现页面错误。\n\n读取顺序如下：\n\n1. 缓存文件夹中读取 html 文件；\n2. 预置资源文件夹中读取 html 文件；\n3. 重新下载路由表 Routes.json，遍历路由表将新的 html 文件下载到缓存文件夹。再次尝试从缓存文件夹读取 html 文件；\n\n以上三步中，任何一步读取成功就停止，并返回读取的结果。如果，三步都完成了仍没有找到文件，就会出现页面错误。\n\n有了预置资源文件和缓存文件的双重保证，一般用户打开 Rexxar 页面时都不会临时向服务器请求资源文件。这大大提升了用户打开页面的体验。\n\n### 使用 RXRViewController\n\n你可以直接使用 `RXRViewController` 作为你的混合开发客户端容器。或者你也可以继承 `RXRViewController`，在 `RXRViewController` 基础上实现你自己的客户端容器。在 Demo 中，创建了 `FullRXRViewController`，它继承于 `RXRViewController`。\n\n为了初始化 RXRViewController，你只需要一个 url。在路由表文件 api 提供的路由表中可以找到这个 url。这个 url 标识了该页面所需使用的资源文件的位置。Rexxar Container 会通过 url 在路由表中寻找对应的 javascript，css，html 资源文件。\n\n```Swift\n  let controller = RXRViewController(URI: uri)\n  let titleWidget = RXRNavTitleWidget()\n  let alertDialogWidget = RXRAlertDialogWidget()\n  controller.activities = [titleWidget, alertDialogWidget]\n  navigationController?.pushViewController(controller, animated: true)\n```\n\n\n## 定制你自己的 Rexxar Container\n\n首先，可以继承 `RXRViewController`，在 `RXRViewController` 基础上以实现你自己客户端容器。\n\n然后，可以使用 Rexxar 提供的三个接口。下面会介绍如何使用这三个接口，更方便地扩展属于自己的特定功能。\n\n### 定制 RXRWidget\n\nRexxar Container 提供了一些原生 UI 组件，供 Rexxar Web 使用。RXRWidget 是一个 Objective-C 协议（Protocol）。该协议是对这类原生 UI 组件的抽象。如果，你需要实现某些原生 UI 组件，例如，弹出一个 Toast，或者添加原生效果的下拉刷新，你就可以实现一个符合 RXRWidget 协议的类，并实现以下三个方法：`canPerformWithURL:`，`prepareWithURL:`，`performWithController:`。\n\n在 Demo 中可以找到一个例子：`RXRNavTitleWidget` ，通过它可以设置导航栏的标题文字。\n\n```Objective-C\n@interface RXRNavTitleWidget ()\n\n@property (nonatomic, copy) NSString *title;\n\n@end\n\n\n@implementation RXRNavTitleWidget\n\n- (BOOL)canPerformWithURL:(NSURL *)URL\n{\n  NSString *path = URL.path;\n  if (path \u0026\u0026 [path isEqualToString:@\"/widget/nav_title\"]) {\n    return true;\n  }\n  return false;\n}\n\n- (void)prepareWithURL:(NSURL *)URL\n{\n  self.title = [[URL rxr_queryDictionary] rxr_itemForKey:@\"title\"];\n}\n\n- (void)performWithController:(RXRViewController *)controller\n{\n  if (controller) {\n    controller.title = self.title;\n  }\n}\n\n@end\n```\n\n### 定制 RXRContainerAPI\n\n我们常常需要在 Rexxar Container 和 Rexxar Web 之间做数据交互。比如 Rexxar Container 可以为 Rexxar Web 提供一些计算结果。如果你需要提供一些由原生代码计算的数据给 Rexxar Web 使用，你就可以选择实现 RXRContainerAPI 协议（Protocol），并实现以下三个方法：`shouldInterceptRequest:`, `responseWithRequest:`, `responseData`。\n\n在 Demo 中可以找到一个例子：`RXRGeoContainerAPI`。这个例子中，`RXRGeoContainerAPI` 返回了设备所在城市，以及经纬度信息。当然，这个 ContainerAPI 仅仅是一个示例，它提供的是一个假数据，数据永远不会变化。你当然可以遵守 `RXRContainerAPI` 协议，实现一个类似的但是数据是真实的功能。\n\n### 定制 RXRDecorator\n\n如果你需要修改运行在 Rexxar Container 中的 Rexxar Web 所发出的请求。例如，在 http 头中添加登录信息，你可以实现 `RXRDecorator` 协议（Protocol），并实现这两个方法：`shouldInterceptRequest:`, `prepareWithRequest:`。\n\n在 Demo 的 `FullRXRViewController` 类中，可以找到一个使用 `RXRRequestDecorator` 添加登录信息，和 URL 参数的例子。这个例子为 Rexxar Web 发出的请求添加了登录信息，并在 URL 参数中增加了 apikey 信息。\n\n### 使用 RXRContainerAPI 和 RXRDecorator\n\nRXRContainerAPI 和 RXRDecorator 生效期应该和 RXRViewController 的生命周期一致。\n\n这就是说可以在 RXRViewController 的 `viewDidLoad:` 方法中注册 RXRContainerAPI 和 RXRDecorator：\n\n```Swift\noverride func viewDidLoad() {\n  super.viewDidLoad()\n\n  ...\n\n  URLProtocol.registerClass(RXRContainerInterceptor.self)\n\n  ...\n\n  URLProtocol.registerClass(RXRRequestInterceptor.self)\n}\n```\n\n可以在 Objective-C 的 `dealloc` 或者 Swift 的 `deinit` 方法中取消 RXRContainerAPI 和 RXRDecorator 的注册:\n\n```Swift\ndeinit {\n  URLProtocol.unregisterClass(RXRContainerInterceptor.self)\n  URLProtocol.unregisterClass(RXRRequestInterceptor.self)\n}\n```\n\n在 Demo 中的 `FullRXRViewController` 可以看到如何注册和取消注册 RXRContainerAPI 和 RXRDecorator。\n\n### 定制 RXRProxy\n\nRXRProxy 是本地请求代理服务，当一个请求允许被本地服务代理时，可以直接返回本地内容（类似 ContainerAPI），当请求不能被本地服务代理时，继续原来的请求(类似 RequestInterceptor)。\n\n## Partial RXRViewController\n\n如果，你发现一个页面无法全部使用 Rexxar 实现。你可以在一个原生页面内内嵌一个 RXRViewController，部分功能使用原生实现，另一部分功能使用 Rexxar 实现。\n\nDemo 中的 PartialRexxarViewController 给出了一个示例。\n\n\n## Rexxar 的公开接口\n\n* Rexxar Container\n  - `RXRConfig`\n  - `RXRViewController`\n\n* Widget\n  - `RXRWidget`\n  - `RXRNavTitleWidget`\n  - `RXRAlertDialogWidget`\n  - `RXRPullRefreshWidget`\n\n* ContainerAPI\n  - `RXRNSURLProtocol`\n  - `RXRContainerInterceptor`\n  - `RXRContainerAPI`\n\n* Decorator\n  - `RXRRequestInterceptor`\n  - `RXRDecorator`\n  - `RXRRequestDecorator`\n\n* Util\n  - `NSURL+Rexxar`\n  - `NSDictionary+RXRMultipleItem`\n\n\n## Changelog\n- 0.3.1   iOS 13.4及以上的系统，可以设置RXRConfig.useCustomScheme = true来拦截rexxar-http和rexxar-https请求，避免直接拦截http和https请求。\n- 0.3.0   使用 WKWebView 替换 UIWebView, 由于 WKWebView 对 NSURLProtocol 支持不够友好，你需要特别关心一下 NSURLProtocol 截获 Post 请求时 Body 被清空的问题。\n- master 2018-12-28 配置文件 `RXRConfig` 删除 `+ (void)setExternalUserAgent:(NSString *)userAgent;` 和 `+ (NSString *)externalUserAgent;`新增 `+ (**void**)setRxrUserAgent:(NSString *)userAgent;` 和 `+ (NSString *)rxrUserAgent;` \n\n## Unit Test\n\n在项目的 RexxarTests 文件夹下可以找到一系列单元测试。这些单元测试可以通过命令 `cmd+u` 在 Xcode 中运行。单元测试除了可以验证代码的正确性之外，还提供了如何使用这些代码的示例。可以查看这些单元测试，以了解如何使用 Rexxar。\n\n\n## License\n\nThe MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdouban%2Frexxar-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdouban%2Frexxar-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdouban%2Frexxar-ios/lists"}