1. 程式人生 > >【SFSafariViewController】之app與wap的cookie連線

【SFSafariViewController】之app與wap的cookie連線

簡介

本文主要介紹,app跨域訪問app外部的瀏覽器的資料的方案,包括外部safari,或者QQ,微信,手百等外部app內的瀏覽器。

主要使用場景就是:

使用者在別的wap網頁上,產生了使用者行為,使用者資料,但是還沒下載app,當用戶下載app後,打算直接在app內延續之前在wap上的行為和資料的時候,就需要運用到跨越瀏覽器與app鴻溝的,互通方案。

簡單舉個例子就是:

使用者在微信瀏覽器裡,訪問某個頁面,感興趣並且登陸了,然後引導下載了app,等使用者下載完app後第一次開啟,希望能自動就完成登陸,甚至同步下來一些剛才使用者在wap頁面上操作的資料

如果能發生跨瀏覽器與app的互通,除了這個case之外,還可以有更多地自由發揮,設計出更加舒暢的使用者體驗

想要實現這樣目前看有2個方案,各自都有弊端,都不是完美的,本文會詳細說明這兩個方案

  • 裝置指紋唯一識別方案
  • iOSSafariCookie互通方案

裝置指紋唯一識別方案

<!-- more -->

如果使用者在wap頁面,能通過某種方式識別到唯一的裝置標識,當用戶離開去下載app,下載完成第一次開啟app的時候,app能識別到一樣的裝置標識,那麼就可以判斷第一次開啟app的使用者,就是剛才瀏覽wap網頁的使用者,這樣服務就可以把剛才wap上操作的資料結果,通過網路下發給app,從而讓app實現,還原剛才wap的操作場景

方案流程

  • 使用者在wap網頁上產生了行為,產生了使用者個人資料
  • wap網頁收集了一種能夠唯一標識
    裝置的資訊,並且傳送給了伺服器
  • app安裝完畢後第一次執行,也去通過app嘗試收集唯一標識裝置的資訊,並且發給伺服器
  • 伺服器經過對比,發現app的唯一標識與wap網頁發上來的唯一標識能夠匹配
  • 伺服器判斷,是同一個人操作,於是下發使用者個人資料

縱觀整個流程發現,一切的核心,一切的關鍵,就是那個唯一標示

選取唯一標識

這個唯一標識要具備苛刻的條件,想找到其實很不容易

  • 選擇當做唯一標識的內容,必須能讓app獲取的到
  • 選擇當做唯一標識的內容,必須也能讓wap獲取的到
  • 選擇當做唯一標識的內容,還必須有能力區分出不同的裝置,如果選的唯一標識好幾個裝置取出來的都一樣,那麼就亂套了

那麼我們看看遵循這幾個條件,我們能選擇啥?

  • UDID,MAC地址啥的,別說wap了,app都不可能取到了
  • JS有好幾套,通過網頁渲染canvas的方案獲取螢幕"指紋",但這玩意app不可能拿到完全一致的東西,二者對不上,就沒任何意義
  • IDFA,IDFV,這玩意app是能取到了,但是wap拿不到啊

上面說的幾個都是相對來說,如果能雙方都拿到,是可以比較精準的進行裝置唯一標識的,但問題是,我們拿不到。。怎麼辦?

看看下面幾個資料

  • 裝置螢幕尺寸(iOS裝置如此的統一,一共就那麼幾個螢幕尺寸,重複的還不一堆一堆的)
  • 裝置作業系統(iOS系統碎片化如此的低,大部分幾乎都升到較高階的系統版本,重複的依然一堆一堆)
  • 裝置IP(IP這玩意會變啊,離開WIFI進入3G,經常變,並且IP這玩意在同一WIFI下也重複的一堆一堆的啊)
  • 訪問時間(時間這玩意更沒譜了,你們的使用者量越大,某一個確定的時間段內,發生第一次安裝,重複的就越多)
  • 還有更多類似的資料

發現沒有,上面的資料最大的特點就是,有一定的描述裝置體徵的資訊,但是如果只靠這一個描述資訊,那結果就是重複的太多太多,根本沒法確定一個唯一性。

但是,如果我們把這麼些描述資訊做成一個合集,同一時間內滿足所有的條件,那麼這個裝置重複的概率一下就縮減了太多太多。

