├── .gitattributes ├── .gitignore ├── LICENSE ├── Podfile ├── README.md ├── fino-applet.xcodeproj └── project.pbxproj ├── fino-applet ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── README.md ├── ViewController.h ├── ViewController.m ├── applet │ ├── AppletService.h │ ├── AppletService.m │ ├── AppletViewController.h │ └── AppletViewController.m ├── main.m └── res │ ├── applet1 │ ├── service.html │ └── view.html │ ├── miniapps_close_dark@2x.png │ ├── miniapps_close_dark@3x.png │ ├── miniapps_more_dark@2x.png │ └── miniapps_more_dark@3x.png └── img ├── 01.jpg ├── 02.jpg └── fino-applet.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=objective-c 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | package-lock.json 4 | Gemfile.lock 5 | dist 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | out/ 23 | 24 | # CocoaPods 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | Pods/ 31 | 32 | # Do not track our workspace since it is created by CocoaPods 33 | *.xcworkspace 34 | .DS_Store 35 | build 36 | build-* 37 | out 38 | out-* 39 | Podfile.lock 40 | data.json* 41 | data1.json* 42 | Riot/QSFramworks 43 | stat.sh 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jiege 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, "9.0" 2 | source 'https://github.com/CocoaPods/Specs.git' 3 | 4 | inhibit_all_warnings! 5 | 6 | target "fino-applet" do 7 | pod 'FinApplet' 8 | end 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 深入小程序系列之一:小程序核心原理及模拟 2 | 3 | 本文将介绍小程序的核心视图层逻辑层分离架构,并通过 iOS 的代码来模拟这种双线程模型。 4 | 5 | ## 什么是小程序 6 | 7 | 小程序是一种新的移动应用程序格式,是一种依赖 Web 技术,但也集成了原生应用程序功能的混合解决方案。 8 | 9 | 目前市面上小程序平台微信、支付宝、百度、头条、京东、[凡泰](https://mp.finogeeks.com)等;小程序一些特性有助于填补 Web 和原生平台之间的鸿沟,因此小程序受到了一些超级应用程序的欢迎。 10 | 11 | - 它不需要安装,支持热更新。 12 | - 具备多个 Web 视图以提高性能。 13 | - 它提供了一些通过原生路径访问操作系统功能(原生接口)或数据的机制。 14 | - 它的内容通常更值得信赖,因为应用程序需要由平台验证。 15 | - 小程序可以分发到多个小程序平台(Web、原生应用,甚至是 OS)。这些平台还为小程序提供了入口,帮助用户轻松找到所需的应用。 16 | 17 | ## 小程序核心功能 18 | 19 | ### 分离视图层与逻辑层 20 | 21 | 在小程序中,视图层通常与逻辑层分离。 22 | 23 | - 视图层 View 负责渲染小程序页面,包括 Web 组件和原生组件渲染,可以将其视为混合渲染。例如,Web 组件渲染可以由 WebView 处理,但 WebView 不支持某些 Web 组件渲染,或者是性能受限;小程序还依赖于某些原生组件,例如地图、视频等。 24 | - 逻辑层 Service 是用主要用于执行小程序的 JS 逻辑。主要负责小程序的事件处理、API 调用和生命周期管理。扩展的原生功能通常来自宿主原生应用程序或操作系统,这些功能包括拍照、位置、蓝牙、网络状态、文件处理、扫描、电话等。它们通过某些 API 调用。当小程序调用原生 API 时,它会将 API 调用传递给扩展的原生功能,以便通过 JSBridge 进一步处理,并通过 JSBridge 从扩展的原生功能获取结果。Service 为每个 Render 建立连接,传输需要渲染的数据以进一步处理。 25 | - 如果事件由小程序页面中的组件触发,则此页面将向 Service 发送事件以进一步处理。同时,页面将等待 Service 发送的数据来重新渲染小程序页面。 26 | - 渲染过程可被视为无状态,并且所有状态都将存储在 Service 中。 27 | 28 | 视图层和逻辑层分离有很多好处: 29 | 30 | - 方便多个小程序页面之间的数据共享和交互。 31 | - 在小程序的生命周期中具有相同的上下文可以为具备原生应用程序开发背景的开发人员提供熟悉的编码体验。 32 | - Service 和 View 的分离和并行实现可以防止 JS 执行影响或减慢页面渲染,这有助于提高渲染性能。 33 | - 因为 JS 在 Service 层执行,所以 JS 里面操作的 DOM 将不会对 View 层产生影响,所以小程序是不能操作 DOM 结构的,这也就使得小程序的性能比传统的 H5 更好。 34 | 35 | ![image.png](https://upload-images.jianshu.io/upload_images/10272390-02e1d39aa10739dc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 36 | 37 | ## 小程序双线程模型模拟 38 | 39 | ~~先看一下运行结果 40 | 41 | ![record.gif](https://upload-images.jianshu.io/upload_images/10272390-66ba7e469b479f1a.gif?imageMogr2/auto-orient/strip) 42 | 43 | 接下来我们将用 iOS 代码来模拟上述的双线程模型。首先我们来实现视图层与逻辑层的数据通讯 44 | 45 | ![image.png](https://upload-images.jianshu.io/upload_images/10272390-df1a079be039cad8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 46 | 47 | 如上图所示,视图层与逻辑层都分别通过 JS Bridge 的 publish 和 subscribe 来实现数据的收发。 48 | 49 | ### 模拟实现 50 | 51 | 1. 视图层调用**JSBridge.publish**把事件传递给原生;参数: {eventName: '', data: {}} 52 | 53 | ```javascript 54 | //点击按钮,通知JS执行业务逻辑 55 | function onTest() { 56 | console.log('aaa') 57 | FinChatJSBridge.subscribe('PAGE_EVENT', function(params) { 58 | document.getElementById('testId').innerHTML = params.data.title 59 | }) 60 | FinChatJSBridge.publish('PAGE_EVENT', { 61 | eventName: 'onTest', 62 | data: {} 63 | }) 64 | } 65 | ``` 66 | 67 | 2. 原生 view 层收到 page 的事件,把事件传递转发给 service 层处理 68 | 69 | ```objective-c 70 | if ([message.name isEqualToString:@"publishHandler"]) { 71 | NSString *e = message.body[@"event"]; 72 | [self.service callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; 73 | } 74 | ``` 75 | 76 | 3. 原生 service 层收到原生 view 层的事件,通过 jsbridge 把事件及参数传递给视图 ervice 层执行 js 逻辑 77 | 78 | ```objective-c 79 | 80 | NSString *js = [NSString stringWithFormat:@"ServiceJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; 81 | [self evaluateJavaScript:js completionHandler:nil]; 82 | ``` 83 | 84 | 4. 视图 service,收到事件后,执行 JS 业务代码 85 | 86 | ```javascript 87 | var Page = { 88 | setData: function(data) { 89 | //向原生视图层发送更新数据信息 90 | ServiceJSBridge.publish('PAGE_EVENT', { 91 | eventName: 'onPageDataChange', 92 | data: data 93 | }) 94 | }, 95 | methods: { 96 | onTest: function() { 97 | // 执行JS方法,模拟小程序的setData,把数据更新到视图层 98 | Page.setData({ 99 | title: '我来自JS代码更新' 100 | }) 101 | console.log('my on Test') 102 | } 103 | } 104 | } 105 | var onWebviewEvent = function(fn) { 106 | ServiceJSBridge.subscribe('PAGE_EVENT', function(params) { 107 | console.log('FinChatJSBridge.subscribe') 108 | var data = params.data, 109 | eventName = params.eventName 110 | fn({ 111 | data: data, 112 | eventName: eventName 113 | }) 114 | }) 115 | } 116 | var doWebviewEvent = function(pEvent, params) { 117 | // do dom ready 118 | 119 | if (Page.methods.hasOwnProperty(pEvent)) { 120 | // 收到视图层的事件,执行JS对应的方法 121 | Page.methods[pEvent].call(params) 122 | } 123 | } 124 | ``` 125 | 126 | 5. 执行业务 JS 代码后,把数据更新传递给视图层去更新 UI 界面展示数据 127 | 128 | ```javascript 129 | ServiceJSBridge.publish('PAGE_EVENT', { 130 | eventName: 'onPageDataChange', 131 | data: data 132 | }) 133 | ``` 134 | 135 | 6. 原生 service 层收到视图 service 层的事件,把事件传递给原生视图层 136 | 137 | ```objective-c 138 | if ([message.name isEqualToString:@"publishHandler"]) { 139 | NSString *e = message.body[@"event"]; 140 | [self.controller callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; } 141 | ``` 142 | 143 | 7. 原生视图层把收到的事件,传递给视图 view 层 144 | 145 | ```javascript 146 | NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; 147 | [self evaluateJavaScript:js completionHandler:nil]; 148 | ``` 149 | 150 | 8. 视图 view 层,收到事件后,更新界面 151 | 152 | ```javascript 153 | FinChatJSBridge.subscribe('PAGE_EVENT', function(params) { 154 | document.getElementById('testId').innerHTML = params.data.title 155 | }) 156 | ``` 157 | 158 | ### 订阅数据回调 159 | 160 | ```javascript 161 | // 首先订阅数据回调 162 | JSBridge.subscribe('PAGE_EVENT', function(params) { 163 | // ... 这里对返回的数据进行处理 164 | }) 165 | // 向JS Bridge发布数据 166 | // eventName: 用于标识事件名 167 | // data: 为传递的数据 168 | JSBridge.publish('PAGE_EVENT', { eventName: 'onTest', data: {} }) 169 | ``` 170 | 171 | ### WKWebView 初始化, 172 | 173 | ```objective-c 174 | WKUserContentController *userContentController = [WKUserContentController new]; 175 | NSString *souce = @"window.__fcjs_environment='miniprogram'"; 176 | WKUserScript *script = [[WKUserScript alloc] initWithSource:souce injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true]; 177 | [userContentController addUserScript:script]; 178 | [userContentController addScriptMessageHandler:self name:@"publishHandler"]; 179 | 180 | WKWebViewConfiguration *wkWebViewConfiguration = [WKWebViewConfiguration new]; 181 | wkWebViewConfiguration.allowsInlineMediaPlayback = YES; 182 | wkWebViewConfiguration.userContentController = userContentController; 183 | 184 | if (@available(iOS 9.0, *)) { 185 | [wkWebViewConfiguration.preferences setValue:@(true) forKey:@"allowFileAccessFromFileURLs"]; 186 | } 187 | WKPreferences *preferences = [WKPreferences new]; 188 | preferences.javaScriptCanOpenWindowsAutomatically = YES; 189 | wkWebViewConfiguration.preferences = preferences; 190 | 191 | self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:wkWebViewConfiguration]; 192 | self.webView.clipsToBounds = YES; 193 | self.webView.allowsBackForwardNavigationGestures = YES; 194 | 195 | [self.view addSubview:self.webView]; 196 | NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"view.html" ofType:nil]; 197 | NSURL *fileURL = [NSURL fileURLWithPath:urlStr]; 198 | [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL]; 199 | ``` 200 | 201 | ### WKWebView 事件回调处理 202 | 203 | ```objective-c 204 | // 执行视图层事件回调 205 | - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)jsonParam 206 | { 207 | NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; 208 | [self evaluateJavaScript:js completionHandler:nil]; 209 | 210 | 211 | } 212 | 213 | - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void(^)(id result,NSError *error))completionHandler 214 | { 215 | 216 | [self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 217 | } 218 | 219 | #pragma mark - WKScriptMessageHandler 220 | // 视图层JSBridge请求接收处理 221 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 222 | { 223 | if ([message.name isEqualToString:@"publishHandler"]) { 224 | NSString *e = message.body[@"event"]; 225 | [self.service callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; 226 | } 227 | } 228 | ``` 229 | 230 | ### 视图层代码 231 | 232 | ```javascript 233 | function onTest() { 234 | console.log('aaa') 235 | FinChatJSBridge.subscribe('PAGE_EVENT', function(params) { 236 | document.getElementById('testId').innerHTML = params.data.title 237 | }) 238 | FinChatJSBridge.publish('PAGE_EVENT', { 239 | eventName: 'onTest', 240 | data: {} 241 | }) 242 | } 243 | ``` 244 | 245 | ```html 246 |
我来自视图层!
247 | 248 | ``` 249 | 250 | ### 逻辑层代码 251 | 252 | ```javascript 253 | // page 对像模拟 254 | var Page = { 255 | setData: function(data) { 256 | ServiceJSBridge.publish('PAGE_EVENT', { 257 | eventName: 'onPageDataChange', 258 | data: data 259 | }) 260 | }, 261 | methods: { 262 | onTest: function() { 263 | Page.setData({ 264 | title: '我来自JS代码更新' 265 | }) 266 | console.log('my on Test') 267 | } 268 | } 269 | } 270 | var onWebviewEvent = function(fn) { 271 | ServiceJSBridge.subscribe('PAGE_EVENT', function(params) { 272 | var data = params.data, 273 | eventName = params.eventName 274 | fn({ 275 | data: data, 276 | eventName: eventName 277 | }) 278 | }) 279 | } 280 | var doWebviewEvent = function(pEvent, params) { 281 | // do dom ready 282 | if (Page.methods.hasOwnProperty(pEvent)) { 283 | Page.methods[pEvent].call(params) 284 | } 285 | } 286 | onWebviewEvent(function(params) { 287 | var eventName = params.eventName 288 | var data = params.data 289 | return doWebviewEvent(eventName, data) 290 | }) 291 | ``` 292 | 293 | 文档中心: [Document](https://mp.finogeeks.com/mop/document/introduce/introduction/) 294 | 295 | 本文示例代码: [https://github.com/finogeeks/fino-applet](https://github.com/finogeeks/fino-applet) 296 | 297 | ## 联系我们,了解更多内容 298 | 299 | ![image](https://upload-images.jianshu.io/upload_images/10272390-64b35906f69cc383.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 300 | -------------------------------------------------------------------------------- /fino-applet.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 57066C0FA13E2CE313B56082 /* libPods-fino-applet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF5290D11226389B4CA65C3F /* libPods-fino-applet.a */; }; 11 | 9DB3D93923F55DCE0079BD3D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DB3D93823F55DCE0079BD3D /* AppDelegate.m */; }; 12 | 9DB3D93F23F55DCE0079BD3D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DB3D93E23F55DCE0079BD3D /* ViewController.m */; }; 13 | 9DB3D94223F55DCE0079BD3D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D94023F55DCE0079BD3D /* Main.storyboard */; }; 14 | 9DB3D94423F55DD00079BD3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D94323F55DD00079BD3D /* Assets.xcassets */; }; 15 | 9DB3D94723F55DD00079BD3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D94523F55DD00079BD3D /* LaunchScreen.storyboard */; }; 16 | 9DB3D94A23F55DD00079BD3D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DB3D94923F55DD00079BD3D /* main.m */; }; 17 | 9DB3D95423F55E080079BD3D /* view.html in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D95323F55E080079BD3D /* view.html */; }; 18 | 9DB3D95623F55E170079BD3D /* service.html in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D95523F55E170079BD3D /* service.html */; }; 19 | 9DB3D95923F582CA0079BD3D /* AppletViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DB3D95823F582CA0079BD3D /* AppletViewController.m */; }; 20 | 9DB3D95C23F5844D0079BD3D /* AppletService.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DB3D95B23F5844D0079BD3D /* AppletService.m */; }; 21 | 9DB3D96123FAA83B0079BD3D /* miniapps_close_dark@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D95D23FAA83B0079BD3D /* miniapps_close_dark@3x.png */; }; 22 | 9DB3D96223FAA83B0079BD3D /* miniapps_more_dark@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D95E23FAA83B0079BD3D /* miniapps_more_dark@2x.png */; }; 23 | 9DB3D96323FAA83B0079BD3D /* miniapps_close_dark@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D95F23FAA83B0079BD3D /* miniapps_close_dark@2x.png */; }; 24 | 9DB3D96423FAA83B0079BD3D /* miniapps_more_dark@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9DB3D96023FAA83B0079BD3D /* miniapps_more_dark@3x.png */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 00CC03A74A2876403632CEE4 /* Pods-fino-applet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-fino-applet.debug.xcconfig"; path = "Target Support Files/Pods-fino-applet/Pods-fino-applet.debug.xcconfig"; sourceTree = ""; }; 29 | 9DB3D93423F55DCE0079BD3D /* fino-applet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "fino-applet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 9DB3D93723F55DCE0079BD3D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 31 | 9DB3D93823F55DCE0079BD3D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 32 | 9DB3D93D23F55DCE0079BD3D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 33 | 9DB3D93E23F55DCE0079BD3D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 34 | 9DB3D94123F55DCE0079BD3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 35 | 9DB3D94323F55DD00079BD3D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 9DB3D94623F55DD00079BD3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 37 | 9DB3D94823F55DD00079BD3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 9DB3D94923F55DD00079BD3D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | 9DB3D95323F55E080079BD3D /* view.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = view.html; sourceTree = ""; }; 40 | 9DB3D95523F55E170079BD3D /* service.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = service.html; sourceTree = ""; }; 41 | 9DB3D95723F582CA0079BD3D /* AppletViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppletViewController.h; sourceTree = ""; }; 42 | 9DB3D95823F582CA0079BD3D /* AppletViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppletViewController.m; sourceTree = ""; }; 43 | 9DB3D95A23F5844D0079BD3D /* AppletService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppletService.h; sourceTree = ""; }; 44 | 9DB3D95B23F5844D0079BD3D /* AppletService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppletService.m; sourceTree = ""; }; 45 | 9DB3D95D23FAA83B0079BD3D /* miniapps_close_dark@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "miniapps_close_dark@3x.png"; sourceTree = ""; }; 46 | 9DB3D95E23FAA83B0079BD3D /* miniapps_more_dark@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "miniapps_more_dark@2x.png"; sourceTree = ""; }; 47 | 9DB3D95F23FAA83B0079BD3D /* miniapps_close_dark@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "miniapps_close_dark@2x.png"; sourceTree = ""; }; 48 | 9DB3D96023FAA83B0079BD3D /* miniapps_more_dark@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "miniapps_more_dark@3x.png"; sourceTree = ""; }; 49 | A622730ADD5AB218833441E6 /* Pods-MyApplet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyApplet.release.xcconfig"; path = "Target Support Files/Pods-MyApplet/Pods-MyApplet.release.xcconfig"; sourceTree = ""; }; 50 | DF5290D11226389B4CA65C3F /* libPods-fino-applet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-fino-applet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E28D2C31AC238C8D786DCE85 /* Pods-MyApplet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyApplet.debug.xcconfig"; path = "Target Support Files/Pods-MyApplet/Pods-MyApplet.debug.xcconfig"; sourceTree = ""; }; 52 | EF2D07278AD62BA044AA432B /* Pods-fino-applet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-fino-applet.release.xcconfig"; path = "Target Support Files/Pods-fino-applet/Pods-fino-applet.release.xcconfig"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 9DB3D93123F55DCE0079BD3D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 57066C0FA13E2CE313B56082 /* libPods-fino-applet.a in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 0394B1C0C87B7A8E30F9F600 /* Frameworks */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | DF5290D11226389B4CA65C3F /* libPods-fino-applet.a */, 71 | ); 72 | name = Frameworks; 73 | sourceTree = ""; 74 | }; 75 | 9DB3D92B23F55DCE0079BD3D = { 76 | isa = PBXGroup; 77 | children = ( 78 | 9DB3D93623F55DCE0079BD3D /* fino-applet */, 79 | 9DB3D93523F55DCE0079BD3D /* Products */, 80 | B3B47DB3AC71596393CCE512 /* Pods */, 81 | 0394B1C0C87B7A8E30F9F600 /* Frameworks */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 9DB3D93523F55DCE0079BD3D /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9DB3D93423F55DCE0079BD3D /* fino-applet.app */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 9DB3D93623F55DCE0079BD3D /* fino-applet */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9DB3D95123F55DEB0079BD3D /* res */, 97 | 9DB3D95023F55DE50079BD3D /* applet */, 98 | 9DB3D93723F55DCE0079BD3D /* AppDelegate.h */, 99 | 9DB3D93823F55DCE0079BD3D /* AppDelegate.m */, 100 | 9DB3D93D23F55DCE0079BD3D /* ViewController.h */, 101 | 9DB3D93E23F55DCE0079BD3D /* ViewController.m */, 102 | 9DB3D94023F55DCE0079BD3D /* Main.storyboard */, 103 | 9DB3D94323F55DD00079BD3D /* Assets.xcassets */, 104 | 9DB3D94523F55DD00079BD3D /* LaunchScreen.storyboard */, 105 | 9DB3D94823F55DD00079BD3D /* Info.plist */, 106 | 9DB3D94923F55DD00079BD3D /* main.m */, 107 | ); 108 | path = "fino-applet"; 109 | sourceTree = ""; 110 | }; 111 | 9DB3D95023F55DE50079BD3D /* applet */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 9DB3D95723F582CA0079BD3D /* AppletViewController.h */, 115 | 9DB3D95823F582CA0079BD3D /* AppletViewController.m */, 116 | 9DB3D95A23F5844D0079BD3D /* AppletService.h */, 117 | 9DB3D95B23F5844D0079BD3D /* AppletService.m */, 118 | ); 119 | path = applet; 120 | sourceTree = ""; 121 | }; 122 | 9DB3D95123F55DEB0079BD3D /* res */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 9DB3D95F23FAA83B0079BD3D /* miniapps_close_dark@2x.png */, 126 | 9DB3D95D23FAA83B0079BD3D /* miniapps_close_dark@3x.png */, 127 | 9DB3D95E23FAA83B0079BD3D /* miniapps_more_dark@2x.png */, 128 | 9DB3D96023FAA83B0079BD3D /* miniapps_more_dark@3x.png */, 129 | 9DB3D95223F55DF50079BD3D /* applet1 */, 130 | ); 131 | path = res; 132 | sourceTree = ""; 133 | }; 134 | 9DB3D95223F55DF50079BD3D /* applet1 */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 9DB3D95323F55E080079BD3D /* view.html */, 138 | 9DB3D95523F55E170079BD3D /* service.html */, 139 | ); 140 | path = applet1; 141 | sourceTree = ""; 142 | }; 143 | B3B47DB3AC71596393CCE512 /* Pods */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | E28D2C31AC238C8D786DCE85 /* Pods-MyApplet.debug.xcconfig */, 147 | A622730ADD5AB218833441E6 /* Pods-MyApplet.release.xcconfig */, 148 | 00CC03A74A2876403632CEE4 /* Pods-fino-applet.debug.xcconfig */, 149 | EF2D07278AD62BA044AA432B /* Pods-fino-applet.release.xcconfig */, 150 | ); 151 | path = Pods; 152 | sourceTree = ""; 153 | }; 154 | /* End PBXGroup section */ 155 | 156 | /* Begin PBXNativeTarget section */ 157 | 9DB3D93323F55DCE0079BD3D /* fino-applet */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 9DB3D94D23F55DD00079BD3D /* Build configuration list for PBXNativeTarget "fino-applet" */; 160 | buildPhases = ( 161 | 20D184C5C9FC6927EECD023A /* [CP] Check Pods Manifest.lock */, 162 | 9DB3D93023F55DCE0079BD3D /* Sources */, 163 | 9DB3D93123F55DCE0079BD3D /* Frameworks */, 164 | 9DB3D93223F55DCE0079BD3D /* Resources */, 165 | E603806120EC525308EFAE60 /* [CP] Embed Pods Frameworks */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = "fino-applet"; 172 | productName = MyApplet; 173 | productReference = 9DB3D93423F55DCE0079BD3D /* fino-applet.app */; 174 | productType = "com.apple.product-type.application"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | 9DB3D92C23F55DCE0079BD3D /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastUpgradeCheck = 1130; 183 | ORGANIZATIONNAME = finogeeks; 184 | TargetAttributes = { 185 | 9DB3D93323F55DCE0079BD3D = { 186 | CreatedOnToolsVersion = 11.3.1; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 9DB3D92F23F55DCE0079BD3D /* Build configuration list for PBXProject "fino-applet" */; 191 | compatibilityVersion = "Xcode 9.3"; 192 | developmentRegion = en; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = 9DB3D92B23F55DCE0079BD3D; 199 | productRefGroup = 9DB3D93523F55DCE0079BD3D /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 9DB3D93323F55DCE0079BD3D /* fino-applet */, 204 | ); 205 | }; 206 | /* End PBXProject section */ 207 | 208 | /* Begin PBXResourcesBuildPhase section */ 209 | 9DB3D93223F55DCE0079BD3D /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 9DB3D96423FAA83B0079BD3D /* miniapps_more_dark@3x.png in Resources */, 214 | 9DB3D95623F55E170079BD3D /* service.html in Resources */, 215 | 9DB3D96123FAA83B0079BD3D /* miniapps_close_dark@3x.png in Resources */, 216 | 9DB3D94723F55DD00079BD3D /* LaunchScreen.storyboard in Resources */, 217 | 9DB3D94423F55DD00079BD3D /* Assets.xcassets in Resources */, 218 | 9DB3D94223F55DCE0079BD3D /* Main.storyboard in Resources */, 219 | 9DB3D95423F55E080079BD3D /* view.html in Resources */, 220 | 9DB3D96323FAA83B0079BD3D /* miniapps_close_dark@2x.png in Resources */, 221 | 9DB3D96223FAA83B0079BD3D /* miniapps_more_dark@2x.png in Resources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXResourcesBuildPhase section */ 226 | 227 | /* Begin PBXShellScriptBuildPhase section */ 228 | 20D184C5C9FC6927EECD023A /* [CP] Check Pods Manifest.lock */ = { 229 | isa = PBXShellScriptBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | inputFileListPaths = ( 234 | ); 235 | inputPaths = ( 236 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 237 | "${PODS_ROOT}/Manifest.lock", 238 | ); 239 | name = "[CP] Check Pods Manifest.lock"; 240 | outputFileListPaths = ( 241 | ); 242 | outputPaths = ( 243 | "$(DERIVED_FILE_DIR)/Pods-fino-applet-checkManifestLockResult.txt", 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | shellPath = /bin/sh; 247 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 248 | showEnvVarsInLog = 0; 249 | }; 250 | E603806120EC525308EFAE60 /* [CP] Embed Pods Frameworks */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputFileListPaths = ( 256 | "${PODS_ROOT}/Target Support Files/Pods-fino-applet/Pods-fino-applet-frameworks-${CONFIGURATION}-input-files.xcfilelist", 257 | ); 258 | name = "[CP] Embed Pods Frameworks"; 259 | outputFileListPaths = ( 260 | "${PODS_ROOT}/Target Support Files/Pods-fino-applet/Pods-fino-applet-frameworks-${CONFIGURATION}-output-files.xcfilelist", 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | shellPath = /bin/sh; 264 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-fino-applet/Pods-fino-applet-frameworks.sh\"\n"; 265 | showEnvVarsInLog = 0; 266 | }; 267 | /* End PBXShellScriptBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 9DB3D93023F55DCE0079BD3D /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 9DB3D93F23F55DCE0079BD3D /* ViewController.m in Sources */, 275 | 9DB3D95923F582CA0079BD3D /* AppletViewController.m in Sources */, 276 | 9DB3D93923F55DCE0079BD3D /* AppDelegate.m in Sources */, 277 | 9DB3D95C23F5844D0079BD3D /* AppletService.m in Sources */, 278 | 9DB3D94A23F55DD00079BD3D /* main.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXSourcesBuildPhase section */ 283 | 284 | /* Begin PBXVariantGroup section */ 285 | 9DB3D94023F55DCE0079BD3D /* Main.storyboard */ = { 286 | isa = PBXVariantGroup; 287 | children = ( 288 | 9DB3D94123F55DCE0079BD3D /* Base */, 289 | ); 290 | name = Main.storyboard; 291 | sourceTree = ""; 292 | }; 293 | 9DB3D94523F55DD00079BD3D /* LaunchScreen.storyboard */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 9DB3D94623F55DD00079BD3D /* Base */, 297 | ); 298 | name = LaunchScreen.storyboard; 299 | sourceTree = ""; 300 | }; 301 | /* End PBXVariantGroup section */ 302 | 303 | /* Begin XCBuildConfiguration section */ 304 | 9DB3D94B23F55DD00079BD3D /* Debug */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ALWAYS_SEARCH_USER_PATHS = NO; 308 | CLANG_ANALYZER_NONNULL = YES; 309 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_ENABLE_OBJC_WEAK = YES; 315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_COMMA = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 322 | CLANG_WARN_EMPTY_BODY = YES; 323 | CLANG_WARN_ENUM_CONVERSION = YES; 324 | CLANG_WARN_INFINITE_RECURSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 | CLANG_WARN_STRICT_PROTOTYPES = YES; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = dwarf; 338 | ENABLE_STRICT_OBJC_MSGSEND = YES; 339 | ENABLE_TESTABILITY = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu11; 341 | GCC_DYNAMIC_NO_PIC = NO; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_OPTIMIZATION_LEVEL = 0; 344 | GCC_PREPROCESSOR_DEFINITIONS = ( 345 | "DEBUG=1", 346 | "$(inherited)", 347 | ); 348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 350 | GCC_WARN_UNDECLARED_SELECTOR = YES; 351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 352 | GCC_WARN_UNUSED_FUNCTION = YES; 353 | GCC_WARN_UNUSED_VARIABLE = YES; 354 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 355 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 356 | MTL_FAST_MATH = YES; 357 | ONLY_ACTIVE_ARCH = YES; 358 | SDKROOT = iphoneos; 359 | }; 360 | name = Debug; 361 | }; 362 | 9DB3D94C23F55DD00079BD3D /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_ANALYZER_NONNULL = YES; 367 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 368 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 369 | CLANG_CXX_LIBRARY = "libc++"; 370 | CLANG_ENABLE_MODULES = YES; 371 | CLANG_ENABLE_OBJC_ARC = YES; 372 | CLANG_ENABLE_OBJC_WEAK = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 380 | CLANG_WARN_EMPTY_BODY = YES; 381 | CLANG_WARN_ENUM_CONVERSION = YES; 382 | CLANG_WARN_INFINITE_RECURSION = YES; 383 | CLANG_WARN_INT_CONVERSION = YES; 384 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 385 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 386 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 387 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | ENABLE_NS_ASSERTIONS = NO; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu11; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 407 | MTL_ENABLE_DEBUG_INFO = NO; 408 | MTL_FAST_MATH = YES; 409 | SDKROOT = iphoneos; 410 | VALIDATE_PRODUCT = YES; 411 | }; 412 | name = Release; 413 | }; 414 | 9DB3D94E23F55DD00079BD3D /* Debug */ = { 415 | isa = XCBuildConfiguration; 416 | baseConfigurationReference = 00CC03A74A2876403632CEE4 /* Pods-fino-applet.debug.xcconfig */; 417 | buildSettings = { 418 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 419 | CODE_SIGN_STYLE = Automatic; 420 | INFOPLIST_FILE = "$(SRCROOT)/fino-applet/Info.plist"; 421 | LD_RUNPATH_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "@executable_path/Frameworks", 424 | ); 425 | PRODUCT_BUNDLE_IDENTIFIER = com.finogeeks.mop.MyApplet; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | TARGETED_DEVICE_FAMILY = "1,2"; 428 | }; 429 | name = Debug; 430 | }; 431 | 9DB3D94F23F55DD00079BD3D /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | baseConfigurationReference = EF2D07278AD62BA044AA432B /* Pods-fino-applet.release.xcconfig */; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | CODE_SIGN_STYLE = Automatic; 437 | INFOPLIST_FILE = "$(SRCROOT)/fino-applet/Info.plist"; 438 | LD_RUNPATH_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "@executable_path/Frameworks", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = com.finogeeks.mop.MyApplet; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | }; 446 | name = Release; 447 | }; 448 | /* End XCBuildConfiguration section */ 449 | 450 | /* Begin XCConfigurationList section */ 451 | 9DB3D92F23F55DCE0079BD3D /* Build configuration list for PBXProject "fino-applet" */ = { 452 | isa = XCConfigurationList; 453 | buildConfigurations = ( 454 | 9DB3D94B23F55DD00079BD3D /* Debug */, 455 | 9DB3D94C23F55DD00079BD3D /* Release */, 456 | ); 457 | defaultConfigurationIsVisible = 0; 458 | defaultConfigurationName = Release; 459 | }; 460 | 9DB3D94D23F55DD00079BD3D /* Build configuration list for PBXNativeTarget "fino-applet" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | 9DB3D94E23F55DD00079BD3D /* Debug */, 464 | 9DB3D94F23F55DD00079BD3D /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | /* End XCConfigurationList section */ 470 | }; 471 | rootObject = 9DB3D92C23F55DCE0079BD3D /* Project object */; 472 | } 473 | -------------------------------------------------------------------------------- /fino-applet/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /fino-applet/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "ViewController.h" 3 | #import 4 | 5 | @interface AppDelegate () 6 | 7 | @end 8 | 9 | @implementation AppDelegate 10 | 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | // 应用程序启动后自定义的覆盖点 14 | NSString *appKey = @"22LyZEib0gLTQdU3MUauAR1TgQsjmhH3rHM7vRrbY6UA"; 15 | FATConfig *config = [FATConfig configWithAppSecret:@"9628ad1684944587" appKey:appKey]; 16 | config.apiServer = @"https://mp.finogeeks.com"; 17 | config.apiPrefix = @"/api/v1/mop"; 18 | 19 | [[FATClient sharedClient] initWithConfig:config error:nil]; 20 | 21 | // 此时在self.window上挂载自定义的nav等属性 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | self.window.backgroundColor = [UIColor whiteColor]; 24 | UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init] ]; 25 | self.window.rootViewController = nav; 26 | [self.window makeKeyAndVisible]; 27 | 28 | return YES; 29 | } 30 | @end 31 | -------------------------------------------------------------------------------- /fino-applet/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /fino-applet/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /fino-applet/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /fino-applet/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /fino-applet/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSBluetoothPeripheralUsageDescription 29 | 是否允许【FinApplet】使用蓝牙? 30 | NSCalendarsUsageDescription 31 | 是否允许【FinApplet】使用日历? 32 | NSCameraUsageDescription 33 | 是否允许【FinApplet】使用你的相机? 34 | NSContactsUsageDescription 35 | 开启同步通讯录信息,可智能补全客户资料。如果系统提示确认权限,请点击“好”。 36 | NSLocationAlwaysUsageDescription 37 | 我们需要通过您的地理位置信息获取您周边的相关数据 38 | NSLocationWhenInUseUsageDescription 39 | 我们需要通过您的地理位置信息获取您周边的相关数据 40 | NSMicrophoneUsageDescription 41 | 是否允许【FinApplet】使用你的麦克风? 42 | NSPhotoLibraryUsageDescription 43 | 是否允许【FinApplet】使用语相册? 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /fino-applet/README.md: -------------------------------------------------------------------------------- 1 | # 在ReactNative中内嵌的小程序容器引擎的解析 2 | 3 | ## 主页面的启动流程 4 | ``` 5 | main.m =======================> AppDelegate.m 6 | =======================> ViewController.m 7 | =====OnTestClick()=====> AppletService.m 8 | =======================> AppletViewController.m 9 | =======================> view.html 10 | =======================> service.html 11 | ``` 12 | 13 | ## 各个文件的功能定义 14 | 15 | | File | Description | 16 | | :---------------------------------- | :-------------------------: | 17 | | [main.m](./main.m) | ? | 18 | | [AppDelegate.m](./AppDelegate.m) | ? | 19 | | [ViewController.m](./ViewController.m) | ? | 20 | | [AppletService.m](./AppletService.m) | ? | 21 | | [AppletViewController.m](./AppletViewController.m) | ? | 22 | | [view.html](./view.html) | ? | 23 | | [service.html](./service.html) | ? | 24 | 25 | 26 | ## 部分核心代码解析 27 | 28 | ##### `AppletSercice.m` 的执行机制: 29 | 30 | + js传过来的 31 | ``` 32 | userContentController() 33 | =======================> callSubscribeHandlerWithEvent() 34 | =======================> evaluateJavaScript() 35 | =======================> completionHandler 36 | ``` 37 | 38 | ##### `ViewController.m` 中 `onTestClick()`: 39 | 40 | ``` 41 | onTestClick(): (id) sender { 42 | self.service = [[AppletService alloc] init]; 43 | [self.service startApplet: self.navigationController] 44 | } 45 | ``` 46 | 47 | ##### `view.html` 中 `window.webkit.messageHandlers.postMessage()`将数据发送给 `service.html`: 48 | 49 | ```js 50 | window.webkit.messageHandlers.publishHandler.postMessage({ 51 | event: event, 52 | paramsString: paramsString, 53 | webviewIds: webviewIds 54 | }) 55 | ``` 56 | + (`AppletService.m`中)先将名为`publishHandler` 的 `ScriptMessageHandler` 注册到webkit: 57 | ``` 58 | [userContentController addScriptMessageHandler:self name:@"publishHandler"]; 59 | ``` 60 | 61 | > [适当参考下](https://lvwenhan.com/ios/461.html) Apple 在WkWebView中的js runtime里,事先注入了一个 window.webkit.messageHandlers.xxx.postMessage() 方法,我们可以使用这个方法直接向 Native 层传值,异常方便。首先,我们要把一个名为 xxx 的 ScriptMessageHandler 注册到我们的 wk。 62 | 63 | 64 | ## 零散疑问 65 | + [] 66 | 67 | ## 注入API的实现方式(自己想的) 68 | 69 | + 文件目录结构: 70 | ```md 71 | jsbridge 72 | - index.js 73 | - iOS/ 74 | - index.js 75 | - getUserInfo.js 76 | - xxx.js 77 | - android/ 78 | - index.js 79 | - getUserInfo.js 80 | - xxx.js 81 | ``` 82 | 83 | + `jsbridge/index.js`: 84 | ```js 85 | import iosBridge from './iOS'; 86 | import androidBridge from './android'; 87 | 88 | export default class Bridge { 89 | constructor() { 90 | super(); 91 | if (isAndroid) { 92 | this.jsbridge = androidBridge; 93 | } else { 94 | this.jsbridge = iosBridge; 95 | } 96 | } 97 | getUserInfo = (...args) => this.jsbridge.getUserInfo(...args); 98 | } 99 | ``` 100 | 101 | + `jsbridge/iOS/index.js`: 102 | ```js 103 | import getUserInfo from './getUserInfo'; 104 | export { 105 | getUserInfo 106 | } 107 | ``` 108 | 109 | + `jsbridge/iOS/getUserInfo.js` 110 | ```js 111 | import registerCallback from '../registerCallback'; 112 | 113 | export default function getUserInfo() { 114 | return new Promise((resolve, reject) => { 115 | try { 116 | window.webkit.messageHandlers.getUserInfo.postMessage({ 117 | callback: registerCallback(resolve), 118 | }); 119 | } catch (e) { 120 | reject(e); 121 | } 122 | }); 123 | } 124 | ``` 125 | 126 | + `jsbridge/android/index.js`: 127 | ```js 128 | import getUserInfo from './getUserInfo'; 129 | export { 130 | getUserInfo 131 | } 132 | ``` 133 | 134 | + `jsbridge/android/getUserInfo.js`: 135 | ```js 136 | import registerCallback from '../registerCallback'; 137 | 138 | export default function getUserInfo() { 139 | return new Promise((resolve, reject) => { 140 | try { 141 | window.webkit.messageHandlers.getUserInfo.postMessage({ 142 | callback: registerCallback(resolve), 143 | }); 144 | } catch (e) { 145 | reject(e); 146 | } 147 | }); 148 | } 149 | ``` -------------------------------------------------------------------------------- /fino-applet/ViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | 5 | @interface ViewController : UIViewController 6 | 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /fino-applet/ViewController.m: -------------------------------------------------------------------------------- 1 | #import "ViewController.h" // 2 | #import "AppletService.h" // 该文件在./applet/AppletService.h 3 | #import 4 | #import 5 | 6 | 7 | @interface ViewController () 8 | @property (nonatomic, strong) AppletService *service; 9 | @end 10 | 11 | @implementation ViewController 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | // Do any additional setup after loading the view. 16 | 17 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 40)]; 18 | [btn setTitle:@"打开小程序" forState:UIControlStateNormal]; 19 | [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 20 | [btn addTarget:self action:@selector(onTestClick:) forControlEvents:UIControlEventTouchUpInside]; 21 | btn.backgroundColor = [UIColor clearColor]; 22 | btn.tintColor = [UIColor redColor]; 23 | [btn.layer setMasksToBounds:YES]; 24 | [btn.layer setCornerRadius:10.0]; 25 | [btn.layer setBorderWidth:1.0]; 26 | [btn.layer setBorderColor: [UIColor grayColor].CGColor]; 27 | 28 | 29 | UIButton *btn1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 200, 40)]; 30 | [btn1 setTitle:@"打开画图小程序" forState:UIControlStateNormal]; 31 | [btn1 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 32 | [btn1 addTarget:self action:@selector(onCanvasClick:) forControlEvents:UIControlEventTouchUpInside]; 33 | btn1.backgroundColor = [UIColor orangeColor]; 34 | btn1.tintColor = [UIColor redColor]; 35 | [btn1.layer setMasksToBounds:YES]; 36 | [btn1.layer setCornerRadius:10.0]; 37 | [btn1.layer setBorderWidth:1.0]; 38 | [btn1.layer setBorderColor: [UIColor grayColor].CGColor]; 39 | 40 | UIButton *btn2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 250, 200, 40)]; 41 | [btn2 setTitle:@"打开官方小程序" forState:UIControlStateNormal]; 42 | [btn2 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 43 | [btn2 addTarget:self action:@selector(onDemoClick:) forControlEvents:UIControlEventTouchUpInside]; 44 | btn2.backgroundColor = [UIColor orangeColor]; 45 | btn2.tintColor = [UIColor redColor]; 46 | [btn2.layer setMasksToBounds:YES]; 47 | [btn2.layer setCornerRadius:10.0]; 48 | [btn2.layer setBorderWidth:1.0]; 49 | [btn2.layer setBorderColor: [UIColor grayColor].CGColor]; 50 | 51 | 52 | UIButton *btn3 = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 200, 40)]; 53 | [btn3 setTitle:@"智能对账单" forState:UIControlStateNormal]; 54 | [btn3 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 55 | [btn3 addTarget:self action:@selector(onProfileClick:) forControlEvents:UIControlEventTouchUpInside]; 56 | btn3.backgroundColor = [UIColor orangeColor]; 57 | btn3.tintColor = [UIColor redColor]; 58 | [btn3.layer setMasksToBounds:YES]; 59 | [btn3.layer setCornerRadius:10.0]; 60 | [btn3.layer setBorderWidth:1.0]; 61 | [btn3.layer setBorderColor: [UIColor grayColor].CGColor]; 62 | 63 | [self.view addSubview:btn]; 64 | [self.view addSubview:btn1]; 65 | [self.view addSubview:btn2]; 66 | [self.view addSubview:btn3]; 67 | 68 | } 69 | - (void)viewWillDisappear:(BOOL)animated{ 70 | self.navigationController.navigationBar.hidden = NO; 71 | } 72 | - (void)viewWillAppear:(BOOL)animated { 73 | self.navigationController.navigationBar.hidden = YES; 74 | 75 | } 76 | - (void)onTestClick:(id)sender { 77 | self.service = [[AppletService alloc] init]; 78 | // startApplet方法定义在: ./applet/AppletService.m中 79 | [self.service startApplet:self.navigationController]; 80 | } 81 | 82 | - (void)onFinAppletClick:(id)sender { 83 | NSString *appId = @"5e4d123647edd60001055df1"; 84 | // 打开小程序 85 | [[FATClient sharedClient] startRemoteApplet:appId startParams:nil InParentViewController:self completion:^(BOOL result, NSError *error) { 86 | NSLog(@"result:%d---error:%@", result, error); 87 | }]; 88 | } 89 | 90 | - (void)onCanvasClick:(id)sender { 91 | 92 | NSString *appId = @"5ea03fa563cb900001d73863"; 93 | // 打开小程序 94 | [[FATClient sharedClient] startRemoteApplet:appId startParams:@{ 95 | @"path": @"/pages/index/index", 96 | @"query": @"" 97 | } InParentViewController:self completion:^(BOOL result, NSError *error) { 98 | NSLog(@"result:%d---error:%@", result, error); 99 | }]; 100 | } 101 | 102 | - (void)onDemoClick:(id)sender { 103 | 104 | NSString *appId = @"5ea0401463cb900001d73865"; 105 | // 打开小程序 106 | [[FATClient sharedClient] startRemoteApplet:appId startParams:nil InParentViewController:self completion:^(BOOL result, NSError *error) { 107 | NSLog(@"result:%d---error:%@", result, error); 108 | }]; 109 | } 110 | 111 | - (void)onProfileClick:(id)sender { 112 | 113 | NSString *appId = @"5ea0412663cb900001d73867"; 114 | // 打开小程序 115 | [[FATClient sharedClient] startRemoteApplet:appId startParams:nil InParentViewController:self completion:^(BOOL result, NSError *error) { 116 | NSLog(@"result:%d---error:%@", result, error); 117 | }]; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /fino-applet/applet/AppletService.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface AppletService : NSObject 6 | 7 | -(void)startApplet:(UINavigationController*)nav; 8 | 9 | - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)param ; 10 | @end 11 | 12 | NS_ASSUME_NONNULL_END 13 | -------------------------------------------------------------------------------- /fino-applet/applet/AppletService.m: -------------------------------------------------------------------------------- 1 | #import "AppletService.h" 2 | #import "AppletViewController.h" 3 | #import 4 | 5 | @interface AppletService() 6 | @property (nonatomic, strong) WKWebView *webView; 7 | @property (nonatomic, strong) AppletViewController *controller; 8 | 9 | @end 10 | 11 | @implementation AppletService 12 | 13 | -(void)setupService { 14 | WKUserContentController *userContentController = [WKUserContentController new]; 15 | NSString *souce = @"window.__fcjs_environment='miniprogram'"; 16 | WKUserScript *script = [[WKUserScript alloc] initWithSource:souce injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true]; 17 | [userContentController addUserScript:script]; 18 | 19 | // 将名为publishHandler的ScriptMessageHandler 注册到webkit中: 20 | [userContentController addScriptMessageHandler:self name:@"publishHandler"]; 21 | 22 | WKWebViewConfiguration *wkWebViewConfiguration = [WKWebViewConfiguration new]; 23 | wkWebViewConfiguration.allowsInlineMediaPlayback = YES; 24 | // 真正注入publishHandler的地方 25 | wkWebViewConfiguration.userContentController = userContentController; 26 | 27 | if (@available(iOS 9.0, *)) { 28 | [wkWebViewConfiguration.preferences setValue:@(true) forKey:@"allowFileAccessFromFileURLs"]; 29 | } 30 | WKPreferences *preferences = [WKPreferences new]; 31 | preferences.javaScriptCanOpenWindowsAutomatically = YES; 32 | wkWebViewConfiguration.preferences = preferences; 33 | 34 | // 此处利用上面的配置,修改webkit的初始化函数(让webkit初始化时就拥有publishHandler等自定义的方法) 35 | self.webView = [[WKWebView alloc] initWithFrame:(CGRect){0,0,1,1} configuration:wkWebViewConfiguration]; 36 | NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"service.html" ofType:nil]; 37 | NSURL *fileURL = [NSURL fileURLWithPath:urlStr]; 38 | [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL]; 39 | } 40 | 41 | -(void)startApplet:(UINavigationController*)nav { 42 | self.controller = [[AppletViewController alloc] init]; 43 | self.controller.service = self; 44 | [nav pushViewController:self.controller animated:YES]; 45 | [self setupService]; 46 | } 47 | 48 | - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)jsonParam { 49 | NSString *js = [NSString stringWithFormat:@"ServiceJSBridge.subscribeHandler('%@',%@)", eventName, jsonParam]; 50 | // 该函数将event和paramsString转换成js的字符串,并交给evaluateJavaScript函数处理 51 | [self evaluateJavaScript:js completionHandler:nil]; 52 | } 53 | 54 | - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void(^)(id result,NSError *error))completionHandler { 55 | [self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 56 | } 57 | 58 | // 此处是在原生内部注册一个js接口(如publishHandler)的处理函数 59 | #pragma mark - WKScriptMessageHandler 60 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 61 | { 62 | // 如果传过来的message.name是publishHandler时, 63 | if ([message.name isEqualToString:@"publishHandler"]) { 64 | NSString *e = message.body[@"event"]; 65 | // 取出postMessage传过来的data中的event和paramsString,交给callSubscribeHandlerWithEvent函数处理 66 | [self.controller callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; } 67 | } 68 | @end 69 | -------------------------------------------------------------------------------- /fino-applet/applet/AppletViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppletService.h" 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface AppletViewController : UIViewController 6 | @property (nonatomic, weak) AppletService *service; 7 | - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)jsonParam; 8 | 9 | @end 10 | 11 | NS_ASSUME_NONNULL_END 12 | -------------------------------------------------------------------------------- /fino-applet/applet/AppletViewController.m: -------------------------------------------------------------------------------- 1 | #import "AppletViewController.h" 2 | #import 3 | 4 | @interface AppletViewController () 5 | 6 | @property (nonatomic, strong) WKWebView *webView; 7 | 8 | 9 | @end 10 | 11 | @implementation AppletViewController 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | // Do any additional setup after loading the view. 16 | [self.navigationController.navigationItem setHidesBackButton:YES]; 17 | [self.navigationItem setHidesBackButton:YES]; 18 | [self.navigationController.navigationBar.backItem setHidesBackButton:YES]; 19 | 20 | UIButton *cancleButton = [UIButton buttonWithType:UIButtonTypeSystem]; 21 | UIImage *imgClose = [UIImage imageNamed:@"miniapps_close_dark"]; 22 | 23 | [cancleButton setImage:imgClose forState:UIControlStateNormal]; 24 | [cancleButton addTarget:self action:@selector(onClose:) forControlEvents:UIControlEventTouchUpInside]; 25 | UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]initWithCustomView:cancleButton]; 26 | rightItem.imageInsets = UIEdgeInsetsMake(0, -15,0, 0);//设置向左偏移 27 | self.navigationItem.rightBarButtonItem = rightItem; 28 | 29 | 30 | WKUserContentController *userContentController = [WKUserContentController new]; 31 | NSString *souce = @"window.__fcjs_environment='miniprogram'"; 32 | WKUserScript *script = [[WKUserScript alloc] initWithSource:souce injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true]; 33 | [userContentController addUserScript:script]; 34 | [userContentController addScriptMessageHandler:self name:@"publishHandler"]; 35 | 36 | WKWebViewConfiguration *wkWebViewConfiguration = [WKWebViewConfiguration new]; 37 | wkWebViewConfiguration.allowsInlineMediaPlayback = YES; 38 | wkWebViewConfiguration.userContentController = userContentController; 39 | 40 | if (@available(iOS 9.0, *)) { 41 | [wkWebViewConfiguration.preferences setValue:@(true) forKey:@"allowFileAccessFromFileURLs"]; 42 | } 43 | WKPreferences *preferences = [WKPreferences new]; 44 | preferences.javaScriptCanOpenWindowsAutomatically = YES; 45 | // preferences.minimumFontSize = 40.0; 46 | wkWebViewConfiguration.preferences = preferences; 47 | 48 | self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:wkWebViewConfiguration]; 49 | self.webView.clipsToBounds = YES; 50 | self.webView.allowsBackForwardNavigationGestures = YES; 51 | 52 | [self.view addSubview:self.webView]; 53 | // 此处加载并打开view.html 54 | NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"view.html" ofType:nil]; 55 | NSURL *fileURL = [NSURL fileURLWithPath:urlStr]; 56 | [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL]; 57 | 58 | 59 | } 60 | 61 | /* 62 | #pragma mark - Navigation 63 | 64 | // In a storyboard-based application, you will often want to do a little preparation before navigation 65 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 66 | // Get the new view controller using [segue destinationViewController]. 67 | // Pass the selected object to the new view controller. 68 | } 69 | */ 70 | 71 | 72 | - (void)onClose:(id)sender { 73 | [self.navigationController popViewControllerAnimated:YES]; 74 | 75 | } 76 | - (void)callSubscribeHandlerWithEvent:(NSString *)eventName param:(NSString *)jsonParam 77 | { 78 | NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.subscribeHandler('%@',%@)",eventName,jsonParam]; 79 | [self evaluateJavaScript:js completionHandler:nil]; 80 | 81 | 82 | } 83 | 84 | - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void(^)(id result,NSError *error))completionHandler 85 | { 86 | 87 | [self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 88 | } 89 | 90 | #pragma mark - WKScriptMessageHandler 91 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 92 | { 93 | if ([message.name isEqualToString:@"publishHandler"]) { 94 | NSString *e = message.body[@"event"]; 95 | [self.service callSubscribeHandlerWithEvent:e param:message.body[@"paramsString"]]; 96 | } 97 | } 98 | @end 99 | -------------------------------------------------------------------------------- /fino-applet/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | NSString * appDelegateClassName; 6 | @autoreleasepool { 7 | // Setup code that might create autoreleased objects goes here. 8 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 9 | } 10 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 11 | } 12 | -------------------------------------------------------------------------------- /fino-applet/res/applet1/service.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 158 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /fino-applet/res/applet1/view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 166 | 176 | 177 | 178 |
我来自视图层!
179 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /fino-applet/res/miniapps_close_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/fino-applet/res/miniapps_close_dark@2x.png -------------------------------------------------------------------------------- /fino-applet/res/miniapps_close_dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/fino-applet/res/miniapps_close_dark@3x.png -------------------------------------------------------------------------------- /fino-applet/res/miniapps_more_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/fino-applet/res/miniapps_more_dark@2x.png -------------------------------------------------------------------------------- /fino-applet/res/miniapps_more_dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/fino-applet/res/miniapps_more_dark@3x.png -------------------------------------------------------------------------------- /img/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/img/01.jpg -------------------------------------------------------------------------------- /img/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/img/02.jpg -------------------------------------------------------------------------------- /img/fino-applet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekeol/containerEngine/327103d001a77b32d579e2827b7500412fc53589/img/fino-applet.gif --------------------------------------------------------------------------------