iOS9适配系列教程

连接地址:http://www.open-open.com/lib/view/open1443194127763.htmlphp

中文快速导航:

  1. iOS9网络适配_ATS:改用更安全的HTTPS(见Demo1)
    1. WHAT(什么是SSL/TLS?跟HTTP和HTTPS有什么关系)
    2. WHY(之前的HTTP不是也能用吗?为何要用SSL/TLS?Apple是否是又在反人类?)
    3. HOW(如何适配?---弱弱地问下:加班要多久?)
      1. 第1种状况:HTTPS Only (只有HTTPS,全部状况下都使用ATS)
      2. 第2种状况:Mix & Match(混合)
      3. 第3种状况:Opt Out(禁用ATS)
      4. 第4种状况:Opt Out With Exceptions(除特殊状况外,都不使用ATS)
      5. Certificate Transparency
    4. Q-A
  2. iOS9新特性_更灵活的后台定位(见Demo2)
  3. 企业级分发
    1. iOS9之后,企业级分发ipa包将遭到与Mac上dmg安装包同样的待遇:默认不能安装,也再也不出现“信任按钮”
    2. iOS9之后,企业分发时可能存在:下载的ipa包与网页二者的 bundle ID 没法匹配而致使下载失败的状况
  4. Bitcode
  5. iOS9 URL Scheme 适配_引入白名单概念(见Demo3)
    1. 常见 URL Scheme
    2. Q-A
  6. iPad适配Slide Over 和 Split View
  7. 字体间隙变大致使 UI 显示异常
  8. 升级 Xcode7 后的崩溃与警告
    1. iOS9 下使用 Masonry 会引发崩溃的一种状况
    2. Xcode 升级后,旧的状态栏的样式设置方式会引发警告
      1. Demo4---navigationController状态栏样式新的设置方法
    3. Xcode7 在 debug 状态下也生成 .dSYM 文件引发的警告
    4. Xcode7 没法使用 8.x 系统的设备调试,一运行就报错 there is an intenal API error
    5. 使用了 HTML 的 iframe 元素可能致使没法从 Safari 跳转至 App
  9. Demo五、Demo6--- 搜索 API

English:arrow_down::arrow_down:

For more infomation ,welcome to follow my twitterhtml

1. Demo1_You'd better Convert HTTP to HTTPS

How to deal with the SSL in iOS9,One solution is to do like: enter image description hereios

As the Apple say :git

enter image description here

enter image description here

enter image description here

iOS 9 and OSX 10.11 require TLSv1.2 SSL for all hosts you plan to request data from unless you specify exception domains in your app's Info.plist file.github

The syntax for the Info.plist configuration looks like this:web

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!--Include to allow insecure HTTP requests-->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!--Include to specify minimum TLS version-->
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>

If your application (a third-party web browser, for instance) needs to connect to arbitrary hosts, you can configure it like this:算法

<key>NSAppTransportSecurity</key>
<dict>
    <!--Connect to anything (this is probably BAD)-->
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

If you're having to do this, it's probably best to update your servers to use TLSv1.2 and SSL, if they're not already doing so. This should be considered a temporary workaround.chrome

As of today, the prerelease documentation makes no mention of any of these configuration options in any specific way. Once it does, I'll update the answer to link to the relevant documentation.api

If your server is support TLSv1.2 ,but you also have to do what I say just now if you want to connect success in iOS9:xcode

After some discussion with Apple Support, the issue is due to the self signed certificate.

ATS trusts only certificate signed by a well known CA, all others are rejected. As a consequence the only solution with a Self signed certificate is to set an exception with NSExceptionDomains.

2.Demo2_iOS9 new feature in CoreLocation : background only when you need

If you're using CoreLocation framework in your app in Xcode7(pre-released),and you may notice that there is a newly added property called allowsBackgroundLocationUpdates in CLLocationManager class.

This new property is explained in the WWDC session "What's New in Core Location"enter image description here

The default value isNOif you link against iOS 9.

If your app uses location in the background (without showing the blue status bar) you have to setallowsBackgroundLocationUpdatestoYESin addition to setting the background mode capability in Info.plist. Otherwise location updates are only delivered in foreground. The advantage is that you can now have location managers with background location updates and other location managers with only foreground location updates in the same app. You can also reset the value toNOto change the behavior.

The documentation is pretty clear about it:

By default, this is NO for applications linked against iOS 9.0 or later, regardless of minimum deployment target.

With UIBackgroundModes set to include "location" in Info.plist, you must also set this property to YES at runtime whenever calling -startUpdatingLocation with the intent to continue in the background.

Setting this property to YES when UIBackgroundModes does not include "location" is a fatal error.

Resetting this property to NO is equivalent to omitting "location" from the UIBackgroundModes value. Access to location is still permitted whenever the application is running (ie not suspended), and has sufficient authorization (ie it has WhenInUse authorization and is in use, or it has Always authorization). However, the app will still be subject to the usual task suspension rules.

See -requestWhenInUseAuthorization and -requestAlwaysAuthorization for more details on possible authorization values.

Set Info.plist like: enter image description here

The syntax for the Info.plist configuration looks like this:

<key>NSLocationAlwaysUsageDescription</key>
<string>微博@iOS程序犭袁 请求后台定位权限</string>

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

Use like:

_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {
    [_locationManager requestAlwaysAuthorization];
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
    _locationManager.allowsBackgroundLocationUpdates = YES;
}
[_locationManager startUpdatingLocation];

3.iOS9 Untrusted Enterprise Developer with no option to trust

Since iOS9 there is no option to trust an enterprise build. Before iOS9,it's very easy to use:if you touch the app,it'll apear this :

enter image description here

Now:

enter image description here

You have to let the user do like: Go to Settings - General - Profiles - tap on your Profile - tap on Trust button.

enter image description here

4.bitcode optional

After Xcode 7,bitcode option will be enabled by default,If your library was compiled without bitcode but the bitcode option is enabled in your project settings.You can

  1. Update your library with bit code, or you'll get warnings like:

(null): URGENT: all bitcode will be dropped because '/Users/myname/Library/Mobile Documents/com~apple~CloudDocs/foldername/appname/GoogleMobileAds.framework/GoogleMobileAds(GADSlot+AdEvents.o)' was built without bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. Note: This will be an error in the future.

  1. Say NO to Enable Bitcode in your target Build Settings

    enter image description here

and the Library Build Settings to remove the warnings

For more information,go to documentation of bitcode in developer library

,and WWDC 2015 Session 102: "Platforms State of the Union"

enter image description here

5.Privacy and Your App【URL scheme changes】

iOS 9 has made a small change to the handling of URL scheme. You must whitelist the url's that your app will call out to using theLSApplicationQueriesSchemeskey in yourInfo.plist.

Please see post here: http://awkwardhare.com/post/121196006730/quick-take-on-ios-9-url-scheme-changes

The main conclusion is that:

If you call the “canOpenURL” method on a URL that is not in your whitelist, it will return “NO”, even if there is an app installed that has registered to handle this scheme. A “This app is not allowed to query for scheme xxx” syslog entry will appear.

If you call the “openURL” method on a URL that is not in your whitelist, it will fail silently. A “This app is not allowed to query for scheme xxx” syslog entry will appear.

The author also speculates that this is a bug with the OS and Apple will fix this in a subsequent release.

This is a new security feature of iOS 9. Watch WWDC 2015 Session 703 for more information.

enter image description here

Any app built with SDK 9 needs to provide aLSApplicationQueriesSchemesentry in its plist file, declaring which schemes it attempts to query.

<key>LSApplicationQueriesSchemes</key>
<array>
 <string>urlscheme</string>
 <string>urlscheme2</string>
 <string>urlscheme3</string>
 <string>urlscheme4</string>
</array>

Assuming two apps TestA and TestB. TestB wants to query if TestA is installed. "TestA" defines the following URL scheme in its info.plist file:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>testA</string>
        </array>
    </dict>
</array>