舉例說明

舉個例子,到app安裝完畢第一次開啟的時候,所有訪問過wap的裝置資訊,把他們的資訊全都收集起來,找到同樣的螢幕尺寸,同樣的作業系統版本,同樣的IP地址,訪問時間相差不超過10min(暫定)的裝置,在如此多得限定條件下,我們近乎可以認定為,是具有唯一性的裝置,是同一個人

可以看到這裡面眾多的資訊一起去過濾,比較強的過濾條件就是IP,但因為IP存在頻繁變化,所以追加了時間條件,IP也可能因為WIFI路由器的原因導致,IP也存在重複和誤傷,這時候,又輔助了簡單的裝置資訊進行二次過濾。

這樣我們就找到了一個並不完美的唯一標識,有了這個唯一標識,就可以實現我們的跨瀏覽器和app的互通。

其實友盟的SDK就是這麼做的

友盟通過這個方法,知道了使用者是從哪個網頁看到的app下載的廣告,然後發生的去appstore下載並執行的行為,從而有效的能核算廣告的收益

a.通過對應用appstore URL進行封裝,獲取分渠道點選使用者的相關資訊,包括:時間、IP、裝置型別、作業系統版本;

b.通過在應用中整合程式碼,獲取初次開啟應用的使用者資訊,包括:時間、IP、裝置型別、作業系統版本;

c.實時對比不同渠道點選使用者和應用啟用使用者資訊,區分不同渠道帶來的啟用使用者;

d.此統計方式不用媒介提供統計資料,實時自動對比,會存在一定誤差,但可以基本衡量各渠道間及不同時期的渠道啟用轉化資料。

方案弊端

他有什麼弊端嗎?弊端還是挺明顯的,因為他是不完美的唯一標識,所以就存在著誤傷。

什麼是誤傷?使用者A瀏覽了WAP介面,使用者B恰巧用同一螢幕,同一作業系統版本,同一網段出口IP,在既定時間內,B使用者下載並運行了APP,這樣我們這套方案,會把B識別成A,等到A真的下載完APP後再來執行,資料可能已經失效了

這種誤傷是概率存在的,在現有的限定條件下,隨著app的使用者體量越來越大,這種誤傷將會越來越明顯。

iOSSafariCookie互通方案

方案簡介

接下來介紹另外一種方案,iOSSafariCookie互通,這種方案藉助的是iOS9系統新出的一個系統APISFSafariViewController,這個API是專為Safari設計的。所以這套方案有他的特點

  • 優點:精準,不會誤傷。
  • 缺點:只能通過safari,不能借助QQ,微信,手百等第三方app的瀏覽器

感謝SafariAutoLoginTest這個demo專案提供的思路

方案思路

詳細說一下思路,如果我們能在使用者訪問wap頁面的時候,通過網頁,網手機裡寫入一些使用者的行為和資料,比如使用者名稱,然後在app執行的時候去讀取這個資訊,那麼就自然能建立起,wap頁面訪問,和app下載安裝後第一次執行,二者之間的聯絡。但是想要做到這一點,談何容易。

大家都知道,iOS是有沙盒的,不同app之間,幾乎不可能跨越沙盒屏障來訪問資料,wap在瀏覽器裡可以寫資料進入cookie,儲存在手機上,這沒問題,但是app所在的cookie,和剛才的外部瀏覽器所在的cookie,分屬不同沙盒,完全就不是同一份cookie。我們在wap上寫cookie寫進的是safari的cookie,我們開啟自己app讀cookie讀得是自己app的cookie。

有什麼方法可以跨越沙盒傳遞資料?URL Scheme沒錯,通過OpenUrl的方式。如果我在wap頁面訪問,wap頁面發出來一個已經和我們的app約定好的URL Scheme跳轉,那麼就可以,喚起我們的app,並且伴隨著url,傳遞來資料。

如果使用者手機裡安裝了我們的app,使用者先去瀏覽wap頁面,wap頁面觸發了url跳轉,自動喚起了已經安裝的app,並且伴隨著url傳遞來了資料,一氣呵成,沒錯使用者很自然的從wap上的操作行為,延續到了app上。

