本文从属于笔者的iOS入门与最佳实践系列文章,最近又被AppStore提交审核搞的要死要死的,因此在本文略作总结。html
AppStore的证书与绑定机制对于笔者感受仍是蛮复杂的,不过流程多走即使可能就会清晰不少。首先须要明白的是,在苹果的证书体系中,Certificates与Identifiers是相互独立的,笔者以前常常有个疑问就是最先提交某个APP审核的开发者的P12丢失了,那后续从新申请的证书还能够用于提交后续版本吗?答案确定是能够的。通常来讲,某个Certificates+Identifiers会获得一个MobileProvision文件,而该文件就是在进行本地开发、打包上传时所必须的。
iOS有两种证书和描述文件:git
证书类型 | 使用场景 |
---|---|
开发(Development)证书和描述文件 | 用于开发测试 |
发布(Distribution)证书和描述文件 | 用于提交Appstore |
在苹果官方的开发者计划(Apple Developer Member Center)层面,App ID 即 Product ID,用于标识一个或者一组 App。github
在 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。app
Wildcard App ID:含有通配符的 App ID,用于标识一组应用程序。例如“”(其实是 Application Identifier Prefix)表示全部应用程序;而“com.apple.”能够表示 Bundle Identifier 以“com.apple.”开头(苹果公司)的全部应用程序。dom
用户可在 Developer Member Center 网站上注册(Register)或删除(Delete)已注册的 App IDs。App ID 被配置到【XcodeTarget|Info|Bundle Identifier】下;对于 Wildcard App ID,只要 bundle identifier 包含其做为 Prefix/Seed 便可。
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 真机设备上开发调试。
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真机调试原有的证书配置体系仍在——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)点击 信任 或 验证 便可。
笔者以前作过体育社交类产品,提交的时候被拒了,缘由是没有添加内容举报的功能。
自2016年6月1日起,苹果要求全部提交App Store的iOS应用必须支持IPv6-only环境,背景也是众所周知的,IPv4地址已基本分配完毕,同时IPv6比IPv4也更加高效,向IPv6过渡是大势所趋。
首先须要明确一点,在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解析流程以下图所示:
用Mac作一个热点,而后用iPhone链接这个Wi-Fi,在“System Preferences”界面选中“Sharing”的同时,要按住“Option”键。
以后在“Sharing”界面中,咱们会看到和以前不同的地方,就是红框所标的地方,多了一个叫“Create NAT64 Network”的选框,选中它。
下图展现的蓝色部分的这些API都是不存在兼容性问题的,而咱们平时本身用的包括那些第三方的网络库大部分都是用的这些API。
大部分状况下,咱们用高级的API彻底可以实现咱们的需求,并且高级API封装的很便于使用,不少底层的像适配IPv6的工做都已经帮咱们作好了。而用底层API会有大量的工做要咱们本身来作,更容易产生bug。但你若是确实须要用底层的POSIX socket API, 请参照这个RFC4038: Application Aspects of IPv6 Transition的指导。
好比下面这个API,nodename这个参数不要传IP地址,而应该用域名
这个方法在著名的第三方Reachability中是用到的,咱们经常使用的第三方网络库AFNetworking就用了这个。因此用到的同窗得好好查一下了。
inet_addr() inet_aton() inet_lnaof() inet_makeaddr() inet_netof() inet_network() inet_ntoa() inet_ntoa_r() bindresvport() getipv4sourcefilter() setipv4sourcefilter()
以及以下的一些数据类型: