中文快速導航:
1.iOS9網絡適配_ATS:改用更安全的HTTPS(見Demo1)
i. WHAT(什麼是SSL/TLS?跟HTTP和HTTPS有什麼關系)
ii. WHY(以前的HTTP不是也能用嗎?為什麼要用SSL/TLS?Apple是不是又在反人類?)
iii. HOW(如何適配?---弱弱地問下:加班要多久?)
a.第1種情況:HTTPS Only (只有HTTPS,所有情況下都使用ATS)
b.第2種情況:Mix & Match(混合)
c.第3種情況:Opt Out(禁用ATS)
d.第4種情況:Opt Out With Exceptions(除特殊情況外,都不使用ATS)
e.Certificate Transparency
iv. Q-A
2.iOS9新特性_更靈活的後台定位(見Demo2)
3.企業級分發
i. iOS9以後,企業級分發ipa包將遭到與Mac上dmg安裝包一樣的待遇:默認不能安裝,也不再出現“信任按鈕”
ii. iOS9以後,企業分發時可能存在:下載的ipa包與網頁兩者的 bundle ID 無法匹配而導致下載失敗的情況
4.Bitcode
5.iOS9 URL Scheme 適配_引入白名單概念(見Demo3)
i.常見 URL Scheme
ii.Q-A
6.iPad適配Slide Over 和 Split View
7.字體間隙變大導致 UI 顯示異常
8.升級 Xcode7 後的崩潰與警告
i.iOS9 下使用 Masonry 會引起崩潰的一種情況
ii.Xcode 升級後,舊的狀態欄的樣式設置方式會引起警告
a.Demo4---navigationController狀態欄樣式新的設置方法
iii.Xcode7 在 debug 狀態下也生成 .dSYM 文件引起的警告
iv.Xcode7 無法使用 8.x 系統的設備調試,一運行就報錯 there is an intenal API error
v.使用了 HTML 的 iframe 元素可能導致無法從 Safari 跳轉至 App
vi.iOS 9鎖屏控制台會打印警告
vii.Xcode7 上傳應用時提示 ITMS-90535 Unable to publish iOS app with xxx SDK 的問題
9.Demo5、Demo6--- 搜索 API
10.iOS國際化問題:當前設備語言字符串返回有變化
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,應該滿足如下條件:
Transport Layer Security協議版本要求TLS1.2以上
服務的Ciphers配置要求支持Forward Secrecy等
證書簽名算法符合ATS要求等
官方文檔 App Transport Security Technote 對ATS 的介紹:
注:有童鞋反映:服務器已支持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這一做法的評論:
這是某社交App上討論,看來業內還是吐槽聲和肯定聲同在。
結論是:"跟你很有關系,加班吧,少年!"
書歸正傳【嚴肅臉】,我們正式討論下 WHAT,WHY,HOW:
WHAT(什麼是SSL/TLS?跟HTTP和HTTPS有什麼關系)
WHY(以前的HTTP不是也能用嗎?為什麼要用SSL/TLS?!Apple是不是又在反人類?)
HOW(如何適配?---弱弱地問下:加班要多久?)
1.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
或者: HTTPS = “HTTP over SSL”
也就是說:Apple讓你的HTTP采用SSL/TLS協議,就是讓你從HTTP轉到HTTPS。而這一做法,官方文檔稱為ATS,全稱為App Transport Security。
2.WHY(以前的HTTP不是也能用嗎?為什麼要用SSL/TLS?Apple是不是又在反人類?)
不使用SSL/TLS的HTTP通信,就是不加密的通信!
不使用SSL/TLS的HTTP通信,所有信息明文傳播,帶來了三大風險:
竊聽風險(eavesdropping):第三方可以獲知通信內容。
篡改風險(tampering):第三方可以修改通信內容。
冒充風險(pretending):第三方可以冒充他人身份參與通信。
SSL/TLS協議是為了解決這三大風險而設計的,希望達到:
所有信息都是加密傳播,第三方無法竊聽。
具有校驗機制,一旦被篡改,通信雙方會立刻發現。
配備身份證書,防止身份被冒充。
SSL/TLS的作用,打個比方來講:
如果原來的 HTTP 是塑料水管,容易被戳破;那麼如今新設計的 HTTPS 就像是在原有的塑料水管之外,再包一層金屬水管(SSL/TLS協議)。一來,原有的塑料水管照樣運行;二來,用金屬加固了之後,不容易被戳破。
3.HOW(如何適配?---弱弱地問下:加班要多久?)
正如文章開頭所說:
TLS 1.2 協議 強制增強數據訪問安全 系統 Foundation 框架下的“相關網絡請求”將不再默認使用 HTTP 等不安全的網絡協議,而默認采用 TLS 1.2。服務器因此需要更新,以解析相關數據。如不更新,可通過在 Info.plist 中聲明,倒退回不安全的網絡請求。
總之:
要麼咱們iOS程序猿加班,要麼後台加班:
方案一:立即讓公司的服務端升級使用TLS 1.2,以解析相關數據。
方案二:雖Apple不建議,但可通過在 Info.plist 中聲明,倒退回不安全的網絡請求依然能讓App訪問指定http,甚至任意的http,具體做法見gif圖,示例Demo見 Demo1
這也是官方文檔和WWDC給出的解決方案:
a.Apple官方文檔
b.WWDC Session: "Networking with NSURLSession" session( 【WWDC 2015 session 703, “Privacy and Your App” O網頁鏈接 】, 時間在30:18左右)
即使你的應用使用的是:你沒有權限控制的CDN (Content Delivery Network),而且它不支持HTTPS!
也別擔心,Apple都替你考慮好了:
正如你在上圖中看到的:蘋果官方提供了一些可選配置項來決定是否開啟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的服務器,那麼你太幸運了。你的應用不需要做任何改變。
唯一需要做的事情就是使用 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 要求的服務器的域名時:
api.insecuredomain.com
cdn.domain.com
thatotherdomain.com
你可以分別設置如下:
api.insecuredomain.com
Info.plist 配置中的XML源碼如下所示:
在 plist 文件裡顯示如下:
我們定義的第一個“例外”(Exception)告訴ATS當與這個子域交互的時候撤銷了必須使用HTTPS的要求。注意這個僅僅針對在“例外”(Exception)中聲明了的子域。非常重要的一點是要理解NSExceptionAllowsInsecureHTTPLoads關鍵字並不僅僅只是與使用HTTPS相關。這個“例外”(Exception)指明了對於那個域名,所有的App Transport Security的要求都被撤銷了。
cdn.domain.com Info.plist 配置中的XML源碼如下所示:
在 plist 文件裡顯示如下:
很可能你的應用是與一個支持HTTPS傳輸數據的服務器交互,但是並沒有使用TLS 1.2或更高。在這種情況下,你定義一個“例外”(Exception),它指明應該使用的最小的TLS的版本。這比完全撤銷那個域名的App Transport Security要更好更安全。
thatotherdomain.com
Info.plist 配置中的XML源碼如下所示:
在 plist 文件裡顯示如下:
NSIncludesSubdomains 關鍵字告訴 App Transport Security 這個“例外”(Exception)適用於這個特定域名的所有子域。這個“例外”(Exception)還進一步通過擴展可接受的密碼列表來定義這個域名可以使用不支持forward secrecy( NSExceptionRequiresForwardSecrecy ) 協議的密碼。想了解更多關於forward secrecy的信息,推薦去看官方文檔 Apple's technote 。
如果你的App中同時用到了這三個域名,那麼應該是這樣:
3. Opt Out(禁用ATS)
上面是比較嚴謹的做法,指定了能訪問哪些特定的HTTP。當然也有暴力的做法: 徹底倒退回不安全的HTTP網絡請求,能任意進行HTTP請求,比如你在開發一款浏覽器App,或者你想偷懶,或者後台想偷懶,或者公司不給你升級服務器。。。
你可以在Info.plist 配置中改用下面的XML源碼:
在 plist 文件裡顯示如下:
4. Opt Out With Exceptions(除特殊情況外,都不使用ATS)
上面已經介紹了三種情景,還有一種可能你也會遇到:
當你的應用撤消了App Transport Security,,但同時定義了一些“例外”(Exception)。當你的應用從很多的服務器上取數據,但是也要與一個你可控的API交互。在這種情況下,在應用的Info.plist文件中指定任何加載都是被允許的,但是你也指定了一個或多個“例外”(Exception)來表明哪些是必須要求 App Transport Security的。下面是Info.plist文件應該會有的內容:
在 plist 文件裡顯示如下:
5.Certificate Transparency
雖然ATS大多數安全特性都是默認可用的,Certificate Transparency 是必須設置的。如果你有支持Certificate Transparency的證書,你可以檢查NSRequiresCertificateTransparency關鍵字來使用Certificate Transparency。再次強調,如果你的證書不支持Certificate Transparency,此項需要設置為不可用。
如果需要調試一些由於采用了ATS而產生的問題,需要設置CFNETWORK_DIAGNOSTICS為1,這樣就會打印出包含被訪問的URL和ATS錯誤在內的NSURLSession錯誤信息。要確保處理了遇到的所有的錯誤消息,這樣才能使ATS易於提高可靠性和擴展性。
6.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
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
或者更直截了當一點,直接修改:
Project -> Your Target —>info-> Custom iOS target properties-> 添加禁用 ATS 的屬性
還有一種可能性是:禁用 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://
當然,你也可以讓公司服務端的同事參考Apple提供官方指南App Transport Security Technote進行服務的升級配置以滿足ATS的要求:
一個符合 ATS 要求的 HTTPS,應該滿足如下條件:
Transport Layer Security協議版本要求TLS1.2以上
服務的Ciphers配置要求支持Forward Secrecy等
證書簽名算法符合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 配置如下:
對應的 Info.plist 的XML源碼是:
3.企業級分發
有兩處變化:
iOS9以後,企業級分發ipa包將遭到與Mac上dmg安裝包一樣的待遇:默認不能安裝,也不再出現“信任按鈕”
iOS9以後,企業分發時可能存在:下載的ipa包與網頁兩者的 bundle ID 無法匹配而導致下載失敗的情況
1. iOS9以後,企業級分發ipa包將遭到與Mac上dmg安裝包一樣的待遇:默認不能安裝,也不再出現“信任按鈕”
iOS9之前,企業級分發十分方便:點擊App出現“信任按鈕”,
iOS9以後,企業級分發ipa包將遭到與Mac上dmg安裝包一樣的待遇:默認不能安裝,也不再出現“信任按鈕”
必須讓用戶進行gif圖中的設置:
2. iOS9以後,企業分發時可能存在:下載的ipa包與網頁兩者的 bundle ID 無法匹配而導致下載失敗的情況
iOS9升級後眾多企業分發的 app 已經出現了不能安裝的情況,而iOS8或更早的系統不受影響。那是因為從iOS9以後,系統會在 ipa 包下載完之後,拿ipa包中的 bundle ID 與網頁中的 plist 文件中的 bundle ID 進行比對,不一致不允許安裝。
錯誤提示如下:
網頁中的 plist 文件中的 bundle ID 的作用可參考 《iOS:蘋果企業證書通過網頁分發安裝app》 。
正如這篇文章提到的,“網頁中的 plist 文件”是習慣的叫法,也有人稱作“manifest文件”,比如 這篇文章。
而iOS9之前,蘋果不會檢查這一項,因此iOS9之前可以安裝。
導致這一錯誤的原因除了粗心,還有開發者是故意設置不一致,據開發者說:
當初服務器 plist 的 bundle id 上故意做成成不一致。是為了解決一些人安裝不上的問題。
詳情可參考: 《升級到ios 9,企業版發布現在無法安裝成功了,有人遇到了這種問題嗎?》
如何知道是因為 bundle id 不一致造成的無法安裝?
通過查看設備上的日志信息:有一個 itunesstored 進程提示安裝信息:
itunesstored → : [Download]: Download task did finish: 8 for download: 2325728577585828282 itunesstored → : [ApplicationWorkspace] Installing download: 2325728577585828282 with step(s): Install itunesstored → : [ApplicationWorkspace]: Installing software package with bundleID: com.***.***: bundleVersion: 1.01 path: /var/mobile/Media/Downloads/2325728577585828282/-1925357977307433048 itunesstored → : BundleValidator: Failed bundleIdentifier: com.***.**** does not match expected bundleIdentifier: com.***.********* itunesstored → : [ApplicationWorkspace]: Bundle validated for bundleIdentifier: com.****.******success: 0 itunesstored → : LaunchServices: Uninstalling placeholder for appcom.****.*******(Placeholder) itunesstored → : LaunchServices: Uninstalling appcom.****.*****(Placeholder)
其中的這一句很重要:
itunesstored → : 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的。
適配方法:
a.兩者的 bundle id 修改一致
一旦出現iOS9能夠安裝企業版本APP,iOS9以下版本不能安裝,一定先查看安裝日志,然後核對每個參數配置。
manifest文件的參考配置。
b.使用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主要有三個機制:
①Slicing
開發者把App安裝包上傳到AppStore後,Apple服務會自動對安裝包切割為不同的應用變體(App variant),當用戶下載安裝包時,系統會根據設備型號下載安裝對應的單個應用變體。
②On-Demand Resources
ORD(隨需資源)是指開發者對資源添加標簽上傳後,系統會根據App運行的情況,動態下載並加載所需資源,而在存儲空間不足時,自動刪除這類資源。
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 編譯機制,就需要注意以下幾點:
Xcode 7默認開啟 Bitcode ,如果應用開啟 Bitcode,那麼其集成的其他第三方庫也需要是 Bitcode 編譯的包才能真正進行 Bitcode 編譯
開啟 Bitcode 編譯後,編譯產生的 .app 體積會變大(中間代碼,不是用戶下載的包),且 .dSYM 文件不能用來崩潰日志的符號化(用戶下載的包是 Apple 服務重新編譯產生的,有產生新的符號文件)
通過 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 setting ENABLE_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,方法見下圖
我們可以在”Build Settings”->”Enable Bitcode”選項中看到:
用 Xcode 7+ 新建一個 iOS 程序時, bitcode 選項默認是設置為YES的。現在需要改成NO。
如果我們開啟了 bitcode ,在提交包時,下面這個界面也會有個 bitcode 選項:
那麼 SDK 廠商如何支持 bitcode 呢?答案是只要在 Xcode7上重新編譯一下就 ok 了。(請確保默認開啟的 bitcode 沒有去主動關閉)
更多信息,請移步
bitcode 蘋果官方文檔
WWDC 2015 Session 102: "Platforms State of the Union"
5.Demo3---iOS9 URL Scheme 適配_引入白名單概念
WWDC 2015 Session 703: "Privacy and Your App ( 時間在30:18左右)關於 URL scheme 的介紹,指出:
也就是說:在iOS9中,如果使用 canOpenURL: 方法,該方法所涉及到的 URL scheme 必須在"Info.plist"中將它們列為白名單,否則不能使用。key叫做LSApplicationQueriesSchemes ,鍵值內容是
白名單上限是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中 openURL: 方法沒有什麼實質性的變化,僅僅多了一個確認動作:
蘋果為什麼要這麼做?
在 iOS9 之前,你可以使用 canOpenURL: 監測用戶手機裡到底裝沒裝微信,裝沒裝微博。但是也有一些別有用心的 App ,這些 App 有一張常用 App 的 URL scheme,然後他們會多次調用canOpenURL: 遍歷該表,來監測用戶手機都裝了什麼 App ,比如這個用戶裝了叫“大姨媽”的App,你就可以知道這個用戶是女性,你就可以只推給這個用戶女性用品的廣告。這是侵犯用戶隱私的行為。
這也許就是原因。
本項目中給出了一個演示用的 Demo ,倉庫的文件夾叫“Demo3_iOS9URLScheme適配_引入白名單概念”,Demo引用自 LSApplicationQueriesSchemes-Working-Example
Demo結構如下:
主要演示的情景是這樣的:
假設有兩個App: weixin(微信) and 我的App. 我的App 想監測 weixin(微信) 是否被安裝了. "weixin(微信)" 在 info.plist 中定義了 URL scheme :
我的App 想監測 weixin(微信) 是否被安裝了 :
即使你安裝了微信,在iOS9中,這有可能會返回NO:
因為你需要將 "weixin(微信)" 添加到 “我的App” 的 info.plist 文件中:
(以上只是為了演示,實際開發中,你不僅需要添加“weixin”還需要“wechat”這兩個。具體 )
另外,推薦一篇博文,其中最關鍵的是以下部分:
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.
常見 URL Scheme
如果想一次性集成最常用的微信、新浪微博、QQ、支付寶四者的白名單,則配置如下:
plist 文件看起來會是這樣的:
其他平台可在下面的列表中查詢: 各平台OpenURL白名單說明:
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
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
【iPad適配Slide Over 和 Split View】 若想適配multi tasking特性,唯一的建議:棄純代碼,改用storyboard、xib,縱觀蘋果WWDC所有Demo均是如此:
Mysteries of Auto Layout, Part 1
What's New in Storyboards
Implementing UI Designs in Interface Builder
Getting Started with Multitasking on iPad in iOS 9
Optimizing Your App for Multitasking on iPad in iOS
7.字體間隙變大導致 UI 顯示異常
iOS8中,字體是Helvetica,中文的字體有點類似於“華文細黑”。只是蘋果手機自帶渲染,所以看上去可能比普通的華文細黑要美觀。iOS9中,中文系統字體變為了專為中國設計的“蘋方” 有點類似於一種word字體“幼圓”。字體有輕微的加粗效果,並且最關鍵的是字體間隙變大了!
所以很多原本寫死了width的label可能會出現“...”的情況:
如果不將 label 的 width 寫死,僅僅添加左端約束則右端的四個數字會越界
所以為了在界面顯示上不出錯,就算是固定長度的文字也還是建議使用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 在部分機型可能導致崩潰。崩潰信息如下圖。
解決:更新新浪微博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 升級後,舊的狀態欄的樣式設置方式會引起警告
: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.: CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
解決辦法:
刪除原先的設置代碼,通常老的設置方式是這樣的:
//設置狀態欄的白色 [[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源碼:
UIViewControllerBasedStatusBarAppearance
然後使用新的方式來實現狀態欄的樣式:
- (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 @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上來,會報警告:
這是 debug 編譯時導出符號文件出現的告警,然而新建的Xcode7工程不會有該問題。
解決方法是讓 debug 編譯的時候不生成符號文件:
Xcode7 無法使用 8.x 系統的設備調試,一運行就報錯 there is an intenal API error
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 元素,並將自定義的鏈接放進了該元素中
舉例說明:
我之前寫的一個 Demo: 模仿 《簡書 App》 的效果:在html中跳轉到App中的對應頁面,並能從App跳轉到原來的網址,在例子中直接調用自定義鏈接在 iOS9上是可以跳轉到 App 中的,然而,如果用 iframe 元素包起來就會變不可用。
參考鏈接:
HTML 的iframe 標簽
iOS 9 safari iframe src with custom url scheme not working
iOS9鎖屏控制台會打印警告
加入運行如下示例代碼:
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^(void) { //在這個10秒內鎖屏 NSLog(@"准備休眠"); sleep(10); NSLog(@"打印成功"); }); }
應用運行過程中鎖屏,總是會出現以下提示:
** -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] ** unhandled action ->{ handler = remote; info ={ (1) = 5; }; }
當應用處於空閒狀態時(無網絡請求)鎖屏對於用戶而言並無較大影響,但是當應用在執行某個異步任務時(比如下拉刷新一下列表)鎖屏,重新解鎖進入就可能會發現異步任務失敗,控制台也會提示 Error 信息:
** -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] ** unhandled action ->{ handler = remote; info ={ (1) = 5; }; } error in __connection_block_invoke_2: Connection interrupted
以上情況不易復現,但確有發生。
在 iOS8 系統下測試並未發現此問題。
對此並未找到合理的解釋和對應的解決辦法,如果你有解決方法,歡迎提 PR !
9.Demo5、Demo6--- 搜索 API
導入兩個 framework,然後像設置tableView 的 cell 一樣設置下每一個“搜索元素”,詳情見代碼。
既然剛才說了搜索元素與 tableView 的 cell 非常相似:那麼我們就展示一下如何讓 tableView 與 CoreSpotlightSearch 進行結合:
詳見 Demo6,Demo6 與 Demo5 的主要差異在於:在點擊搜索結果跳轉到 App 後,還會進一步根據搜索的內容 push 到相應的詳情頁中:
10.iOS國際化問題:當前設備語言字符串返回有變化。
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSArray *allLanguage = [defaults objectForKey:@"AppleLanguages"]; NSString *currentLanguage = [allLanguage objectAtIndex:0]; NSLog(@"The current language is : %@", currentLanguage);
iOS 9 之前:以上返回結果:語言字符串代碼。例如:"zh-Hans"
iOS 9:以上返回結果:語言字符串代碼 + 地區代碼。例如:"zh-Hans-US"
備注:
1.請注意判斷當前語言類型,不要用以下形式的代碼了,不然在iOS9上就會遇到坑。
if ([currentLanguage isEqualToString:@"zh-Hans"])
可以使用:
if ([currentLanguage hasPrefix:@"zh-Hans"])
另外:對於中文,語言有:
簡體中文:zh-Hans
繁體中文:zh-Hant
香港中文:zh-HK
澳門中文:zh-MO
台灣中文:zh-TW
新加坡中文:zh-SG
備注:以上iOS9 當前語言字符串返回結果:語言字符串代碼 + 地區代碼。在某些情況下不是這樣,本人手機型號:大陸版電信iPhone5S/A1533/16GB測試結果:zh-HK/zh-TW,在地區為"中國"、"中國香港"、"中國台灣"的時候,顯示的還是zh-HK/zh-TW,一旦切換到其它地區,設備語言會自動的切換到中文繁體。請開發人員注意中文的問題!
結束語
如果你在開發中遇到什麼新的 iOS9 的坑,或者有什麼適配細節本文沒有提及,歡迎給本倉庫提 pull request。也歡迎在微博@iOS程序犭袁 或在“iOS9開發學習交流群:146652529”中交流。
疏漏之處,可前往閱讀下這個網站,這裡有每年 WWDC 演講的英文記錄。