問題來了,如果使用者沒裝app怎麼辦?難道讓使用者先瀏覽Wap站,產生了行為資料,被引導下載app,下載完後,重新回到wap站,重新再由wap站發起url跳轉?這體驗簡直渣到爆,簡直無法忍受

SFSafariViewController

iOS9以後,蘋果推出了SFSafariViewController這個全新的類,這個類的API允許在app內開啟一個safari瀏覽器,而不是一個app內部的webview。

這個app內safari和外面系統的safari是同一個,共享同一個沙盒,可以操作同一個cookie

剛才我們設想的操作流程,使用者體驗很差的流程

  • 使用者瀏覽wap站
  • 使用者引導下載app
  • 使用者回到wap站,跳轉app
  • wap通過openurl喚醒app傳遞資料

經過app內safari的處理,我們可以採用一些鬼點子,順著這個舊思路,把使用者體驗極差的第三部,第四部,給隱藏了,讓使用者無感知的靜默完成,這樣方案就完美了

  • 使用者通過safari瀏覽wap站,wap站寫使用者行為資料進入cookie
  • 使用者通過引導下載app,執行app
  • 第一次執行app,app內靜默的開啟一個純透明safari(讓使用者感覺不出來)
  • 純透明的safari訪問一個專門用來靜默取cookie得頁面
  • 純透明的safari訪問的取cookie的頁面,取到了正確的cookie資料,
  • 純透明的safari將資料通過openurl,靜默的回傳給app
  • app拿到瀏覽器資料後,銷燬無用的純透明safari

流程上看起來很複雜,但結果就是,使用者用系統safari,瀏覽了wap站,下載了app,app開啟後就自動能恢復到他瀏覽wap站的個人資訊了(或者其他資料)

VKSafariDomainBridge使用

上面的流程,如果用程式碼進行開發還算挺麻煩的,所以我封裝了一個工具,來輔助進行這一串靜默讓使用者無感知的操作

按理說整個流程應該分為2部分

  • wap頁面功能:
    • 使用者瀏覽wap頁的存cookie (wap地址1,使用者訪問用的)
    • 隱藏safari瀏覽的讀cookie頁面(wap地址2,靜默程式碼訪問用的)
    • 隱藏safari跳轉openurl功能
  • app內功能:
    • 開啟隱藏safari
    • 收聽openurl的回撥,處理資料
    • 關閉隱藏safari

鑑於實在是不會h5相關的開發,所以我封裝的工具就只包含app內的功能

//初始化 VKSafariDomainBridge
NSURL *url = [NSURL URLWithString:@"wap地址2,靜默程式碼訪問用的url"];
NSString *key = @"xxkey"
[VKSafariDomainBridge VKSetupSafariBridgeUrl:url AndKey:key];

url的地址就是wap地址2,靜默程式碼訪問用的
key作為協議識別關鍵字,隱藏safari發起的跳轉,通過這個key識別,才會走入VKSafariDomainBridge的處理邏輯,如果是其他正規渠道的openurl跳轉,key不匹配,便直接走正常邏輯,不會進行VKSafariDomainBridge處理

想要獲取wap使用者資料的時候

[[VKSafariDomainBridge VKSingleton]VKGetSafariInfo:^(BOOL success, NSString *info) {
        NSLog(@"%@ status = %@",info,@(success));
}];

通過回撥,如果成功success會返回YES,並且整個跳轉含有資料的url會被轉成string,通過block返回,如果失敗,則會返回NO

程式碼分析

這個功能需要通過appdelegate的openurl回撥來實現,既然是封裝工具,就要做到無侵入性,寫成category形式,只要使用者匯入工程,便可以一行程式碼不需要寫,自動生效。

application:openURL:options:這個方法,如果開發者沒有在工程中用到,我會自動新增,保證了openurl回撥可以正常工作。

如果開發者已經在工程中使用,已經有很多使用者自己的openurl協議要處理了,那麼我的category會生成一個新方法,交換掉老的系統函式(MethodSwizzling),先判斷url協議裡是否含有上面提到的專屬Key,含有則走我的處理邏輯,如果不含有,呼叫老函式,保證原專案功能無異常。

SEL origSelector = @selector(application:openURL:options:);
SEL newSelector = @selector(vkApplication:openURL:options:);

Method origMethod = class_getInstanceMethod(class,origSelector);