The second app "TestB" tries to find out if "TestA" is installed by calling:

[[UIApplication sharedApplication]
                    canOpenURL:[NSURL URLWithString:@"TestA://"]];

But this will normally return NO in iOS9 because "TestA" needs to be added to the LSApplicationQueriesSchemes entry in TestB's info.plist file. This is done by adding the following code to TestB's info.plist file:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>TestA</string>
</array>

A working implementation can be found here: https://github.com/gatzsche/LSApplicationQueriesSchemes-Working-Example

6. Support Slide Over and Split View of iOS 9

enter image description here How to transition an an older project to support Slide Over and Split View of iOS 9? You may find that all the demo projects was written by storyboard or xib, but the older project's UI is written by code!

I would suggest switching to storyboards to make your life easy.

I would highly recommend you watch the following WWDC videos and then think about what exactly you need to do in order to support multi tasking.

  1. Mysteries of Auto Layout, Part 1

  2. What's New in Storyboards

  3. Implementing UI Designs in Interface Builder

  4. Getting Started with Multitasking on iPad in iOS 9

  5. Optimizing Your App for Multitasking on iPad in iOS

License

Posted by 微博@iOS程序犭袁

Creative Commons BY-NC-ND 3.0

中文

1. Demo1_iOS9网络适配_ATS:改用更安全的HTTPS

[摘要]为了强制加强数据访问安全, iOS9 默认会把 全部的http请求 全部从NSURLConnection、CFURL、NSURLSession发出的 HTTP 请求,都改成 HTTPS 请求:iOS9.x-SDK编译时,默认会让全部从NSURLConnection、CFURL、NSURLSession发出的 HTTP 请求统一采用TLS 1.2 协议。由于 AFNetworking 如今的版本底层使用了NSURLConnection,众多App将被影响(基于iOS8.x-SDK的App不受影响)。服务器所以须要更新,以解析相关数据。如不更新,可经过在 Info.plist 中声明,倒退回不安全的网络请求。而这一作法,官方文档称为ATS,全称为App Transport Security,是iOS9的一个新特性。

一个符合 ATS 要求的 HTTPS,应该知足以下条件:

  1. Transport Layer Security协议版本要求TLS1.2以上
  2. 服务的Ciphers配置要求支持Forward Secrecy等
  3. 证书签名算法符合ATS要求等

官方文档 App Transport Security Technote 对ATS 的介绍:

enter image description here

注:有童鞋反映:服务器已支持TLS 1.2 SSL ,但iOS9上仍是不行,还要进行本文提出的适配操做。

那是由于:要注意 App Transport Security 要求 TLS 1.2,并且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的,ATS只信任知名CA颁发的证书,小公司所使用的 self signed certificate,仍是会被ATS拦截。。所以慎重检查与你的应用交互的服务器是否是符合ATS的要求很是重要。对此,建议使用下文中给出的 NSExceptionDomains,并将大家公司的域名挂在下面。下文也会详细描述该问题。

官方文档 App Transport Security Technote 对CA颁发的证书要求:

Certificates must be signed using a SHA256 or better signature hash algorithm, with either a 2048 bit or greater RSA key or a 256 bit or greater Elliptic-Curve (ECC) key. Invalid certificates result in a hard failure and no connection

在讨论以前,跟往常同样,先说下iOS程序猿们最关心的问题:

跟我有毛关系?须要我加班吗?!

首先我们来看下业内对Apple这一作法的评论:

enter image description here

这是某社交App上讨论,看来业内仍是吐槽声和确定声同在。

结论是:

跟你颇有关系,加班吧,少年!

书归正传【严肃脸】,咱们正式讨论下 WHAT,WHY,HOW:

  1. WHAT(什么是SSL/TLS?跟HTTP和HTTPS有什么关系)
  2. WHY(之前的HTTP不是也能用吗?为何要用SSL/TLS?!Apple是否是又在反人类?)
  3. HOW(如何适配?---弱弱地问下:加班要多久?)

WHAT(什么是SSL/TLS?跟HTTP和HTTPS有什么关系)

什么是SSL/TLS? SSL你必定知道,在此不作赘述。主要说下什么是TLS,还有跟HTTP和HTTPS有什么关系。

TLS 是 SSL 新的别称:

“TLS1.0”之于“SSL3.1”,犹“公元2015”之于“民国104”,“一千克”之于“一公斤”:称呼不一样,意思相同。

SSL 3.0版本以后的迭代版本被从新命名为TLS 1.0:TLS 1.0=SSL 3.1。因此咱们日常也常常见到 “SSL/TLS” 这种说法。

目前,应用最普遍的是TLS 1.0,接下来是SSL 3.0。目前主流浏览器都已经实现了TLS 1.2的支持。

经常使用的有下面这些:

  • SSL 2.0
  • SSL 3.0
  • TLS 1.0 (SSL 3.1)
  • TLS 1.1 (SSL 3.1)
  • TLS 1.2 (SSL 3.1)

那为何标题是“使用HTTPS”而没有说起SSL和TLS什么事? “SSL/TLS”跟HTTP和HTTPS有什么关系?

要理解这个,要看下他们之间的关系:

HTTP+SSL/TLS+TCP = HTTPS

HTTP+SSL/TLS+TCP

或者

HTTPS = “HTTP over SSL”

也就是说:

Apple让你的HTTP采用SSL/TLS协议,就是让你从HTTP转到HTTPS。而这一作法,官方文档称为ATS,全称为App Transport Security。

WHY(之前的HTTP不是也能用吗?为何要用SSL/TLS?Apple是否是又在反人类?)

不使用SSL/TLS的HTTP通讯,就是不加密的通讯!

不使用SSL/TLS的HTTP通讯,全部信息明文传播,带来了三大风险:

  1. 窃听风险(eavesdropping):第三方能够获知通讯内容。
  2. 篡改风险(tampering):第三方能够修改通讯内容。
  3. 冒充风险(pretending):第三方能够冒充他人身份参与通讯。

SSL/TLS协议是为了解决这三大风险而设计的,但愿达到:

  1. 全部信息都是加密传播,第三方没法窃听。
  2. 具备校验机制,一旦被篡改,通讯双方会马上发现。
  3. 配备身份证书,防止身份被冒充。

SSL/TLS的做用,打个比方来说:

若是原来的 HTTP 是塑料水管,容易被戳破;那么现在新设计的 HTTPS 就像是在原有的塑料水管以外,再包一层金属水管(SSL/TLS协议)。一来,原有的塑料水管照样运行;二来,用金属加固了以后,不容易被戳破。

HOW(如何适配?---弱弱地问下:加班要多久?)

正如文章开头所说:

TLS 1.2 协议 强制加强数据访问安全 系统 Foundation 框架下的“相关网络请求”将再也不默认使用 HTTP 等不安全的网络协议,而默认采用 TLS 1.2。服务器所以须要更新,以解析相关数据。如不更新,可经过在 Info.plist 中声明,倒退回不安全的网络请求。

总之:

要么我们iOS程序猿加班,要么后台加班:

方案一:当即让公司的服务端升级使用TLS 1.2,以解析相关数据。

方案二:虽Apple不建议,但可经过在 Info.plist 中声明,倒退回不安全的网络请求依然能让App访问指定http,甚至任意的http,具体作法见gif图,示例Demo见 Demo1

enter image description here

这也是官方文档和WWDC给出的解决方案:

  1. Apple官方文档 enter image description here

  2. WWDC Session: "Networking with NSURLSession" session( 【WWDC 2015 session 703, “Privacy and Your App” O网页连接 】, 时间在30:18左右)

    enter image description here

    enter image description here

    enter image description here

即便你的应用使用的是:你没有权限控制的CDN (Content Delivery Network),并且它不支持HTTPS!

也别担忧,Apple都替你考虑好了:

enter image description here 正如你在上图中看到的:苹果官方提供了一些可选配置项来决定是否开启ATS模式,也就是能够选择开启或者不开启。

