IOS未安装APP获取Safari浏览器数据

本篇文章的目的就是要达成未安装的app在安装app之后,去获取安装app之前Safari所浏览的数据,比如说用户在未安装手机百度时,已经在Safari浏览器中登录了百度帐号,当安装手机百度之后,打开该APP可以自动登录这个Safari浏览器中所登录的百度帐号。

当然也可以达成那种效果,比如打开某个带有邀请码的网页推荐用户去下载app,当通过appstore下载完app之后,打开该app,可以知道该邀请码是多少,网页和下载app之间隔了一层appstore,不能从网页直接传值到app,所以如果未安装该app的话,就需要网页和app去读取一个相同的值去标记,但是对于ios系统来说,idfa等唯一标识符只能app获取,网页获取不到,所以一般无法通信

空白.png

比如上面这个图,A用户从web网页跳转到appstore,再从appstore下载app,但是这个过程中,也许用户A、B、C都在下载该软件,web网页是无法直接像app传送任何数据的,所以就需要确认到底哪个是从web网页推荐过来下载的,哪些是自己去appstore下载的。

一、判断唯一用户

1、通过idfa等标识判断

idfa等唯一标识只有oc能获取,但是网页一般获取不到ios的idfa,uuid等硬件的唯一标识符(除非使用苹果配置文件,这个用户需要安装,无法做到无痕,而且使用起来比较麻烦,有兴趣的可以去看看)

2、通过多重可获取的信息综合判断

由于无法准确的获得idfa等信息,所以可以多记录几个网页和app都能获取的数据,比如网络模式、IP、时间、机型、位置等,通过多重判断来看是不是同一个人,但是这种方式是存在误差的,比如一个公司用的同一个网络同一个机型的两个人就会判断有错误。

3、通过cookies判断

之前是不可以的,因为ios应用是沙盒运行,app之间创建的webview之间的cookies都是沙盒状态的,但是ios9之后,终于增加了一个全新的类SFSafariViewController,这个相当于在app内部创建了一个safari浏览器,用的和safari浏览器共同的cookies,所以可以用SFSafariViewController来获取cookies

二、使用SFSafariViewController

这个SFSafariViewController很简单,导入头文件#import <SafariServices/SafariServices.h>就可以像其他普通的viewcontroller一样创建

self.safariView = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:@"http://adapp.jidonggame.com/"]];
self.safariView.delegate=self;
[self presentViewController:self.safariView animated:false completion:nil]

SFSafariViewController一般使用这两个代理函数

//点击done按钮时调用
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller
{
    NSLog(@"%s",__func__);
}
//加载完成之后调用
- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully
{
    NSLog(@"%d",didLoadSuccessfully);
}

使用之后就可以打开指定的url,并且使用的是safari的cookies,打开的页面样子像下面这样

Simulator Screen Shot 2016年10月14日 下午3.41.33.png

下面的工具栏在代理中也可以设置,但是一般不自己再设置了

三、网页和app共用cookies

使用SFSafariViewController访问域名之后,用的就是用safari打开的页面的cookies,比如我在手机的safari页面打开的网址是:www.hudongdong.com/uid/20,产生了一个cookies:20,然后我在app中打开网址www.hudongdong.com的时候,就可以用到同一个cookies:20,,而别人如果没有用safari页面事先打开这个网址的话,cookies:20是不会有的,这样就知道是我打开的,然后我在app中给服务器单独传值即可。

现在cookies虽然知道了,但是还有一点就是在SFSafariViewController中是获取不到cookies的,因为这个cookies存在safari系统中,那么怎么得到对应的值呢,那就需要服务端做一个对于你软件的scheme跳转了。

因为当你访问网页的时候,网页可以获取到你的cookies,你无需手动获得cookies,需要做的就是使用这个cookies去登录app中需要打开的那个网站,而在网页返回数据中调用你软件的scheme,同时在调用的时候把定义的cookies或者其他值传进来,比如我软件的scheme是comjdnetkuaifa,那么可以通过js调用:location.href = "comjdnetkuaifa://uid/" + uid;,这样就可以把用户的uid标识直接传过来了,然后在app中的代理方法

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options NS_AVAILABLE_IOS(9_0)