if (!origMethod) {
    SEL emptySelector = @selector(vkEmptyApplication:openURL:options:);
    Method emptyMethod = class_getInstanceMethod(class,emptySelector);
    IMP emptyImp = method_getImplementation(emptyMethod);
    class_addMethod(self, origSelector, emptyImp,
                    method_getTypeEncoding(emptyMethod));
}

origMethod = class_getInstanceMethod(class,origSelector);
Method newMethod = class_getInstanceMethod(class,newSelector);
if (origMethod && newMethod) {
    method_exchangeImplementations(origMethod, newMethod);
}

接下來就是開啟一個透明safari,等待來自網頁的openurl跳轉。製作透明safari的方法就是new出來後,alpha改為0,直接present。

-(void)VKGetSafariInfo:(VKSafariReturn)rtBlock
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) {
        if (rtBlock) {
            self.rtblock = rtBlock;

            SFSafariViewController *safari = [[SFSafariViewController alloc]initWithURL:self.safariUrl];
            safari.delegate = self;
            safari.modalPresentationStyle = UIModalPresentationOverCurrentContext;
            safari.view.alpha = 0.0f;
            self.safari = safari;

            UIViewController *currentVC = [self getCurrentVC];
            self.currentVC = currentVC;
            [currentVC presentViewController:safari animated:NO completion:nil];
        }
    }else
    {
        if (rtBlock) {
            rtBlock(NO,nil);
        }
    }

}

當透明safari載入完畢後,略微延遲後直接銷燬safari,如果在延遲期間,openurl返回則判斷,取cookie資料成功,回撥成功,如果超時,就判斷取cookie資料失敗,回撥失敗。

此處是SFSafariViewController的delegate回撥

-(void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully{
    __weak typeof(self) weakself = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeOut * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakself.currentVC dismissViewControllerAnimated:NO completion:^{
            weakself.safari = nil;
            weakself.currentVC = nil;
        }];
        [weakself VKTimeOut];
    });
}

openUrl的邏輯就不細說明了。

相關推薦

SFSafariViewControllerappwap的cookie連線

簡介 本文主要介紹,app跨域訪問app外部的瀏覽器的資料的方案,包括外部safari,或者QQ,微信,手百等外部app內的瀏覽器。 主要使用場景就是: 使用者在別的wap網頁上,產生了使用者行為,使用者資料,但是還沒下載app,當用戶下載app後,打算直接在app內延續之

Android開發FragmentAcitvity通信

對象 p s ets roi mit blog () open findview   上一篇我們講到與Fragment有關的經常使用函數,既然Fragment被稱為是“小Activity”。如今我們來講一下Fragment怎樣與Acitivity通信。

web app變革rem(手機屏幕實現全適配)