开发者能够针对某些肯定的URL不使用ATS,这须要在工程中的info.plist中标记NSExceptionDomains。在NSExceptionDomains字典中,能够显式的指定一些不使用ATS的URL。这些你可使用的例子能够是:

  • NSIncludesSubdomains

  • NSExceptionAllowInsecureHTTPLoads

  • NSExceptionRequiresForwardSecrecy

  • NSExceptionMinimumTLSVersion

  • NSThirdPartyExceptionAllowsInsecureHTTPLoads

  • NSThirdPartyExceptionMinimumTLSVersion

  • NSThirdPartyExceptionRequiresForwardSecrecy

这些关键字使咱们能够更加细致的设置针对不使用ATS的域名状况下禁用ATS或者一些特殊的ATS选项。

你可能注意到一些关键字像是使用了一些其余关键字中的词可是在前面加上了"ThirdParty"字样,好比列表里最后三个:

  • NSThirdPartyExceptionAllowsInsecureHTTPLoads

  • NSThirdPartyExceptionMinimumTLSVersion

  • NSThirdPartyExceptionRequiresForwardSecrecy

在功能上,这些关键字与不含有"ThirdParty"的关键字有一样的效果。并且实际运行中所调用的代码将会彻底忽略是否使用"ThirdParty"关键字。你应该使用适用于你的场景的关键字而没必要过多考虑这些。

关于App Transport Security,每一个应用都属于4个大类当中的一类。咱们来看看每个大类都是怎样影响应用的。

-- 分类名 解释
1. HTTPS Only (只有HTTPS,全部状况下都使用ATS) 若是你的应用只基于支持HTTPS的服务器,那么你太幸运了。你的应用不须要作任何改变。可是,注意App Transport Security要求TLS 1.2并且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的。所以慎重检查与你的应用交互的服务器是否是符合ATS的要求很是重要。
2. Mix & Match(混合) 你的应用与一个不符合ATS要求的服务器工做是颇有可能的。在这种状况下,你须要告诉操做系统哪些站点是涉及到的而后在你的应用的 Info.plist文件中指明哪些要求没有达到。
3. Opt Out(禁用ATS) 若是你在建立一个网页浏览器,那么你有一个更大的麻烦。由于你不可能知道你的用户将要访问那个网页,你不可能指明这些网页是否支持ATS要求且在HTTPS上传输。在这种状况下,除了所有撤销 App Transport Security 没有其它办法。
4. Opt Out With Exceptions(除特殊状况外,都不使用ATS) 当你的应用撤消了App Transport Security,,但同时定义了一些例外。这很是有用就是当你的应用从不少的服务器上取数据,可是也要与一个你可控的API交互。在这种状况下,在应用的Info.plist文件中指定任何加载都是被容许的,可是你也指定了一个或多个例外来代表哪些是必需要求 App Transport Security的。

下面分别作一下介绍:

1.HTTPS Only (只有HTTPS,全部状况下都使用ATS)

若是你的应用只基于支持HTTPS的服务器,那么你太幸运了。你的应用不须要作任何改变。

惟一须要作的事情就是使用NSURLSession。若是你的开发目标是iOS 9或者 OS X EI Capitan以后,ATS 的最佳实践将会应用到全部基于NSURLSession的网络。

但也有人遇到过这样的疑惑:服务器已支持TLS 1.2 SSL ,但iOS9上仍是不行,还要进行本文提出的适配操做。

那是由于:要注意 App Transport Security 要求 TLS 1.2,并且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的,ATS只信任知名CA颁发的证书,小公司所使用的 self signed certificate,仍是会被ATS拦截。。所以慎重检查与你的应用交互的服务器是否是符合ATS的要求很是重要。对此,建议使用下文中给出的 NSExceptionDomains,并将大家公司的域名挂在下面。

官方文档 App Transport Security Technote 对CA颁发的证书要求:

Certificates must be signed using a SHA256 or better signature hash algorithm, with either a 2048 bit or greater RSA key or a 256 bit or greater Elliptic-Curve (ECC) key. Invalid certificates result in a hard failure and no connection

2.Mix & Match(混合)

你的应用与一个不符合ATS要求的服务器工做是颇有可能的,

当你遇到如下三个不符合 ATS 要求的服务器的域名时:

  1. api.insecuredomain.com
  2. cdn.domain.com
  3. thatotherdomain.com

你能够分别设置以下:

  1. api.insecuredomain.com

    Info.plist 配置中的XML源码以下所示:

          <key>NSAppTransportSecurity</key>
          <dict>
              <key>NSExceptionDomains</key>
              <dict>
                  <key>api.insecuredomain.com</key>
                  <dict>
                      <key>NSExceptionAllowsInsecureHTTPLoads</key>
                      <false/>
                  </dict>
              </dict>
          </dict>

    在 plist 文件里显示以下:

    enter image description here

    咱们定义的第一个“例外”(Exception)告诉ATS当与这个子域交互的时候撤销了必须使用HTTPS的要求。注意这个仅仅针对在“例外” (Exception)中声明了的子域。很是重要的一点是要理解NSExceptionAllowsInsecureHTTPLoads关键字并不只仅只是与使用HTTPS相关。这个“例外”(Exception)指明了对于那个域名,全部的App Transport Security的要求都被撤销了。

  2. cdn.domain.com Info.plist 配置中的XML源码以下所示:

      <key>NSAppTransportSecurity</key>
      <dict>
          <key>NSExceptionDomains</key>
          <dict>
              <key>cdn.somedomain.com</key>
              <dict>
                  <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                  <string>TLSv1.1</string>
              </dict>
          </dict>
      </dict>

    在 plist 文件里显示以下:

    enter image description here

    极可能你的应用是与一个支持HTTPS传输数据的服务器交互,可是并无使用TLS 1.2或更高。在这种状况下,你定义一个“例外”(Exception),它指明应该使用的最小的TLS的版本。这比彻底撤销那个域名的App Transport Security要更好更安全。

  3. thatotherdomain.com

    Info.plist 配置中的XML源码以下所示:

         <key>NSAppTransportSecurity</key>
          <dict>
              <key>NSExceptionDomains</key>
              <dict>
                  <key>thatotherdomain.com</key>
                  <dict> <!--适用于这个特定域名下的全部子域--> <key>NSIncludesSubdomains</key>
                      <true/> <!--扩展可接受的密码列表:这个域名可使用不支持 forward secrecy 协议的密码--> <key>NSExceptionRequiresForwardSecrecy</key>
                      <false/> <!--容许App进行不安全的HTTP请求--> <key>NSExceptionAllowsInsecureHTTPLoads</key>
                      <true/> <!--在这里声明所支持的 TLS 最低版本--> <key>NSExceptionMinimumTLSVersion</key>
                      <string>TLSv1.1</string>
                  </dict>
              </dict>
          </dict>

    在 plist 文件里显示以下:

    enter image description here

    NSIncludesSubdomains关键字告诉 App Transport Security 这个“例外”(Exception)适用于这个特定域名的全部子域。这个“例外”(Exception)还进一步经过扩展可接受的密码列表来定义这个域名可使用不支持forward secrecy(NSExceptionRequiresForwardSecrecy) 协议的密码。想了解更多关于forward secrecy的信息,推荐去看官方文档 Apple's technote 

若是你的App中同时用到了这三个域名,那么应该是这样:

     <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSExceptionDomains</key>
            <dict>
                <key>api.insecuredomain.com</key>
                <dict>
                    <key>NSExceptionAllowsInsecureHTTPLoads</key>
                    <false/>
                </dict>
                <key>cdn.somedomain.com</key>
                <dict>
                    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                    <string>TLSv1.1</string>
                </dict>
                <key>thatotherdomain.com</key>
                <dict>
                    <key>NSIncludesSubdomains</key>
                    <true/>
                    <key>NSExceptionRequiresForwardSecrecy</key>
                    <false/>
                </dict>
            </dict>
        </dict>

enter image description here

3. Opt Out(禁用ATS)

