本文从属于笔者的iOS入门与最佳实践系列文章,最近又被AppStore提交审核搞的要死要死的,所以在本文略做总结。
AppStore
Reference
证书
AppStore的证书与绑定机制对于笔者感觉还是蛮复杂的,不过流程多走即便可能就会清晰很多。首先需要明白的是,在苹果的证书体系中,Certificates与Identifiers是相互独立的,笔者之前经常有个疑问就是最早提交某个APP审核的开发者的P12丢失了,那后续重新申请的证书还可以用于提交后续版本吗?答案肯定是可以的。一般来说,某个Certificates+Identifiers会得到一个MobileProvision文件,而该文件就是在进行本地开发、打包上传时所必须的。
iOS有两种证书和描述文件:
证书类型 | 使用场景 |
---|---|
开发(Development)证书和描述文件 | 用于开发测试 |
发布(Distribution)证书和描述文件 | 用于提交Appstore |
App ID & Bundle Identifier
在苹果官方的开发者计划(Apple Developer Member Center)层面,App ID 即 Product ID,用于标识一个或者一组 App。
在 Mac/iOS 开发语境中,bundle(捆绑) 是指一个内部结构按照标准规则组织的特殊目录。在 Mac OS 应用程序目录下的某个 *.app 上可右键显示包内容(Contents),其本质上就是可执行二进制文件(MacOS/)及其资源(Resources/)的打包组合。因此,在 Xcode 中配置的 Bundle Identifier 必须和 App ID 是一致的(Explicit)或匹配的(Wildcard)。
App ID 字符串通常以反域名(reverse-domain-name)格式的 Company Identifier(Company ID)作为前缀(Prefix/Seed),一般不超过 255 个 ASCII 字符。
App ID 全名会被追加 Application Identifier Prefix(一般为 TeamID.),分为两类:
Explicit App ID:唯一的 App ID,用于唯一标识一个应用程序。例如“com.apple.garageband”这个 App ID,用于标识 Bundle Identifier 为“com.apple.garageband”的 App。
Wildcard App ID:含有通配符的 App ID,用于标识一组应用程序。例如“”(实际上是 Application Identifier Prefix)表示所有应用程序;而“com.apple.”可以表示 Bundle Identifier 以“com.apple.”开头(苹果公司)的所有应用程序。
用户可在 Developer Member Center 网站上注册(Register)或删除(Delete)已注册的 App IDs。App ID 被配置到【XcodeTarget|Info|Bundle Identifier】下;对于 Wildcard App ID,只要 bundle identifier 包含其作为 Prefix/Seed 即可。
Certificates
iOS 证书是用来证明 iOS App 内容(bundle with executable and resources)的合法性和完整性的数字证书。对于想安装到真机或发布到 AppStore 的应用程序(App),只有经过签名验证(Signature Validated)才能确保来源可信,并且保证 App 内容是完整、未经篡改的。
iOS 证书分为两类:Development 和 Production(Distribution)。
Development 证书用来开发和调试应用程序:A development certificate identifies you, as a team member, in a development provisioning profile that allows apps signed by you to launch on devices.
Production 主要用来分发应用程序(根据证书种类有不同作用):A *distribution certificate identifies your team or organization in a distribution provisioning profile and allows you to *submit your app to the store. Only a team agent or an admin can create a distribution certificate.
普通个人开发账号最多可注册 iOS Development/Distribution 证书各2个,用户可在网站上删除(Revoke)已注册的 Certificate。Apple 证书颁发机构 WWDRCA(Apple Worldwide Developer Relations Certification Authority) 将使用其 private key 对 CSR 中的 public key 和一些身份信息进行加密签名生成数字证书(ios_development.cer)并记录在案(Apple Member Center)。
而P12文件,正是用于在多台设备间共享证书。在 Keychain Access|Certificates 中选中欲导出的 certificate 或其下 private key,右键 Export 或者通过菜单 File|Export Items 导出 Certificates.p12——PKCS12 file holds the private key and certificate。其 他 Mac 机器上双击 Certificates.p12(如有密码需输入密码)即可安装该共享证书。有了共享证书之后,在开发者网站上将欲调试的 iOS 设备注册到该开发者账号名下,并下载对应证书授权了 iOS 调试设备的 Provisioning Profile 文件,方可在 iOS 真机设备上开发调试。
Provision Profiles
Provisioning Profile 文件包含了上述的所有内容:证书、App ID 和 设备 ID。
一个 Provisioning Profile 对应一个 Explicit App ID 或 Wildcard App ID(一组相同 Prefix/Seed 的 App IDs)。在网站上手动创建一个 Provisioning Profile 时,需要依次指定 App ID(单选)、证书(Certificates,可多选)和设备(Devices,可多选)。用户可在网站上删除(Delete)已注册的 Provisioning Profiles。Provisioning Profile 决定 Xcode 用哪个证书(公钥)/私钥组合(Key Pair/Signing Identity)来签署应用程序(Signing Product),并将在应用程序打包时嵌入到 .ipa 包里。安装应用程序时,Provisioning Profile 文件被拷贝到 iOS 设备中,运行该 iOS App 的设备通过它来认证安装的程序。如果要打包到真机上运行一个APP,一般要经历以下三步:
首先,需要指明它的 App ID,并且验证 Bundle ID 是否与其一致;
其次,需要证书对应的私钥来进行签名,用于标识这个 APP 是合法、安全、完整的;
然后,如果是真机调试,需要确认这台设备是否授权运行该 APP。
Provisioning Profile 把这些信息全部打包在一起,方便我们在调试和发布程序打包时使用。这样,只要在不同的情况下选择不同的 Provisioning Profile 文件就可以了。Provisioning Profile 也分为 Development 和 Distribution 两类,有效期同 Certificate 一样。Distribution 版本的 ProvisioningProfile 主要用于提交 App Store 审核,其中不指定开发测试的Devices(0,unlimited)。App ID 为 Wildcard App ID(*)。App Store 审核通过上架后,允许所有 iOS 设备(Deployment Target)上安装运行该App。Xcode 将全部供应配置文件(包括用户手动下载安装的和 Xcode 自动创建的 Team Provisioning Profile)放在目录 ~/Library/MobileDevice/Provisioning Profiles 下。
Xcode 7 免证书调试
所谓“免证书”真机调试,并不是真的不需要证书,Xcode真机调试原有的证书配置体系仍在——All iOS, tvOS, and watchOS appsmust be code signed and provisioned to launch on a device. 所以,上文啰嗦几千字还是有点用的。
自 Xcode7 开始,原来基于付费开发者账号及自助生成证书及配置文件的繁琐过程被苹果简化,Xcode将针对任何普通账号自动为联调真机生成所需相关的证书及配置文件。当你打算向 App Store 提交发布应用,才需要付费。
第一步:进入 Xcode Preferences|Accounts,添加自己的 Apple ID 账号。
第二步:Build Settings|Code Signing 下的 Provisioning Profile 选择 Automatic,Code Signing Identity 选择 Automatic 下的iOS Developer。
第三步:General 配置 Bundle identifier,Team 下拉选择苹果Member Center自动为你的账号生成的Personal Team ID。
自己的账号在调试公司或其他第三方APP代码时,若填写 Bundle identifier 为他人账号注册的 APP ID(例如苹果相机应用 com.apple.camera),会报错:
No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) matching the bundle identifier “com.apple.camera” were found.
即使编译通过了,可能运行时APP自身与服务器校验也可能会报签名错误,肿么办???
Her skill:此时,可以在他人原有App ID基础上添加后缀(例如com.apple.camera.extension),配置成应用的衍生插件(相当于置于同一 App Group 下)就可以快乐的玩耍了。
如果启动APP时,Xcode报错“process launch failed: Security”或iPhone报错【不受信任的开发者】,此时需要到iPhone通用配置中的描述文件(最新系统中可能叫设备管理)中,在描述文件(开发商应用)中选择对应的描述文件(你的Apple ID)点击 信任 或 验证 即可。
内容/功能 Issue
UGC社区审核
笔者之前做过体育社交类产品,提交的时候被拒了,原因是没有添加内容举报的功能。
IPv6 Issue
自2016年6月1日起,苹果要求所有提交App Store的iOS应用必须支持IPv6-only环境,背景也是众所周知的,IPv4地址已基本分配完毕,同时IPv6比IPv4也更加高效,向IPv6过渡是大势所趋。
IPv6 Only
首先需要明确一点,在App Store审核APP的IPv6-only的环境下也是可以正常访问IPv4的服务的,只是首先由DNS64将解析出来的IPv4地址转成兼容的IPv6地址,然后访问IPv4服务时通过NAT64网关对IPv4和IPv6进行NAT,并不需要客户有实际的IPv6服务。如下图所示:
客户端在向DNS64请求一个域名的IPv6地址时,DNS64会向域名的授权DNS请求IPv6地址,如果存在IPv6地址,则直接给客户端返回IPv6地址,如果不存在IPv6地址,则向授权请求IPv4地址,并将返回的IPv4地址转换为兼容的IPv6地址。
以Google DNS64为例说明转换规则,分别请求dnspod.cn的A记录(IPv4地址)和AAAA记录(IPv6地址):
从解析结果可以看出IPv4地址对应的IPv6地址,后32位的3b25:7465实际上就是IPv4地址的16进制表示59=0x3b,37=0x25,116=0x74,101=0x65,明白该规则后也可以自己进行IPv4向兼容的IPv6地址的转换,如119.29.29.29的兼容IPv6地址为64:ff9b::771d:1d1d,其中::表示为全0。
DNS64解析流程如下图所示:
搭建IPv6测试环境
用Mac做一个热点,然后用iPhone连接这个Wi-Fi,在“System Preferences”界面选中“Sharing”的同时,要按住“Option”键。
之后在“Sharing”界面中,我们会看到和之前不一样的地方,就是红框所标的地方,多了一个叫“Create NAT64 Network”的选框,选中它。
开发修正
避免使用底层的网络API
下图展示的蓝色部分的这些API都是不存在兼容性问题的,而我们平时自己用的包括那些第三方的网络库大部分都是用的这些API。
大部分情况下,我们用高级的API完全能够实现我们的需求,而且高级API封装的很便于使用,很多底层的像适配IPv6的工作都已经帮我们做好了。而用底层API会有大量的工作要我们自己来做,更容易产生bug。但你如果确实需要用底层的POSIX socket API, 请参照这个RFC4038: Application Aspects of IPv6 Transition的指导。
避免直接使用IP地址
比如下面这个API,nodename这个参数不要传IP地址,而应该用域名
这个方法在著名的第三方Reachability中是用到的,我们常用的第三方网络库AFNetworking就用了这个。所以用到的同学得好好查一下了。
检查不兼容的IPv6代码
inet_addr()
inet_aton()
inet_lnaof()
inet_makeaddr()
inet_netof()
inet_network()
inet_ntoa()
inet_ntoa_r()
bindresvport()
getipv4sourcefilter()
setipv4sourcefilter()
以及如下的一些数据类型: