在app內(nèi)拿到用戶安裝app前信息的解決方案(支持iOS11)

如何讓用戶更順滑的完成“由外而內(nèi)”的“激活過程”
iOS11前后的Cookie共享和深度鏈接:SFSafariViewController/SFAuthenticationSession + Associated Domains

2017/10/23更新:
隨著逐漸在項目里把這一部分東西具體化沼撕,發(fā)現(xiàn)之前寫的內(nèi)容不夠系統(tǒng)化宾尚,這次把相關(guān)的內(nèi)容都體系化一下,算是一套方案吧翘狱。

大致流程如下圖:


流程圖

常規(guī)的從外進入App內(nèi)部狠毯,有很成熟且簡單的方案:私有協(xié)議蚕脏,但使用私有協(xié)議的前提是你當(dāng)前的設(shè)備上已經(jīng)安裝了你自己的App髓削。
本文要討論就是微服,將用戶的激活/信息收集過程變成一個單向的線性流程回挽,當(dāng)前設(shè)備是否安裝了App不再是障礙没咙,也不需要用戶手動返回進行重復(fù)操作。

  1. 通過Associated Domains(也就是首版內(nèi)容中的Universal Links)來判斷當(dāng)前設(shè)備是否安裝了App千劈,也就是祭刚,有App時就直接跳轉(zhuǎn)到App內(nèi)部了,如果沒有的話,使用Associated Domains的web頁面加載完成后涡驮,立即跳轉(zhuǎn)到新的頁面暗甥,提示用戶在Safari中打開(因為在微信中存儲的Cookie無法與我們自身的App共享);

  2. 用戶下載好App后捉捅,打開App撤防,我們以是否為第一次打開App為判斷標(biāo)準(zhǔn),如果是的話棒口,就與Safari共享Cookie寄月,檢測Cookie數(shù)據(jù),這樣就獲取到了app存在前產(chǎn)生的信息无牵。
    如何獲取到呢漾肮?這里有版本的區(qū)別:
    iOS11以后的版本:使用SFAuthenticationSession這個類,這也是更新前這篇文章所主要闡述的茎毁;
    iOS11之前的版本(iOS9+):使用SFSafariViewController這個類克懊,這個類的使用方法網(wǎng)上有很多前輩寫過,我也不做贅述了七蜘。

這里格外強調(diào)一下他們之間的差異:從第4步和第7步也可以看出來谭溉,SFAuthenticationSession的

- (instancetype)initWithURL:(NSURL *)URL callbackURLScheme:(nullable NSString *)callbackURLScheme completionHandler:(SFAuthenticationCompletionHandler)completionHandler;

方法有個回調(diào)的block,其中的參數(shù)callbackURL實際上就是當(dāng)前打開的web頁面調(diào)用私有協(xié)議的URL崔梗,這里就可以讓web帶上Cookie中的值通過私有協(xié)議傳遞給我們夜只;

而SFSafariViewController同樣是打開指定的web頁面,web同樣發(fā)起私有協(xié)議蒜魄,但是此時的私有協(xié)議會即時“生效”扔亥,也就是直接調(diào)用了AppDelegate中的

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation谈为;

方法旅挤,我們就可以從這兩個方法中拿到所需的參數(shù),也就是和從外部調(diào)用私有協(xié)議的處理過程是一致的伞鲫,也就是從app內(nèi)調(diào)用app自己(這里我最開始繞了一段時間才整明白?????♂?)粘茄。