上面是比较严谨的作法,指定了能访问哪些特定的HTTP。固然也有暴力的作法:完全倒退回不安全的HTTP网络请求,能任意进行HTTP请求,好比你在开发一款浏览器App,或者你想偷懒,或者后台想偷懒,或者公司不给你升级服务器。。。

你能够在Info.plist 配置中改用下面的XML源码:

    <key>NSAppTransportSecurity</key>
    <dict> <!--完全倒退回不安全的HTTP网络请求,能任意进行HTTP请求 (不建议这样作)--> <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

在 plist 文件里显示以下:

enter image description here

4. Opt Out With Exceptions(除特殊状况外,都不使用ATS)

上面已经介绍了三种情景,还有一种可能你也会遇到:

当你的应用撤消了App Transport Security,,但同时定义了一些“例外”(Exception)。当你的应用从不少的服务器上取数据,可是也要与一个你可控的API交互。在这种状况下,在应用的Info.plist文件中指定任何加载都是被容许的,可是你也指定了一个或多个“例外”(Exception)来代表哪些是必需要求 App Transport Security的。下面是Info.plist文件应该会有的内容:

<key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSExceptionDomains</key>
            <dict>
                <key>api.tutsplus.com</key>
                <dict>
                    <key>NSExceptionAllowsInsecureHTTPLoads</key>
                    <false/>
                </dict>
            </dict>
        </dict>

在 plist 文件里显示以下:

enter image description here

【注:以上在Info.plist配置中的作法已经验证可行,但目前Apple的prerelease版本的官方文档并未说起Info.plist中配置的代码,我将密切关注官方文档,若有说起,再来更新本文 .你若发现官方文档有说起了,也可在微博@iOS程序犭袁通知下我。】(官方文档已经有阐述)

Certificate Transparency

虽然ATS大多数安全特性都是默承认用的,Certificate Transparency 是必须设置的。若是你有支持Certificate Transparency的证书,你能够检查NSRequiresCertificateTransparency关键字来使用Certificate Transparency。再次强调,若是你的证书不支持Certificate Transparency,此项须要设置为不可用。

若是须要调试一些因为采用了ATS而产生的问题,须要设置CFNETWORK_DIAGNOSTICS为1,这样就会打印出包含被访问的URL和 ATS错误在内的NSURLSession错误信息。要确保处理了遇到的全部的错误消息,这样才能使ATS易于提升可靠性和扩展性。

Q-A

Q:我用xcode7编译的app,若是不在plist里面加关键字说明,ios9下不能进行网络请求,由于咱们服务器并不支持 TLS 1.2 ,我要是直接下载app store上的,什么也没有作,也是能正常网络请求。

A:本文中所罗列的新特性,多数状况下指的是 iOS9.X-SDK 新特性,AppStore 的版本是基于 iOS8.X-SDK或 iOS7.X-SDK,因此并不受 iOS9新特性约束。也就是说:Xcode7给iOS8打设备包能够请求到网络,Xcode7给iOS9设备打的包请求不到网络,Xcode7和iOS9缺一不可,才须要网络适配ATS。

那么,如何确认本身项目所使用的 SDK?在Targets->Build Setting-->Architectures

enter image description here

Q:服务器已支持TLS 1.2 SSL ,但iOS9上仍是不行,还要进行本文提出的适配操做。

A:那是由于:要注意 App Transport Security 要求 TLS 1.2,并且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的,ATS只信任知名CA颁发的证书,小公司所使用的 self signed certificate,仍是会被ATS拦截。。所以慎重检查与你的应用交互的服务器是否是符合ATS的要求很是重要。对此,建议使用下文中给出的 NSExceptionDomains,并将大家公司的域名挂在下面。

官方文档 App Transport Security Technote 对CA颁发的证书要求:

Certificates must be signed using a SHA256 or better signature hash algorithm, with either a 2048 bit or greater RSA key or a 256 bit or greater Elliptic-Curve (ECC) key. Invalid certificates result in a hard failure and no connection

Q:我使用的是第三方的网络框架,好比 AFNetworking 、ASIHTTPRequest、CFSocket 等,这个有影响没有?

A: AFNetworking 有影响,其它没影响。

ATS 是只针对NSURLConnection、CFURL、NSURLSession,若是底层涉及到这三个类就会有影响。

如今的 AFNetworking 的 AFHTTPRequestOperationManager 实现是使用的NSURLConnection。

但 AFNetworking 也有更新计划,移除NSURLConnection相关API,迁移到 AFHTTPSessionManager ,但还未执行,详情见:https://github.com/AFNetworking/AFNetworking/issues/2806

Q:试了一下禁用 ATS 的方法 可是仍是没法联网 仍然提示要使用https?

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.

A:遇到这类问题,90%是出如今“一个 Project 多 Target ”的状况下,因此请确保你修改的,确实是你的 Target 所属的 Info.plist !

如何确认?请前往这里,确认你 Target 所属的 Info.plist 到底是哪一个:

Project -> Your Target -> Build Settings -> Info.plist File

enter image description here

或者更直截了当一点,直接修改:

Project -> Your Target —>info-> Custom iOS target properties-> 添加禁用 ATS 的属性

enter image description here

还有一种可能性是:禁用 ATS 的代码粘贴进 plist 时,位置不对,能够尝试放在 diwuhang

Q:个人项目是“一个 Project 多 Target ”,按照本文禁用 ATS 的方法,是否是每一个 Info.plist 都要修改?

A:不须要,用到哪一个 Target 修改哪一个的 Info.plist ,Target 是独立的,不受其余 Target 的影响,也不会影响其余 Target。

Q:如何检测咱们公司 HTTPS 是否符合 ATS 的要求?

A:若是你的 App 的服务也在升级以适配ATS要求,可使用以下的方式进行校验:

在OS X EI Capitan系统的终端中经过nscurl命令来诊断检查你的HTTPS服务配置是否知足Apple的ATS要求:

 $ nscurl --verbose --ats-diagnostics https://<your_server_domain>

固然,你也可让公司服务端的同事参考Apple提供官方指南App Transport Security Technote进行服务的升级配置以知足ATS的要求:

一个符合 ATS 要求的 HTTPS,应该知足以下条件:

  1. Transport Layer Security协议版本要求TLS1.2以上
  2. 服务的Ciphers配置要求支持Forward Secrecy等
  3. 证书签名算法符合ATS要求等

2.Demo2_iOS9新特性_更灵活的后台定位

【iOS9在定位的问题上,有一个坏消息一个好消息】坏消息:若是不适配iOS9,就不能偷偷在后台定位(不带蓝条,见图)!好消息:将容许出现这种场景:同一App中的多个location manager:一些只能在前台定位,另外一些可在后台定位,并可随时开启或者关闭特定location manager的后台定位。

若是没有请求后台定位的权限,也是能够在后台定位的,不过会带蓝条: enter image description here

如何偷偷在后台定位:请求后台定位权限:

// 1. 实例化定位管理器
_locationManager = [[CLLocationManager alloc] init];
// 2. 设置代理
_locationManager.delegate = self;
// 3. 定位精度
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
// 4.请求用户权限:分为:⓵只在前台开启定位⓶在后台也可定位,
//注意:建议只请求⓵和⓶中的一个,若是两个权限都须要,只请求⓶便可,
//⓵⓶这样的顺序,将致使bug:第一次启动程序后,系统将只请求⓵的权限,⓶的权限系统不会请求,只会在下一次启动应用时请求⓶
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {
    //[_locationManager requestWhenInUseAuthorization];//⓵只在前台开启定位
    [_locationManager requestAlwaysAuthorization];//⓶在后台也可定位
}
// 5.iOS9新特性:将容许出现这种场景:同一app中多个location manager:一些只能在前台定位,另外一些可在后台定位(并可随时禁止其后台定位)。
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
    _locationManager.allowsBackgroundLocationUpdates = YES;
}
// 6. 更新用户位置
[_locationManager startUpdatingLocation];

