WebViewJavascriptBridge 是一个可以让 OC 与 JS 进行交互通信的第三方开源库。相比其他热门的第三方库,WebViewJavascriptBridge 代码量比较少,并且设计优雅巧妙,可以说是 “小而美”。
WebViewJavascriptBridge 库在 OC 端和 JS 端都有对等的逻辑实现,事先注册 handler,内部维护一个消息队列。透明的 iframe HTML 元素和 webview 的 stringByEvaluatingJavaScriptFromString 是通信的关键。OC 端发消息给 JS 端比较直观,调起 stringByEvaluatingJavaScriptFromString 执行脚本传入消息即可。JS 端发消息给 OC 端,需要事先把消息存到队列中,然后借助 iframe 发起一个伪请求,伪请求会被 webview 的代理方法拦截下来,OC 端因此得知 JS 端消息队列中有消息,最后调起 stringByEvaluatingJavaScriptFromString 方法解析 JS 方法拿到队列中的消息并处理。交互流程见下图:
整个库只有以下几个文件:
WebViewJavascriptBridge.h WebViewJavascriptBridge.m WKWebViewJavascriptBridge.h WKWebViewJavascriptBridge.m WebViewJavascriptBridgeBase.h WebViewJavascriptBridgeBase.m WebViewJavascriptBridge_JS.h WebViewJavascriptBridge_JS.m 一般使用只需要关注 WebViewJavascriptBridge 类提供的接口,这个类的主要职责是用来做 Mac 和 iOS webview 的适配(包括 WKWebView,但是这部分代理出去给 WKWebViewJavaScriptBridge 类)并为客户端提供便利的使用接口。WebViewJavascriptBridgeBase 类负责有关数据加工、消息队列管理、消息派发及回调的处理工作。WebViewJavascriptBridge_JS 类包含 JS 端的实现代码,通过宏处理返回 JS 端实现代码的一个 OC 字符串,便于在适当时机将其注入到文档模型中完成 bridge 的初始化。
在 iOS 开发中,一般打发布包都是在本地打包,也就是工程师在自己开发电脑上使用 Xcode 编译并导出安装包来进行发布,为了提高效率可能会制作一些自动化打包脚本。本文聊的是远程打包的内容,通过资源拷贝及参数替换然后编译完成打包。
由于 HTML5 跨平台的特点,很多技术团队考虑到代码复用,在部分模块中会采用 h5 来描述界面。甚至有些不需要太复杂交互的 app,全部界面采用 h5 来编写,也就是一个 web 工程。对于大部分现有的 web 工程,能打包成 app 就已经满足了业务诉求。DCloud 团队开发的 HBuilder(IDE)工具中提供了云打包的功能,用起来很方便,简单的说,就是把 web 工程上传到云打包服务器,最后打包生成 app,点击下载即可安装使用。
虽然云打包服务很方便,但上传源码总感觉不太妥当,总有些秘密不想让别人看见,并且其他同事也有打包的需求,但不一定会使用 HBuilder。因此,搭建一个自己的打包服务很有必要。
按照 HBuilder 提供的云打包功能,先定一个初步的需求:
支持修改应用 id、版本号 、icon、启动图 支持导入签名文件 开工!!!
准备工作 首先,需要一台安装了 MacOS 的电脑(当做服务器使用)。
笔者手头上刚好有台闲置的电脑就拿来当服务器使用了,装了 WMWare,然后装了 MacOS 虚拟机(问题较多,不建议使用虚拟机)。
物理机 windows7,内存 4G;虚拟机 MacOS,内存 3G。
其次,在服务器上部署一个 web 服务,提供打包交互界面方便客户端上传资源文件及下载安装包。我们的界面只提供了一个 www zip 包的上传入口,所有应用资源及打包相关的配置文件都在里面。www 目录结构如下:
appConfig.json 文件内容 { "id":"com.domain.pack", "appName":"我的应用", "debug":true, "launchPath": "index.html", "version": { "name": "1.0.0", "code": "100" }, ... } launchPath 对应 web 应用入口文件,iOS 工程使用这个文件路径作为 webview 的加载入口。
介绍 关于 Core Bluetooth Core Bluetooth 框架提供 iOS 应用和 Mac 应用与设备(配备了蓝牙低能耗无线技术的设备)通信的类。例如,应用可以发现、探测并与低能耗外围设备(比如心率监听器和数字恒温器)交互。从 macOS 10.9 和 iOS 6 开始,Mac 和 iOS 设备还可以当做蓝牙低能耗外设来使用,为其它设备提供数据,包括其他 Mac 和 iOS 设备。
一览 蓝牙低能耗无线技术基于蓝牙 4.0 规范,规范中除了别的之外,定义了与低能耗设备通信的一套协议。Core Bluetooth 框架是蓝牙低能耗协议栈的一个抽象,也就是说,它为开发者隐藏了许多规范中的底层细节,让开发者更加容易开发应用(与蓝牙低能耗设备交互的应用)。
中央和外围是 Core Bluetooth 的核心成员 在蓝牙低能耗通信中,有两个核心成员:中央(central)和外围(peripheral)。每个成员扮演不同的角色。外围通常拥有其他设备需要的数据,中央通常使用外围提供的信息来完成一些任务。例如,一个配备了蓝牙低能耗技术的数字恒温器可能为一个 iOS 应用提供房间的温度信息,然后该应用采用用户友好的方式来显示温度。
每个成员在扮演它的角色时都会执行一组不同的任务。外围通过在空中广播持有的数据来让自身的存在被感知,中央设备扫描附近的外围设备(可能包含中央设备感兴趣的数据)。当中央设备发现外围设备,中央设备就请求与外围设备连接并开始探测和交互外围设备的数据。外围设备负责以适当的方式来响应中央设备。
相关章节:Core Bluetooth Overview
Core Bluetooth 简化了一般的蓝牙任务 Core Bluetooth 框架抽离了蓝牙 4.0 规范中的底层细节。因此,应用中需要实现的一般蓝牙低能耗任务被简化了。如果开发实现中央角色的应用,Core Bluetooth 使得发现、连接外围设备和探测、交互外围数据变得简单。另外,Core Bluetooth 还让本地设备实现外围角色变得简单。
相关章节:Performing Commmon Central Role Tasks,Performing Common Peripheral Role Tasks
iOS 应用的状态影响蓝牙的表现 当应用处于后台或挂起状态时,蓝牙相关的特性会受到影响。在这两种状态下,默认是应用无法执行蓝牙低能耗任务。也就是说,如果应用需要在后台执行蓝牙低能耗任务,可以声明支持 Core Bluetooth 后台运行模式中的一个或两个(一个属于中央角色,另一个属于外围角色)。即使在你指定了一个后台运行模式或两个都指定,当应用处于后台时,某些蓝牙任务的执行依然会有所不同,设计应用时,需要考虑到这些差异。
即使应用支持后台处理,应用仍然可能在任意时刻被系统终止以清空内存给当前前台应用使用。在 iOS 7之后,Core Bluetooth 支持保存中央和外围管理者对象的状态信息并在应用启动的时候恢复该状态,可以使用这个特性来支持涉及蓝牙设备的长期活动(long-term actions)。
原文地址:本地和远程通知编程指南
应用中的通知 本地和远程通知概览 重要 这篇文档包含开发中有关 API 或技术的初步信息,这些信息可能会改变,并且根据这篇文档来实现的软件应当在最终的操作系统软件中进行测试。
本地通知和远程通知是在应用有新数据可用时通知用户的两种方式,即使此时应用不在前台运行。例如,短信应用可能会让用户知道有新的短信来了,日历应用可能会通知用户即将到来的约会。本地通知和远程通知的区别很简单:
对于本地通知,应用在本地配置通知的细节并把这些细节传给系统,然后由系统来处理通知的传递(当应用不在前台时)。iOS、tvOS、watchOS 都支持本地通知。 对于远程通知,使用公司服务器中的一个通过苹果推送通知服务把数据推送到用户的设备。iOS、tvOS、watchOS、macOS 都支持远程通知。 本地通知和远程通知都需要添加代码来支持应用中的通知的调度和处理。对于远程通知,必须提供一个服务器环境,该环境能够接收来自用户设备的数据和发送通知相关的数据到 苹果推送消息服务 (简称 APNs,由苹果提供的用来处理远程通知传递的服务)。
User Notifications 和 User Notifications UI 框架 从 iOS 10、watchOS 3、tvOS 10 开始,User Notifications 框架提供一致的方式来和处理本地通知。除了管理本地通知,该框架也支持远程通知的处理,然而远程通知的配置仍然需要一些平台特有的 API。因为这是一个独立的框架,所以可以在应用中或者扩展中使用,比如 WatchKit 扩展。
注意 macOS 上远程通知的配置和处理需要使用平台特有的方法(在 AppKit 框架中找)
User Notifications 框架也支持创建 通知服务应用扩展 (notification service app extension),它可以让你在远程通知传递之前修改通知的内容。如果在应用中包含通知服务应用扩展,系统会把收到的通知在传递给用户之前先传递给扩展。可以使用这类扩展来给应用的通知实现端到端的加密、在通知传递前修改其内容,又或者下载与通知相关的额外的图片或媒体文件。
User Notifications UI 框架是 User Notifications 的配套,它可以让你自定义系统的通知界面的外观。使用User Notifications UI 框架来定义 通知内容应用扩展(notification content app extension),它的任务就是提供一个包含自定义内容的视图控制器来显示在通知界面中。系统会显示自定义视图控制器而不是默认的系统界面。可以使用这种扩展在通知界面中加入多媒体或动态内容。
更多有关 User Notifications 框架的类的信息,请看 User Notifications Framework Reference。关于创建通知内容应用扩展的类的信息,请看 User Notifications UI Framework Reference。
在代理方法中拦截协议 使用 JavaScriptCore WKWebView 的 WKScriptMessagehandler 使用 NSURLProtocol 拦截请求 使用第三方库 WebViewJavascriptBridge 使用 WebSocket 这里只介绍第 6 种,其它的相关资料网上有很多。
使用 WebSocket 的方式需要在应用内起一个 websocket server 服务(有很多第三方的 websocket server 库),html 页面通过 Websocket 连接到服务,接着就是发送消息了,剩下的就跟代理方法拦截协议类似。
// OC code, 以 PocketSocket 这个库为例 _socketServer = [PSWebSocketServer serverWithHost:nil port:9001]; _socketServer.delegate = self; _socketServer.delegateQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); [_socketServer start]; #pragma mark - PSWebSocketServerDelegate - (void)serverDidStart:(PSWebSocketServer *)server { NSLog(@"Server did start…"); } - (void)serverDidStop:(PSWebSocketServer *)server { NSLog(@"Server did stop…"); } - (BOOL)server:(PSWebSocketServer *)server acceptWebSocketWithRequest:(NSURLRequest *)request { NSLog(@"Server should accept request: %@", request); return YES; } - (void)server:(PSWebSocketServer *)server webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message { // 在这里拦截 NSLog(@"Server websocket did receive message: %@", message); NSString *text = message; NSURL *url = [NSURL URLWithString:text]; if ([url.
HTTPS HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,不适合用来传输隐私信息。默认 80 端口。
Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了 SSL(Secure Sockets Layer) 协议用于对 Http 协议传输的数据进行加密,保证会话过程中的安全性。默认 443 端口。
SSL 包含对称加密和非对称加密,在建立传输链路时,SSL 首先使用非对称加密的方式对对称加密密钥进行加密,建立链路后,使用对称加密的方式对传输内容进行加密。非对称加密有更高的安全性,在这个基础上使用对称加密可以获得更快的速度,提高传输效率。
单向认证 客户端校验服务端证书
client hello 客户端发起一条到服务端的连接,包含客户端支持的 TLS 版本、支持的加密套件(即加密算法)以及客户端随机数。
server hello 服务端回应,包含服务端 SSL 证书、选择的加密套件以及服务端随机数。
authentication 客户端向颁发证书的 CA 验证服务端的 SSL 证书,以确认服务端是它声称的那个身份,从而保证客户端与域名真正的所有者通信。
send premaster key 客户端发送另一个随机数 “premaster key”,并对它使用服务端的公钥(从服务端 SSL 证书中获取得到)进行加密。
decrypt premaster key 服务端解密 premaster key
create session key 客户端和服务端各自使用客户端随机数、服务端随机数以及 premaster key 来计算 “session key”,即对称加密密钥,两边计算得出同样的结果并各自保留。
client send finished 客户端使用 session key 加密一条 finished 消息并发送给服务端。
国内 DCloud 团队推出的 HTML5+ 技术框架可以用来开发 Hybrid 应用。经过调研,我们决定试一试 。框架的核心原理是使用 iOS 系统原生 UIWebView 和 WKWebView 来加载资源并渲染界面,Native 的能力(如拍照、蓝牙)通过自定义插件来提供。
我们的应用有个需求,就是在 webview 加载完页面或者加载页面之前加入一些东西。比如:加载完页面后,根据 HTML 的 title 标签来设置导航栏标题。
原生想要插手页面加载周期,只能靠代理方法。但是因为没法修改源码,所以只能找其它办法。主要思路是:使用 Method Swizzle 找出代理对象然后再换掉代理方法实现。
以 UIWebView 为例,具体操作如下:
第一步,通过交换 setDelegate 的实现,找到目标代理对象所属的类;
UIWebView+Intercepter.m - (void)p_setDelegate:(id<UIWebViewDelegate>)delegate { [self p_setDelegate:delegate]; Class delegateClass = [self.delegate class]; // 进一步交换 delegateClass 的代理方法 [UIWebViewDelegateHook exchangeUIWebViewDelegateMethod:delegateClass]; } #pragma mark - Method Swizzling + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [super class]; // When swizzling a class method, use the following: // Class class = object_getClass((id)self); SEL originalSelector = @selector(setDelegate:); SEL swizzledSelector = @selector(p_setDelegate:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } 第二步,把目标代理对象所属类的代理方法实现换成我们自己写的方法实现。
iOS 10 开始对隐私权限更加严格, 如需使用隐私权限需要在工程的 info.plist 文件中声明,如果不声明程序在调用隐私权限(如相机)时应用程序会崩溃。
key 可以从下拉列表选择,value 为弹框提示文字(类型 String)
权限名称 Key 值 通讯录 NSContactsUsageDescription 麦克风 NSMicrophoneUsageDescription 相册 NSPhotoLibraryUsageDescription 相机 NSCameraUsageDescription 持续获取地理位置 NSLocationAlwaysUsageDescription 使用时获取地理位置 NSLocationWhenInUseUsageDescription 蓝牙 NSBluetoothPeripheralUsageDescription 语音转文字 NSSpeechRecognitionUsageDescription 日历 NSCalendarsUsageDescription
GCD(Grand Central Dispatch)是异步执行任务的技术之一。
一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的 Dispatch Queue 中,GCD 就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率。
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ /** * 长时间处理 * 例如:AR用图像识别、数据库访问 */ /** * 长时间处理结束,主线程使用该处理结果 */ dispatch_async(dispatch_get_main_queue(), ^{ /** * 只在主线程可以执行的处理 * 例如用户界面刷新 */ }); }); 在导入 GCD 之前,Cocoa 框架提供了 NSObject 类的performSelectorInBackground:withObject实例方法和performSelectorOnMainThread实例方法等简单的多线程编程技术。
线程
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派 CPU 的基本单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。 “一个 CPU 执行的 CPU 命令列为一条无分叉路径”即为“线程”。 现在一个物理的 CPU 芯片实际上有64个(64核)CPU,尽管如此,“一个 CPU 执行的 CPU 命令列为一条无分叉路径”仍然不变。 OS X 和 iOS 的核心 XNU 内核在发生操作系统事件时(如每隔一定时间,唤起系统调用等情况)会切换执行路径。执行中路径的状态,例如CPU的寄存器等信息保存到各自路径专用的内存块中,从切换目标路径专用的内存块中,复原 CPU 寄存器等信息,继续执行切换路径的 CPU 命令列。这称为“上下文切换”。 由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此看上去好像1个 CPU 核能够并列地执行多个线程一样。而且在具有多个 CPU 核的情况下,就不是“看上去像”了,而是真的提供了多个CPU核并行执行多个线程的技术。 使用多线程容易引发的常见问题
视图需要有确定的位置与大小才能正确显示在屏幕上。Auto Layout 使用对齐矩阵来确定视图的位置与大小,也就是所谓的约束。我们创建的每一条规则都规定了界面的一部分与另一部分的关系,某一部分可以由另一部分计算得出结果。
y = ax + b; 是一种线性关系。
创建约束的常见的方式:
Xib NSLayoutConstraint VFS 第一种,可以在(Interface Builder)IB中布局约束,并且根据需求自定义它们。
第二种,可以使用代码创建单个约束。NSLayoutConstraint 类提供constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:contant:方法,可以让你每次创建一个约束,它将某项的属性关联到另一项。
第三种,使用可视化格式语言来表示各项是如何沿着垂直和水平坐标轴布局的。
所有约束都是 NSLayoutConstraint 类的成员,无论你是以何种方式创建它们的。每个约束都在一个 Objective—C 对象中存储y = ax + b规则,并且通过 Auto Layout 引擎来表达该规则。可视化约束 是另一种实现相同效果的工具。