對于SFAuthenticationSession的處理還有一個需要注意的點:SFSafariViewController在加載完成后,有代理方法可以把它dismiss掉秕脓,但是SFAuthenticationSession只有在產(chǎn)生回調(diào)時才會自動dismiss柒瓣,也就是說,如果用戶在系統(tǒng)Safari中沒有所需的Cookie并且還同意打開SFAuthenticationSession中的頁面的話吠架,那么用戶就必須在手動把頁面在關(guān)上芙贫,而且這一系列過程都不會對用戶帶來任何有益的結(jié)果,那醬紫對用戶就很不友好了~因此傍药,建議讓web同學(xué)在沒有Cookie的時候磺平,也調(diào)用私有協(xié)議魂仍,然后帶上指定的標(biāo)識,那么我們就可以在接收到指定標(biāo)識時繼續(xù)常規(guī)的流程拣挪,而無需麻煩用戶擦酌。
(對了,我之前提問的關(guān)于“SFSafariViewController如何隱性展示”的問題菠劝,我最后使用的解決方案是直接present出來赊舶,然后加載完成后dismiss掉,因為我們的驗證頁面幾乎沒有需要加載的元素闸英,只是邏輯判斷的代碼锯岖,加載速度會很快,幾乎是一閃而過的甫何。但是相信還是有童鞋用更隱性的方式完成了的出吹,還請賜教!)

  1. 這里之所以采用Associated Domains辙喂,是出于以下兩點優(yōu)勢:
  • web同學(xué)沒法準(zhǔn)確判斷當(dāng)前設(shè)備是否安裝了app捶牢,如果強行調(diào)用私有協(xié)議的話,在未安裝的情況下巍耗,Safari上會出現(xiàn)無法執(zhí)行的提示秋麸,用戶體驗很不友好;
  • 突破微信的限制:現(xiàn)今“由外而內(nèi)”的方式一般都是以二維碼為起點炬太,而且大多數(shù)用戶都在使用微信掃碼灸蟆,但是微信會對自己不支持的私有協(xié)議進行攔截,而Associated Domains則可以完全突破他的限制亲族,直接有效的進入app炒考;

除了更新前說的一些內(nèi)容,這里我想格外強調(diào)一下的就是跨域問題霎迫。這個是指:如果你希望app在從A頁面跳轉(zhuǎn)到B頁面的URL時被喚起斋枢,那么,你就需要讓A知给、B兩個頁面不在同一個域瓤帚,例如,a.xtshow.com和b.xtshow.com就是跨域的兩個URL(這一點在一些教程中沒有太明確涩赢,多虧一篇文章中一個同學(xué)的回復(fù)才幫我繞出去戈次,鳴謝!)筒扒。

除了指定技術(shù)點上的注意之外朝扼,還希望大家能夠注意一下ViewController層級結(jié)構(gòu)的問題,以免造成相互覆蓋霎肯,或者無法present擎颖、push的情況,具體情況還要看各位的工程特點观游。

分割線(首版)


  • 前言

項目中新增了一個掃描二維碼 激活賬號+指定商品 的功能搂捧,加完之后發(fā)現(xiàn)不能過審。懂缕。允跑。才發(fā)現(xiàn)發(fā)現(xiàn)原來二維碼功能并不是能隨便加的(官方文檔)。搪柑。聋丝。但是因為推廣渠道特殊,二維碼的功能一定要有工碾,因此考慮將App內(nèi)的全部二維碼掃描功能移除弱睦,換成從外部掃描跳轉(zhuǎn)App的方式。
又進一步考慮用戶體驗的問題渊额,希望能夠讓用戶只掃描一次二維碼就線性完成從下載到打開激活全過程况木,然后就有了這片文章~

  • 開始

常規(guī)的custom scheme瀏覽器跳轉(zhuǎn)什么的就不在此處贅述了。此處對我來說的新鮮點應(yīng)該是Deep Link相關(guān)實現(xiàn)旬迹。

Deep Link:顧名思義火惊,就是深度鏈接。鏈接很好理解奔垦,就是把一些東西串起來屹耐,那何為深度呢?這就要針對iOS系統(tǒng)的特性來說了椿猎,iOS下App絕大多數(shù)情況下都是從App Store上下載下來的惶岭。剛下載的app是完全“初始化”的,而且出于Apple對用戶隱私的保護鸵贬,App是無法得知當(dāng)前用戶是誰俗他。但我們又希望通過某些方式能夠在第一次用戶開啟App時,不需要用戶手動的告訴我們他是誰(此處的“誰”不是客觀的是誰阔逼,而是對于我們來說“用戶是誰”)兆衅。

SFSafariViewController

通過一番Google~發(fā)現(xiàn)了SFSafariViewController這么個好玩意兒!SFSafariViewController的核心好處就是:共享系統(tǒng)Safari的Cookie嗜浮。
也就是說羡亩,通過SFSafariViewController可以拿到你在Safari瀏覽器中打開的某個網(wǎng)頁的Cookie。Cookie中就可以存儲一些你需要的信息:例如激活碼危融,用戶的登錄信息等畏铆。而這個web頁面一般就是在三方app,例如微信中掃碼或者點擊分享等操作打開的吉殃,再在Safari中打開辞居,而Cookie則是web小伙伴根據(jù)二維碼中的信息進行處理后寫入web頁面中的楷怒,因此,這個是需要web小伙伴的配合的哦瓦灶。
這樣鸠删,在第一次打開App時,檢測一下Safari中指定頁面的Cookie贼陶,就可以拿到我們所需的信息刃泡。對應(yīng)到我現(xiàn)在的項目,也就是可以知道用戶在安裝App之前碉怔,掃得二維碼的信息烘贴,也就不要用再讓用戶退出app,回到微信掃第二次碼了~做到了“一碼兩用”-既能下載又能傳值撮胧!
具體的實踐方案桨踪,我在這就不再贅述了,直接在Google中查SFSafariViewController就會有一大票前輩們的文章可供參考趴樱。

  • 重點