可是若是照着这种方式尝试,而没有配置Info.plist,100%你的程序会崩溃掉,并报错:

*** Assertion failure in -[CLLocationManager setAllowsBackgroundLocationUpdates:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/CoreLocationFramework_Sim/CoreLocation-1808.1.5/Framework/CoreLocation/CLLocationManager.m:593

要将 Info.plist 配置以下: enter image description here

对应的 Info.plist 的XML源码是:

<key>NSLocationAlwaysUsageDescription</key>
<string>微博@iOS程序犭袁 请求后台定位权限</string>

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

3.企业级分发

有两处变化:

  1. iOS9之后,企业级分发ipa包将遭到与Mac上dmg安装包同样的待遇:默认不能安装,也再也不出现“信任按钮”
  2. iOS9之后,企业分发时可能存在:下载的ipa包与网页二者的 bundle ID 没法匹配而致使下载失败的状况

1. iOS9之后,企业级分发ipa包将遭到与Mac上dmg安装包同样的待遇:默认不能安装,也再也不出现“信任按钮”

iOS9以前,企业级分发十分方便:点击App出现“信任按钮”,

enter image description here

iOS9之后,企业级分发ipa包将遭到与Mac上dmg安装包同样的待遇:默认不能安装,也再也不出现“信任按钮”

enter image description here

必须让用户进行gif图中的设置:

enter image description here

2. iOS9之后,企业分发时可能存在:下载的ipa包与网页二者的 bundle ID 没法匹配而致使下载失败的状况

iOS9升级后众多企业分发的 app 已经出现了不能安装的状况,而iOS8或更早的系统不受影响。那是由于从iOS9之后,系统会在 ipa 包下载完以后,拿ipa包中的 bundle ID 与网页中的 plist 文件中的 bundle ID 进行比对,不一致不容许安装。

错误提示以下:

enter image description here

网页中的 plist 文件中的 bundle ID 的做用可参考 《iOS:苹果企业证书经过网页分发安装app》 。

正如这篇文章提到的,“网页中的 plist 文件”是习惯的叫法,也有人称做“manifest文件”,好比 这篇文章

而iOS9以前,苹果不会检查这一项,所以iOS9以前能够安装。

致使这一错误的缘由除了粗心,还有开发者是故意设置不一致,据开发者说:

当初服务器 plist 的 bundle id 上故意作成成不一致。是为了解决一些人安装不上的问题。

详情可参考: 《升级到ios 9,企业版发布如今没法安装成功了,有人遇到了这种问题吗?》

如何知道是由于 bundle id 不一致形成的没法安装?

经过查看设备上的日志信息:有一个 itunesstored 进程提示安装信息:

itunesstored →  <Warning>: [Download]: Download task did finish: 8 for download: 2325728577585828282
  itunesstored →  <Warning>: [ApplicationWorkspace] Installing download: 2325728577585828282 with step(s): Install
  itunesstored →  <Warning>: [ApplicationWorkspace]: Installing software package with bundleID: com.***.***: bundleVersion: 1.01 path: /var/mobile/Media/Downloads/2325728577585828282/-1925357977307433048
  itunesstored →  <Warning>: BundleValidator: Failed bundleIdentifier: com.***.**** does not match expected bundleIdentifier: com.***.*********
  itunesstored →  <Warning>: [ApplicationWorkspace]: Bundle validated for bundleIdentifier: com.****.******success: 0
  itunesstored →  <Warning>: LaunchServices: Uninstalling placeholder for app <LSApplicationProxy: 0x12677be70> com.****.*******(Placeholder) <file:///private/var/mobile/Containers/Bundle/Application/B62D8EA3-2052-4393-8A7E-3FD27228BFC2/2325728577585828282.app>
  itunesstored →  <Warning>: LaunchServices: Uninstalling app <LSApplicationProxy: 0x12677be70> com.****.*****(Placeholder) <file:///private/var/mobile/Containers/Bundle/Application/B62D8EA3-2052-4393-8A7E-3FD27228BFC2/2325728577585828282.app>

其中的这一句很重要:

itunesstored →  <Warning>: BundleValidator: Failed bundleIdentifier: com.***.**** does not match expected bundleIdentifier: com.***.*********

通过核对,果真是.ipa文件中真实的Bundle ID和manifest文件中配置的信息不匹配,而后测试发现:

iOS 9是校验bundle-identifier值的,而iOS 9如下版本是不校验,一旦iOS 9发现bundle-identifier不匹配,即便下载成功了,也会 Uninstall(日志中提示的)app的。

适配方法:

  1. 二者的 bundle id 修改一致

    一旦出现iOS9可以安装企业版本APP,iOS9如下版本不能安装,必定先查看安装日志,而后核对每一个参数配置。

    manifest文件的参考配置。

    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict> <!-- array of downloads. --> <key>items</key>
    <array>
       <dict> <!-- an array of assets to download --> <key>assets</key>
           <array> <!-- software-package: the ipa to install. --> <dict> <!-- required.  the asset kind. --> <key>kind</key>
                   <string>software-package</string> <!-- optional.  md5 every n bytes.  --> <!-- will restart a chunk if md5 fails. --> <key>md5-size</key>
                   <integer>10485760</integer> <!-- optional.  array of md5 hashes --> <key>md5s</key>
                   <array>
                       <string>41fa64bb7a7cae5a46bfb45821ac8bba</string>
                       <string>51fa64bb7a7cae5a46bfb45821ac8bba</string>
                   </array> <!-- required.  the URL of the file to download. --> <key>url</key>
                   <string>http://www.example.com/apps/foo.ipa</string>
               </dict> <!-- display-image: the icon to display during download. --> <dict>
                   <key>kind</key>
                   <string>display-image</string> <!-- optional. icon needs shine effect applied. --> <key>needs-shine</key>
                   <true/>
                   <key>url</key>
                   <string>http://www.example.com/image.57×57.png</string>
               </dict> <!-- full-size-image: the large 512×512 icon used by iTunes. --> <dict>
                   <key>kind</key>
                   <string>full-size-image</string> <!-- optional.  one md5 hash for the entire file. --> <key>md5</key>
                   <string>61fa64bb7a7cae5a46bfb45821ac8bba</string>
                   <key>needs-shine</key>
                   <true/>
                   <key>url</key>
                   <string>http://www.example.com/image.512×512.jpg</string>
               </dict>
           </array><key>metadata</key>
           <dict> <!-- required --> <key>bundle-identifier</key>
               <string>com.example.fooapp</string> <!-- optional (software only) --> <key>bundle-version</key>
               <string>1.0</string> <!-- required.  the download kind. --> <key>kind</key>
               <string>software</string> <!-- optional. displayed during download; --> <!-- typically company name --> <key>subtitle</key>
               <string>Apple</string> <!-- required.  the title to display during the download. --> <key>title</key>
               <string>Example Corporate App</string>
           </dict>
       </dict>
    </array>
    </dict>
    </plist>
  2. 使用fir.im等第三方分发平台:上述“ bundle id 不一致致使下载失败”这种状况只会出如今企业本身搭建网页分发的情形下,事实证实第三方的分发平台更加专业,已经很好地规避了该状况的发生。

Q-A

Q:企业分发,企业版证书在iOS9上安装应用报Ignore manifest download, already have bundleID: com.mycom.MyApp只有个人手机没法安装,别人 iOS9 均可以安装

A:这并不是 iOS9的问题,iOS8及之前的系统也会出现,和缓存有关系,请尝试关机重启手机,而后就能够安装了。

4.Bitcode

【前言】将来, Watch 应用必须包含 bitcode ,iOS不强制,Mac OS不支持。但最坑的一点是: Xcode7 及以上版本会默认开启 bitcode 。

什么是 bitcode ?

通俗解释:在线版安卓ART模式。

Apple 官方文档-- App Distribution Guide – App Thinning (iOS, watchOS) 是这样定义的:

Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.

翻译过来就是:

bitcode 是被编译程序的一种中间形式的代码。包含 bitcode 配置的程序将会在 App Store 上被编译和连接。 bitcode 容许苹果在后期从新优化咱们程序的二进制文件,而不须要咱们从新提交一个新的版本到 App Store 上。

在 Xcode简介--- What’s New in Xcode-New Features in Xcode 7 中这样描述:

Bitcode. When you archive for submission to the App Store, Xcode will compile your app into an intermediate representation. The App Store will then compile the bitcode down into the 64 or 32 bit executables as necessary.

也就是

当咱们提交程序到 App Store上时, Xcode 会将程序编译为一个中间表现形式( bitcode )。而后 App store 会再将这个 bitcode 编译为可执行的64位或32位程序。

再看看这两段描述都是放在App Thinning(App瘦身)一节中,能够看出其与包的优化有关了。

打个比方,没有 bitcode 的 AppStore 里所提供的 App,相似在新华书店里卖捆绑销售的《四大名著丛书--精装版》,要买只能全买走,有了 bitcode 就比如这套四大名著每本均可以单卖,顾客就能按需购买。咱们开发者在这个过程当中扮演的角色是图书出版商的角色,应该照顾那些没钱一次买四本的顾客。(不要作不珍惜用户流量和存储空间的奸商。。)

那为何第三方的 SDK 不支持 bitcode,个人 app 也就不能支持?打个比方,《四大名著丛书》只要有一本是能够单卖的,那么你很难再卖捆绑销售款的《四大名著丛书》了,因此干脆全均可以单卖,这大概就是 Apple 的逻辑。

App Thinning 官方文档解释以下:

The App Store and operating system optimize the installation of iOS and watchOS apps by tailoring app delivery to the capabilities of the user’s particular device, with minimal footprint. This optimization, called app thinning, lets you create apps that use the most device features, occupy minimum disk space, and accommodate future updates that can be applied by Apple. Faster downloads and more space for other apps and content provides a better user experience.

开发者都知道,当前 iOS App 的编译打包方式是把适配兼容多个设备的执行文件及资源文件合并一个文件,上传和下载的文件则包含了全部的这些文件,致使占用较多的存储空间。

App Thinning是一个关于节省iOS设备存储空间的功能,它可让iOS设备在安装、更新及运行App等场景中仅下载所需的资源,减小App的占用空间,从而节省设备的存储空间。

根据Apple官方文档的介绍,App Thinning主要有三个机制:

  1. Slicing

    开发者把App安装包上传到AppStore后,Apple服务会自动对安装包切割为不一样的应用变体(App variant),当用户下载安装包时,系统会根据设备型号下载安装对应的单个应用变体。

  2. On-Demand Resources

    ORD(随需资源)是指开发者对资源添加标签上传后,系统会根据App运行的状况,动态下载并加载所需资源,而在存储空间不足时,自动删除这类资源。

  3. Bitcode 开启Bitcode编译后,可使得开发者上传App时只需上传Intermediate Representation(中间件),而非最终的可执行二进制文件。 在用户下载App以前,AppStore会自动编译中间件,产生设备所需的执行文件供用户下载安装。

(喵大(@onevcat)在其博客 《开发者所须要知道的 iOS 9 SDK 新特性》 中也描述了iOS 9中苹果在App瘦身中所作的一些改进,你们能够转场到那去研读一下。)

其中,Bitcode的机制能够支持动态的进行App Slicing,而对于Apple将来进行硬件升级的措施,此机制能够保证在开发者不从新发布版本的状况下而兼容新的设备。

Bitcode 是一种中间代码,那它是什么格式的呢? LLVM 官方文档有介绍这种文件的格式: LLVM Bitcode File Format

若是你的应用也准备启用 Bitcode 编译机制,就须要注意如下几点:

  1. Xcode 7默认开启 Bitcode ,若是应用开启 Bitcode,那么其集成的其余第三方库也须要是 Bitcode 编译的包才能真正进行 Bitcode 编译
  2. 开启 Bitcode 编译后,编译产生的.app体积会变大(中间代码,不是用户下载的包),且.dSYM文件不能用来崩溃日志的符号化(用户下载的包是 Apple 服务从新编译产生的,有产生新的符号文件)
  3. 经过 Archive 方式上传 AppStore 的包,能够在Xcode的Organizer工具中下载对应安装包的新的符号文件

如何适配?

在上面的错误提示中,提到了如何处理咱们遇到的问题:

You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64

正如开头所说的:

将来, Watch 应用必须包含 Bitcode ,iOS不强制,Mac OS不支持。但最坑的一点是: Xcode7 及以上版本会默认开启 Bitcode 。

Xcode 7 + 会开启 Bitcode。

也就是说,也两种方法适配:

方法一:更新 library 使包含 Bitcode ,不然会出现如下中的警告;

(null): URGENT: all bitcode will be dropped because '/Users/myname/Library/Mobile Documents/com~apple~CloudDocs/foldername/appname/GoogleMobileAds.framework/GoogleMobileAds(GADSlot+AdEvents.o)' was built without bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. Note: This will be an error in the future.

甚至有的会报错误,没法经过编译:

ld: ‘/Users//Framework/SDKs/PolymerPay/Library/mobStat/libSDK.a(**ForSDK.o)’ does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64

或:

ld: -undefined and -bitcode_bundle (Xcode settingENABLE_BITCODE=YES) cannot be used together clang: error: linker command failed with exit code 1 (use -v to see invocation)

enter image description here

不管是警告仍是错误,获得的信息是:咱们引入的一个第三方库不包含bitcode。

方法二:关闭Bitcode,方法见下图

enter image description here

咱们能够在”Build Settings”->”Enable Bitcode”选项中看到:

用 Xcode 7+ 新建一个 iOS 程序时, bitcode 选项默认是设置为YES的。如今须要改为NO。

若是咱们开启了 bitcode ,在提交包时,下面这个界面也会有个 bitcode 选项:

enter image description here

那么 SDK 厂商如何支持 bitcode 呢?答案是只要在 Xcode7上从新编译一下就 ok 了。(请确保默认开启的 bitcode 没有去主动关闭)

更多信息,请移步

  1. bitcode 苹果官方文档

  2. WWDC 2015 Session 102: "Platforms State of the Union"

    enter image description here

5.Demo3---iOS9 URL Scheme 适配_引入白名单概念

WWDC 2015 Session 703: "Privacy and Your App ( 时间在30:18左右)关于URL scheme的介绍,指出:

enter image description here

也就是说:在iOS9中,若是使用canOpenURL:方法,该方法所涉及到的URL scheme必须在"Info.plist"中将它们列为白名单,不然不能使用。key叫作LSApplicationQueriesSchemes ,键值内容是

<key>LSApplicationQueriesSchemes</key>
<array>
 <string>urlscheme</string>
 <string>urlscheme2</string>
 <string>urlscheme3</string>
 <string>urlscheme4</string>
</array>

白名单上限是50个:

WWDC 2015 Session 703: "Privacy and Your App )有说明:

“So for apps that are linked before iOS 9 and are running on iOS 9, they will be given 50 distinct URL schemes.” -- WWDC 2015 session 703 Privacy and Your App

然而,咱们却发现了一件意外的事:当咱们在 iOS9-beta(截至本文发布时,iOS9正式版还未发布)中,使用openURL:方法时,不在白名单中的 URL 会报错 > “This app is not allowed to query for scheme xxx” 。不管是官方文档仍是 WWDC 的视频中都没有说起openURL:方法的这一变更,因此猜想这是 beta 版本一个 bug ,截至本文发布时,iOS9正式版还未发布,指望在正式版中能得以修复。在此以前,可经过将openURL:用到的URL scheme列入白名单来解决这个 bug 。(经测试:iOS9 beta5中已经修复)

iOS9中openURL:方法没有什么实质性的变化,仅仅多了一个确认动做:

enter image description here

苹果为何要这么作?

在 iOS9 以前,你可使用canOpenURL:监测用户手机里到底装没装微信,装没装微博。可是也有一些别有用心的 App ,这些 App 有一张经常使用 App 的URL scheme,而后他们会屡次调用canOpenURL:遍历该表,来监测用户手机都装了什么 App ,好比这个用户装了叫“大姨妈”的App,你就能够知道这个用户是女性,你就能够只推给这个用户女性用品的广告。这是侵犯用户隐私的行为。

这也许就是缘由。

本项目中给出了一个演示用的 Demo ,仓库的文件夹叫“Demo3_iOS9URLScheme适配_引入白名单概念”,Demo引用自 LSApplicationQueriesSchemes-Working-Example

Demo结构以下:

enter image description here

主要演示的情景是这样的:

假设有两个App: weixin(微信) and 个人App. 个人App 想监测 weixin(微信) 是否被安装了. "weixin(微信)" 在 info.plist 中定义了 URL scheme :

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>weixin</string>
        </array>
    </dict>
</array>

个人App 想监测 weixin(微信) 是否被安装了 :

[[UIApplication sharedApplication]
                    canOpenURL:[NSURL URLWithString:@"weixin(微信)://"]];

即便你安装了微信,在iOS9中,这有可能会返回NO:

由于你须要将 "weixin(微信)" 添加到 “个人App” 的 info.plist 文件中:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>weixin</string>
</array>

(以上只是为了演示,实际开发中,你不只须要添加“weixin”还须要“wechat”这两个。具体 )

关于openURL:这个问题,可在 Demo3 中自行测试,若是该 bug 修复了的话,请私信微博@iOS程序犭袁,我再来更新本文。(经测试:iOS9 beta5中已经修复)

另外,推荐一篇博文,其中最关键的是如下部分:

If you call the “canOpenURL” method on a URL that is not in your whitelist, it will return “NO”, even if there is an app installed that has registered to handle this scheme. A “This app is not allowed to query for scheme xxx” syslog entry will appear.

> If you call the “openURL” method on a URL that is not in your whitelist, it will fail silently. A “This app is not allowed to query for scheme xxx” syslog entry will appear.

常见 URL Scheme

若是想一次性集成最经常使用的微信、新浪微博、QQ、支付宝四者的白名单,则配置以下:

 <key>LSApplicationQueriesSchemes</key>
<array> <!-- 微信 URL Scheme 白名单--> <string>wechat</string>
    <string>weixin</string> <!-- 新浪微博 URL Scheme 白名单--> <string>sinaweibohd</string>
    <string>sinaweibo</string>
    <string>sinaweibosso</string>
    <string>weibosdk</string>
    <string>weibosdk2.5</string> <!-- QQ、Qzone URL Scheme 白名单--> <string>mqqapi</string>
    <string>mqq</string>
    <string>mqqOpensdkSSoLogin</string>
    <string>mqqconnect</string>
    <string>mqqopensdkdataline</string>
    <string>mqqopensdkgrouptribeshare</string>
    <string>mqqopensdkfriend</string>
    <string>mqqopensdkapi</string>
    <string>mqqopensdkapiV2</string>
    <string>mqqopensdkapiV3</string>
    <string>mqzoneopensdk</string>
    <string>wtloginmqq</string>
    <string>wtloginmqq2</string>
    <string>mqqwpa</string>
    <string>mqzone</string>
    <string>mqzonev2</string>
    <string>mqzoneshare</string>
    <string>wtloginqzone</string>
    <string>mqzonewx</string>
    <string>mqzoneopensdkapiV2</string>
    <string>mqzoneopensdkapi19</string>
    <string>mqzoneopensdkapi</string>
    <string>mqzoneopensdk</string> <!-- 支付宝  URL Scheme 白名单--> <string>alipay</string>
    <string>alipayshare</string>

</array>

plist 文件看起来会是这样的:

enter image description here

其余平台可在下面的列表中查询:各平台OpenURL白名单说明

平台名称 URL Schem 补充说明
微信 wechat, weixin  
支付宝 alipay,alipayshare  
QQ mqqOpensdkSSoLogin, mqqopensdkapiV2,mqqopensdkapiV3,wtloginmqq2,mqq,mqqapi  
QZONE mqzoneopensdk, mqzoneopensdkapi,mqzoneopensdkapi19,mqzoneopensdkapiV2,mqqOpensdkSSoLogin,mqqopensdkapiV2,mqqopensdkapiV3,wtloginmqq2,mqqapi,mqqwpa,mqzone,mqq [注:若同时使用QQ和QZONE,则直接添加本格便可]
新浪微博 sinaweibo,sinaweibohd,sinaweibosso,sinaweibohdsso,weibosdk,weibosdk2.5 [后两个若导入新浪SDK则须要]
豆瓣 无需配置  
开心网 无需配置  
易信 yixin, yixinopenapi  
Google+ googlechrome, googlechrome-x-callback,hasgplus4,com.google.gppconsent,com.google.gppconsent.2.2.0,com.google.gppconsent.2.3.0,com.google.gppconsent.2.4.0,com.google.gppconsent.2.4.1  
人人网 renrenapi,renrenios,renreniphone,renren,  
Facebook fbauth2  
Twitter 无需配置  
Pocket pocket-oauth-v1  
Pinterest pinit  
Instagram instagram  
WhatsApp whatsapp  
Line line  
KakaoTalk kakaolink  
KaokaoStory storylink  
LinkedIn 无需配置  
Tumblr 无需配置  
非平台类 无需配置 ( 如短信,复制,邮件等)

Q-A

Q:我用xcode7编译的app,若是不在plist里面加scheme,ios9下qq就会不显示,由于我用了qqsdk里的判断是否安装qq的方法,我要是直接下载app store上的,没有加scheme,qq也是能显示。

A:本文中所罗列的新特性,多数状况下指的是 iOS9.X-SDK 新特性,AppStore 的版本是基于 iOS8.X-SDK或 iOS7.X-SDK,因此并不受 iOS9新特性约束。也就是说:Xcode7给iOS8打设备包不须要白名单也能调用“canOpenURL” ,Xcode7给iOS9设备打的包则否则,Xcode7和iOS9缺一不可,才须要适配URL Scheme。

那么,如何确认本身项目所使用的 SDK?在Targets->Build Setting-->Architectures

enter image description here

Q:咱们本身的应用跳到微信、支付宝、微博等的URLScheme是固定几个,可是从微信、支付宝、微博跳回到咱们的应用的URLScheme多是成千上万个,那他们那些大厂是如何作这个白名单?

A:白名单策略影响的仅仅是 canOpenURL: 接口,OpenURL: 不受影响,这些大厂只调用 openURL: 因此不受 iOS9 的影响。

Q:文中提到了设置白名单的缘由,然而,若是这些别有用心的APP在它本身的白名单列出它关心的APP, 而后依次调用canOpenURL来检测,照样能够监控用户都安装了哪些APP啊?因此我依然不明白苹果这样作得缘由。

A:白名单的数目上限是50个。苹果这样子作,使得最多只能检测50个App。

Q:按照文中的适配方法,error缘由就没有了的确没问题了,可是仍是会打印以下信息:

 -canOpenURL: failed for URL: "XXXXXXXXXX" - error: "(null)"。

A:这个模拟器的一个 bug,不管使用iOS9的真机仍是模拟器均出现该问题,估计 Xcode 后续的升级中会修复掉。

那如何判断日志到底是 Xcode bug 形成的仍是没有适配形成的?看error的值,若是是null,则是 bug。(2015-09-21更)

6. iPad适配Slide Over 和 Split View

enter image description here

【iPad适配Slide Over 和 Split View】若想适配multi tasking特性,惟一的建议:弃纯代码,改用storyboard、xib,纵观苹果WWDC全部Demo均是如此:

  1. Mysteries of Auto Layout, Part 1

  2. What's New in Storyboards

  3. Implementing UI Designs in Interface Builder

  4. Getting Started with Multitasking on iPad in iOS 9

  5. Optimizing Your App for Multitasking on iPad in iOS

7.字体间隙变大致使 UI 显示异常

iOS8中,字体是Helvetica,中文的字体有点相似于“华文细黑”。只是苹果手机自带渲染,因此看上去可能比普通的华文细黑要美观。 iOS9中,中文系统字体变为了专为中国设计的“苹方” 有点相似于一种word字体“幼圆”。字体有轻微的加粗效果,而且最关键的是字体间隙变大了!

因此不少本来写死了width的label可能会出现“...”的状况:

状况 显示 解释
XIB 将 label 的 width 写死 下面这两张图也能够直观的看出同一个界面,同一个label的变化。
iOS8 enter image description here 正常
iOS9 enter image description here 最后四位数字、、、

若是不将 label 的 width 写死,仅仅添加左端约束则右端的四个数字会越界

状况 显示 解释
XIB enter image description here 若是仅仅添加左端约束
iOS8 enter image description here 正常
iOS9 enter image description here “3199”这四个数字越界了

因此为了在界面显示上不出错,就算是固定长度的文字也仍是建议使用sizetofit 或者ios向上取整 ceilf() 或者提早计算:

CGSize size = [title sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14.0f]}]; CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