理想 那種 內嵌 自己的 大屏幕 block 行業 尺寸 是我 以往web移動適配,常規寫法是:media only screen @media only screen and (min-device-width: 320px){ //針對iP

Mac系統 + Python + Django開發一個釋出會系統Django模型(二) Mac系統 + Mysql安裝Mysql資料庫 Python + Mysql用pymysql庫連線Mysql資料庫並進行增刪改查操作

上一部分給大家介紹Django的檢視。 接下來繼續來了解Django框架,來看第二部分,此部分是對資料庫的操作。   目錄: 一、設計系統表 二、admin後臺管理 三、基本資料訪問(SQLite資料庫) 四、Django配置MySQL   &

C# 封裝、繼承多型

我們知道封裝、繼承和多型是面向物件方法設計中的三大基本特性,下面將具體講解這三個特性的具體表現及意義。 #一、封裝 ##1、說明   從字面意思上看,封裝就是打包的意思,將什麼包裝起來,專業一點就是資訊的隱藏,將物件的屬性和方法打包成一個相對獨立的單位,儘可能隱蔽物件的內部細

C# 語句結構陣列

#一、語句塊 在說語句結構和陣列之前咱們先來看看一些碎碎念——雜七雜八的語句塊 ##1、常量 常量 是程式碼中人為設定固定不變的一個元素值,聲明後可以直接使用在範圍內的程式碼當中。 常量的宣告 和變數宣告差不太多,只是在最前面加上const。 **例:**const int num =1

Tensorflowtf.app.run() tf.app.flags()

tf.app.flags tf.app.flags.DEFINE_XXX()用來定義引數項: import tensorflow as tf tf.app.flags.DEFINE_float(

Redis搭建叢集如何使用Jedis連線叢集

1.redis-cluster架構圖 架構細節: (1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬. (2)節點的fail是通過叢集中超過半數的節點檢測失效時才生效. (3)客戶端與redis節點直

Android開發wifi開關wifi連線(密碼連線

過放蕩不羈的生活,容易得像順水推舟,但是要結識良朋益友,卻難如登天。—— 巴爾扎克 本文demo來自網路,找了好久找到的,後面自己做了些許修改,這裡對原始碼解析,愧於忘記哪裡出來了,感謝作者! 接下來就記錄一下wifi開發的一些學習心得,這邊先看幾張效果圖吧!     

SLAMPoint Cloud Library(PCL)簡介安裝

PCL(Point Cloud Library)是在吸收了前人點雲相關研究基礎上建立起來的大型跨平臺開源C++程式設計庫,它實現了大量點雲相關的通用演算法和高效資料結構,涉及到點雲獲取、濾波、分割、配準、檢索、特徵提取、識別、追蹤、曲面重建、視覺化等。支援多種作業系統平臺,

轉載《JAVA模式》原型模式

在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述原型(Prototype)模式的:   原型模式屬於物件的建立模式。通過給出一個原型物件來指明所有建立的物件的型別,然後用複製這個原型物件的辦法創建出更多同類型的物件。這就是選型模式的用意。 原型模式的結構   原型模式要求物件實現一個可以“克隆”自身的介

Tensorflowtf.app.run()命令列引數解析

tf.app.run() 首先給出一段常見的程式碼: if __name__ == '__main__': tf.app.run() 找到Tensorflow中關於上述函式run()的原始

Javastatic靜態方法非static靜態方法區別

span get nbsp ati pub public ring spa 靜態方法 1、A.class:沒有static public class A { public String getText(){ } B.class調用A的方法

BZOJ4027[HEOI2015]兔子櫻花 貪心

註意 getchar des clas -s 裏的 多少 content 一個數 【BZOJ4027】[HEOI2015]兔子與櫻花 Description 很久很久之前,森林裏住著一群兔子。有一天,兔子們突然決定要去看櫻花。兔子們所在森林裏的櫻花樹很特殊。櫻花樹由

bzoj1283序列 線性規劃費用流

子序列 from emp sin href name clu html def 題目描述 給出一個長度為 的正整數序列Ci,求一個子序列,使得原序列中任意長度為 的子串中被選出的元素不超過K(K,M<=100) 個,並且選出的元素之和最大。 輸入 第1行三個數N

BioCode根據seq位點信息截取窗口

窗口大小 txt -s font == def n) image pau 代碼說明 sequence24371.txt 以上為所有氨基酸的編號,序列,與位點標記。根據標記為“1”的位點,截取窗口:如下(實驗結果): 圖示為一個窗口為12的蛋白質片段 2N+1=2

BZOJ4373算術天才⑨等差數列 線段樹+set

size true sam tput 組合 pre 無重復 second 希望 【BZOJ4373】算術天才⑨與等差數列 Description 算術天才⑨非常喜歡和等差數列玩耍。有一天,他給了你一個長度為n的序列,其中第i個數為a[i]。他想考考你,每次他會給出詢

rediscentos6.x安裝redis3.0.x

local con releases 新建 zxvf 分享 執行 .tar.gz all centos6.9_x86_64 1、下載redis安裝包 http://download.redis.io/releases/redis-3.2.9.tar.gz 2、解壓 tar

Swoole簡單安裝創建TCP服務器

客戶 編寫程序 lac nco 版本 size sock ear light pecl install swoole PHP的異步、並行、高性能網絡通信引擎,使用純C語言編寫,提供了php語言的異步多線程服務器,異步TCP/UDP網絡客戶端,異步MySQL

AMQJMS Mesage structure(JMS消息結構)

api 兼容 ctu 標識 提供商 nbsp 連接 特定 ext Δ消息體:JMS API 定義了5種消息格式也叫消息類型,可以使用不同形式發送和接收數據,並可以兼容現有的消息格式 TextMessage,MapMessage,ByteMessage,StreamMessa