├── .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 | 
36 |
37 | ## 小程序双线程模型模拟
38 |
39 | ~~先看一下运行结果
40 |
41 | 
42 |
43 | 接下来我们将用 iOS 代码来模拟上述的双线程模型。首先我们来实现视图层与逻辑层的数据通讯
44 |
45 | 
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 | 
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
--------------------------------------------------------------------------------