弄好之后馒闷,無意之間在stackoverflow上發(fā)現(xiàn)一個哥們說:SFSafariViewController在iOS11上要被封了(此處的被封不是DEPRECATED,而是Cookie共享的機制沒有了)叁征!雖然只有一個贊纳账,但也寧可信其有不可啊捺疼!萬一廢了半天勁弄的東西最后半個來月之后就不能用了那豈不是鬧心死了所以又去官網(wǎng)確認(rèn)了下疏虫。。啤呼。果然卧秘。。官扣。

WX20170828-142216.png

官方原文地址

但是還好翅敌,柳暗花明又一村,下面出來個instead的~ 就說嗎~ 不應(yīng)該封了一條路之后惕蹄,不開條新路出來嘛~

SFAuthenticationSession

這個是iOS11新加入的API蚯涮,因此需要Xcode9 + iOS11的環(huán)境。

需要引入SafariServices框架:

#import <SafariServices/SafariServices.h>

這個玩意兒的使用方式比SFSafariViewController還要簡單卖陵,只有一個可用的初始化方法:

- (instancetype)initWithURL:(NSURL *)URL callbackURLScheme:(nullable NSString *)callbackURLScheme completionHandler:(SFAuthenticationCompletionHandler)completionHandler;

很奇怪遭顶,其實這個初始化方法前面還有個初始化方法:

- (instancetype)init NS_UNAVAILABLE;

但是直接就給不可用了。可能由于現(xiàn)在還是beta版吧泪蔫,估計正式版之后會直接刪掉吧棒旗。

這個方法有三個參數(shù):URL、callbackURLScheme撩荣、completionHandler铣揉。
官方庫中的解釋很詳細(xì)饶深,但我還是啰嗦兩句吧:

URL:需要同步Cookie的網(wǎng)站的URL;
callbackURLScheme:App期望在回調(diào)URL中出現(xiàn)的自定義協(xié)議逛拱;
completionHandler:處理完成后的回調(diào)粥喜。

callbackURLScheme,有兩種設(shè)置方法橘券,除了在此處直接設(shè)置外,還可以在Targets->Info->URL Types中設(shè)置卿吐。
completionHandler旁舰,在請求用戶權(quán)限時,如果點擊了取消嗡官,也會調(diào)用此回調(diào)箭窜,error code = 1;而且在出現(xiàn)任何回調(diào)時衍腥,無論是出現(xiàn)error還是callbackURL磺樱,都會自動dismiss剛彈出的web頁面。

示例代碼1:

SFAuthenticationSession *auth = [[SFAuthenticationSession alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"] callbackURLScheme:@"xtshow" completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) {
        //用戶點擊取消時婆咸,會出現(xiàn)error:SFAuthenticationErrorCanceledLogin
        NSLog(@"%@---%@",callbackURL,error);
}];

實例化一個SFAuthenticationSession后竹捉,一定要使用strong屬性全局強持有這個auth,不然的話會出現(xiàn)申請權(quán)限alert一閃而過的情況。很好奇為什么oc中會出現(xiàn)這類不strong強引用就會馬上消失掉的對象尚骄?是因為單例嗎块差?單例如果不強引用就會拿上release掉以防止影響其他對象持有嗎?有木有大神給解個惑~謝謝了~~倔丈!如果不強引用的話憨闰,離開作用域(一般就是當(dāng)前代碼塊)時,對象就會被釋放掉需五。

示例代碼2:

self.auth = auth;

最后啟用auth就可以了鹉动。
示例代碼3:

[self.auth start]

啟用后就會present一個很類似于SFSafariViewController的網(wǎng)頁。

SFAuthenticationSession類中還有最后一個方法:

- (void)cancel;

用來手動dismiss web頁面宏邮。

還有一個好消息就是泽示,由于對web的邏輯是一致的,所以web小伙伴的代碼都不需要改變蜀铲,用一套就可以了边琉!

用戶可見的部分,最大的區(qū)別就是记劝,在調(diào)用SFAuthenticationSession的時候变姨,會出現(xiàn)一個系統(tǒng)的alert,請求用戶的權(quán)限允許