8.升级 Xcode7 后的崩溃与警告

旧版本新浪微博 SDK 在 iOS9 上会致使的 Crash

 app was compiled with optimization - stepping may behave oddly; variables may not be available

打印出来这句话,而后崩溃。可能是启动的过程当中程序就崩溃。

在iOS9下,新浪微博SDK里面使用的 JSONKit 在部分机型可能致使崩溃。崩溃信息以下图。

enter image description here

解决:更新新浪微博SDK,新浪的SDK最新版作了对iOS9兼容。

iOS9 下使用 Masonry 会引发崩溃的一种状况

咱们在使用时候一直将 leading 与 left 划为等号,这样作在 iOS8(及之前)上是正常的,但在 iOS9 上这样的观念可能会引发崩溃,好比:

 make.left.equalTo(self.mas_leading).offset(15);

应该为:

 make.left.equalTo(self.mas_left).offset(15);

同理 mas_training 也须要改成right

Xcode 升级后,旧的状态栏的样式设置方式会引发警告

<Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

出错缘由:设置 app 的状态栏样式的时候,使用了旧的方式,在 info.plist 里面的View controller-based status bar appearance默认会为 YES,即便不设置也是 YES,但通常 iOS6 的时候为了设置状态栏样式,须要将其设为NO,iOS7,8也兼容,可是到了iOS9 就会报警告。