获取scheme传过来的值,比如像我们做的一样把uid传过来。

屏幕快照 2016-10-14 下午3.28.16.png

cookies的作用仅仅是登录同一个网站让网页端去判断之前是哪个用户登录的,然后网页返回对应的需要的值即可。

四、细节优化

因为要做到无痕,就是在用户不知不觉间登录,所以需要调整SFSafariViewController的样式,因为如果使用presentViewController的方案,在viewcontroller之间跳转的话,肯定会影响用户的体验,而网上流传的方案

 safari.view.alpha = 0.0;
 safari.view.hidden = true;

设置透明度为透明和隐藏的话,苹果现在官方是不允许的

SafariViewContoller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SafariViewController to track users without their knowledge and consent.

并且现在透明度最低设置为0.05,所以这个方案是会被拒的。

网上提供了一种思路,可以考虑吧safariView加到现在的Viewcontroller中,

 [self addChildViewController:self.safariView];
 [self.view addSubview:self.safariView.view];

当使用完毕之后,再移除即可

[self.safariView.view removeFromSuperview];
[self.safariView removeFromParentViewController];
self.safariView = nil;

但在实际运行中,发现如果这样处理的话的确隐藏了,但是却并没有调用访问。

所以就用了一种解决方案是使用presentViewController切换界面

[self presentViewController:self.safariView animated:false completion:nil];

在初始化完成的时候,切换过来即可

- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully
{
    NSLog(@"didLoadSuccessfully:%d",didLoadSuccessfully);
    if (didLoadSuccessfully) {
        [controller dismissViewControllerAnimated:true completion:nil];
    }
}

五、其他获取cookies的方法

如果使用webview的话,可以在webview的代理函数中,去获取cookies。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
   
    NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:[NSURL URLWithString:@"http://adapp.jidonggame.com/"]];
    NSEnumerator *enumerator = [cookies objectEnumerator];
    NSHTTPCookie *cookie;
    while (cookie = [enumerator nextObject]) {
        NSLog(@"COOKIE{name: %@, value: %@}", [cookie name], [cookie value]);
        [sharedHTTPCookieStorage deleteCookie:cookie];
    }
}

但是这个cookies是沙盒中的cookies,就是软件自己的cookies,不是safari的cookies

六、demo下载

Github下载地址:https://github.com/DamonHu/cookiessssss

GitOSC下载地址:http://git.oschina.net/DamonHoo/cookiessssss

当然还有另外一个别人写好可以测试网页的demo

github下载地址:https://github.com/mackuba/SafariAutoLoginTest

七、参考文章

iOS app与浏览器 跨域互通

iOS10 SFSafariViewController not working when alpha is set to 0

如何获取一个uiwebview中的请求的cookie

iOS9-WKWebView+SFSafariViewController

Last modification:January 1st, 1970 at 08:00 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

6 comments

  1. JDD

    你好!

        我在下载了你的demo运行之后,发现没有走appdelegate的openURL回调,看到之后,希望能回复我,帮我解疑,万分感谢!
    1. 东东
      @JDD

      这个回调是服务器返回的数据发起的回调,需要服务器返回的数据通过调用scheme,接着回调openurl,现在那个demo中的网址链接返回的数据已经变了

      1. JDD
        @东东

        文章中提到软件的scheme是comjdnetkuaifa,这个软件的scheme是怎么定义的啊?还是和服务器约定好就可以的?

        1. 东东
          @JDD

          Sheme的作用你知道吧,就是通过这个调用软件的,这个是前后端约定好的,不能和其他软件重复了,你定义好在项目的info里面设置好sheme 的标识,让服务端调就可以了

          1. JDD
            @东东

            那服务器应该怎么调用呢?第一次接触,有点愚钝啊,烦请指教。

            1. 东东
              @JDD

              服务端通过返回js数据调用,你可以百度下js调用scheme,就类似于打开网页那样

Leave a Comment