調(diào)用SFAuthenticationSession后提示用戶的alert

嗯。厌丑。定欧。渔呵。。web頁面的邏輯我還是啰嗦兩句吧:
web判斷是否有Cookie:
沒有的話砍鸠,應(yīng)該是出現(xiàn)在Safari中的情況扩氢,web會把url中的參數(shù)寫入到Cookie中;
有的話爷辱,則應(yīng)該是出現(xiàn)在App內(nèi)的情況录豺,web頁面帶著需要傳遞參數(shù)請求私有協(xié)議,例如:

xtshow://name=cool&password=bugaosuni

那么此時我們的app就能拿到所需的參數(shù)饭弓。

Universal Links

之前在閑逛的時候双饥,見過這個東東,這是一種可以突破微信限制弟断,直接從微信跳轉(zhuǎn)到我們自己的App中的技術(shù)咏花,這次也用上了。具體操作可以Google前輩們的文章~(這里有個小坑阀趴,很多文章中會提到一個Apple官方的驗證網(wǎng)站,驗證是否實現(xiàn)了Universal Links昏翰,但是我在這個網(wǎng)站上一直驗證不通過,實際上各種特性刘急,記事本長按啊棚菊、web頁面下拉app內(nèi)打開什么的特性都是有的~所以我個人懷疑這個網(wǎng)站的驗證不是絕對正確的,還是建議記事本長按和web頁面下拉app內(nèi)打開等方式驗證排霉。)

總而言之窍株,custom scheme、SFSafariViewController攻柠、SFAuthenticationSession球订、Universal Links這些技術(shù),在合適的時機搭配使用瑰钮,可以很好的提升用戶在使用app的順暢感冒滩。

還有一個問題,想請教一下使用過SFSafariViewController并已經(jīng)過審的大神們~想要讓用戶無感知的實現(xiàn)SFSafariViewController浪谴,看到有的大神是設(shè)置size為(0.5,0.5)开睡,還有大神是設(shè)置alpha=0.05,但是由于Guideline 5.1.1(iv)里寫得不是那么清楚苟耻,到底怎么做能夠平穩(wěn)過審呢篇恒?謝謝!

這是本人的第一篇技術(shù)博客凶杖,Markdown也是邊用邊查的[手動捂臉]胁艰。。。之所以寫這篇文章是因為之前找SFAuthenticationSession方面的資料的時候腾么,發(fā)現(xiàn)全網(wǎng)好像都是英文的奈梳,中文版的也只有一篇灣灣同行的繁體中文版(可能是太簡單了各位大神覺得不需要寫一篇文章吧),因此就想自己寫一篇~ 第一次寫解虱,語言組織啊攘须、排版啊什么的都很粗糙,希望各位大神能夠多多指教~ 不喜勿噴哈~
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末殴泰,一起剝皮案震驚了整個濱河市于宙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悍汛,老刑警劉巖限煞,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異员凝,居然都是意外死亡,警方通過查閱死者的電腦和手機奋献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門健霹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓶蚂,你說我怎么就攤上這事糖埋。” “怎么了窃这?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵瞳别,是天一觀的道長。 經(jīng)常有香客問我杭攻,道長祟敛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任兆解,我火速辦了婚禮馆铁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锅睛。我一直安慰自己埠巨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布现拒。 她就那樣靜靜地躺著辣垒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪印蔬。 梳的紋絲不亂的頭發(fā)上勋桶,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音,去河邊找鬼哥遮。 笑死岂丘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眠饮。 我是一名探鬼主播奥帘,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仪召!你這毒婦竟也來了寨蹋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤扔茅,失蹤者是張志新(化名)和其女友劉穎已旧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體召娜,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡运褪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了玖瘸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸讹。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雅倒,靈堂內(nèi)的尸體忽然破棺而出璃诀,到底是詐尸還是另有隱情,我是刑警寧澤蔑匣,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布劣欢,位于F島的核電站,受9級特大地震影響裁良,放射性物質(zhì)發(fā)生泄漏凿将。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一价脾、第九天 我趴在偏房一處隱蔽的房頂上張望丸相。 院中可真熱鬧,春花似錦彼棍、人聲如沸灭忠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弛作。三九已至,卻和暖如春华匾,著一層夾襖步出監(jiān)牢的瞬間映琳,已是汗流浹背机隙。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萨西,地道東北人有鹿。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像谎脯,于是被迫代替她去往敵國和親葱跋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容