解决办法:

删除原先的设置代码,一般老的设置方式是这样的:

 //设置状态栏的白色 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

删除的缘由见下:

 // Setting the statusBarStyle does nothing if your application is using the default UIViewController-based status bar system. @property(readwrite, nonatomic) UIStatusBarStyle statusBarStyle NS_DEPRECATED_IOS(2_0, 9_0, "Use -[UIViewController preferredStatusBarStyle]");
- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated NS_DEPRECATED_IOS(2_0, 9_0, "Use -[UIViewController preferredStatusBarStyle]");

修改方式是在Info.plist文件中作以下修改:

将View controller-based status bar appearance删除(默认为 YES),或设置为YES:

对应的 plist 里的 XML源码:

 <key>UIViewControllerBasedStatusBarAppearance</key>
    <true/>

看起来长这样:

enter image description here

而后使用新的方式来实现状态栏的样式:

- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIViewController *)childViewControllerForStatusBarStyle;
- (void)setNeedsStatusBarAppearanceUpdate

好比,你想将状态栏设置为白色,就能够这样写:

//设置状态栏的白色 -(UIStatusBarStyle)preferredStatusBarStyle
{ return UIStatusBarStyleLightContent;
}

记得要 clean 下或者删除应用程序从新运行

Demo4---navigationController状态栏样式新的设置方法

若是你按照上面的方法设置了,但仍是不行。八成是 rootViewController 设置的问题,你必须设置 rootViewController,编译器才会去 rootViewController 中重载 preferredStatusBarStyle 方法。

另外当你在 appdelegate 中将 navigationController 设为 rootViewController 的时候:

     self.window.rootViewController = self.navigationController;

由于 rootViewController 变为了 navigationController,你在 ViewController 里重写 preferredStatusBarStyle 方法是不会起做用的。因此最好的方法是

 - (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"微博@iOS程序犭袁";
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
}

若是你仍是想重写 preferredStatusBarStyle 方法来达到做用,那最好使用分类来解决:

.h文件:

 // //  UINavigationController+StatusBarStyle.h //  微博@iOS程序犭袁 // //  Created by  https://github.com/ChenYilong/iOS9AdaptationTips/ on 15/6/8. //  Copyright (c) 2015年   http://weibo.com/luohanchenyilong/  . All rights reserved. // #import <UIKit/UIKit.h> @interface UINavigationController (StatusBarStyle) @end 

.m文件:

 // //  UINavigationController+StatusBarStyle.m //  微博@iOS程序犭袁 // //  Created by  https://github.com/ChenYilong/iOS9AdaptationTips/ on 15/6/8. //  Copyright (c) 2015年   http://weibo.com/luohanchenyilong/  . All rights reserved. // #import "UINavigationController+StatusBarStyle.h" @implementation UINavigationController (StatusBarStyle)

- (UIStatusBarStyle)preferredStatusBarStyle { //also you may add any fancy condition-based code here return UIStatusBarStyleLightContent;
} @end

我在仓库里给出了 navigation 的设置方法,见Demo4。

参考连接: preferredStatusBarStyle isn't called--For anyone using a UINavigationController:

Xcode7 在 debug 状态下也生成 .dSYM 文件引发的警告

Xcode6 的工程升级到 Xcode7上来,会报警告:

enter image description here

这是 debug 编译时导出符号文件出现的告警,

然而新建的Xcode7工程不会有该问题。

解决方法是让 debug 编译的时候不生成符号文件:

enter image description here

Xcode7 没法使用 8.x 系统的设备调试,一运行就报错there is an intenal API error

enter image description here

Xcode7调试iOS8.x的真机,须要确保项目名改成英文,中间含有中文会报错there is an intenal API error

按照下面的步骤检查:

bulid settings -> packaging -> product name

使用了 HTML 的 iframe 元素可能致使没法从 Safari 跳转至 App

咱们都知道,从网易新闻分享一条新闻到QQ,而后从QQ中打开连接再用safari打开连接,在iOS8上,这个时候会跳转到网易新闻App。可是如今(2015年09月23日)版本的网易新闻在 iOS9 就不能正常跳转,会跳转到 App Store 页面并提示要不要打开 App Store。

这是极可能是由于使用了 HTML 的 iframe 元素,并将自定义的连接放进了该元素中

举例说明:

enter image description here

我以前写的一个 Demo: 模仿 《简书 App》 的效果:在html中跳转到App中的对应页面,并能从App跳转到原来的网址,在例子中直接调用自定义连接在 iOS9上是能够跳转到 App 中的,然而,若是用 iframe 元素包起来就会变不可用。

参考连接:

  1. HTML 的iframe 标签
  2. iOS 9 safari iframe src with custom url scheme not working

9.Demo五、Demo6--- 搜索 API

导入两个 framework,

而后像设置tableView 的 cell 同样设置下每个“搜索元素”,详情见代码。

enter image description here

既然刚才说了搜索元素与 tableView 的 cell 很是类似:那么咱们就展现一下如何让 tableView 与 CoreSpotlightSearch 进行结合:

详见 Demo6,Demo6 与 Demo5 的主要差别在于:在点击搜索结果跳转到 App 后,还会进一步根据搜索的内容 push 到相应的详情页中:

enter image description here

结束语

若是你在开发中遇到什么新的 iOS9 的坑,或者有什么适配细节本文没有说起,欢迎给本仓库提 pull request。也欢迎在微博@iOS程序犭袁 或在“iOS9开发学习交流群:141607754”中交流。

相关文章
相关标签/搜索