SFSafariViewController はトラッキングの夢を見るか

# はじめに

なんか某そういう系の仕事で、

SFSafariViewController を利用することで
Safari の Cookie 情報をアプリでも共有できるんだって！
夢がひろがりんぐ！調べてちょ！（意訳）

と依頼されたので、調べてみたメモ。

# やりたいこと
 + アプリと Safari で Cookie を__同期__したい

# 結論
__できなかった！__
ということで、つらつらと言い訳していきます。

# カスタムは許されない
いろんな先人が言ってるけど、__SFSafariViewController はカスタムできない。__
厳密に言うとできるんだけど、公開されているメソッドが
すんごく限定されているから、カスタムするにしてもやれることがない。

SFSafariViewController が公開しているメソッドは、
表示対象のURLを指定して、初期化するメソッド。__ただ、それだけである。__
  ※ ViewCotroller 的なメソッドはある程度利用できます。「固有のメソッドは」って話です。

動的に組んだ HTML を表示なんて、__どうあがいてもできません。__
こいつの機能は、__「指定された URL にアクセスしてウェブページを表示する」だけ__です。

ちなみに、ContainerView を利用して、大きさをコントロールしようとも試みたが、
指定した Frame でチラッと表示されたと思ったら、すぐに真っ白になって表示されなくなった。
デリゲートも呼ばれないし、エラーログも出てこなかった。

なんかこう、もうちょっとできないような強制はできなかったのかな…。

# Cookie の共有は一方通行
前項の時点で「（この仕事での利用は）__無理かな！__」
ってなったんだけど、さらに立ちふさがってきたのがコレ。
__SFSafariViewController → Safari の一方向でしか Cookie の共有ができない。__ふぁー！！（錯乱）
以下、Google ログインで試した組み合わせ。

| 手順 | 期待値 | 結果 |
|:-----|:-----|:-----|
| SFSafariViewController からログイン → Safari でログイン後のページを表示 | ログイン状態で表示される | OK |
| Safari からログイン → SFSafariViewController でログイン後のページを表示 | ログイン状態で表示される | NG |

__ふぁー！！（錯乱、6行ぶり2度目）__

なんかできたって記事も見たんだけど、Safari からは共有されなかったよ…(´；ω；｀)ﾌﾞﾜｯ
でも、クラスリファレンスをみると、__「sync」じゃなくて「share」って表記されてんだよね。__
つまり、この認識は正しい可能性が微粒子レベルで存在してるんじゃないかと思いたい。

# SFSafariViewController 経由で起動しないと共有しない
前項で「SFSafariViewController → Safari はできる」って話をしたんだけど、
正確には、「特定の手順を踏めば」が頭につく。

SFSafariViewController でウェブページを表示すると、下のバーに Safari のアイコンが出てくる。
__このアイコン経由でしか共有してくれないのである。__

「SFSafariViewController でログインしてー、アプリをバックグラウンドにまわしてー、Safari 起動！」
とかやっても、共有されない。
iOS 弱者の私は、これで小一時間悩んだ。__笑えよ。__

# できる Cookie とできない Cookie がいる
Google ログインに関しては、そんな感じでできたんだけど、
未だに理解できないのは、__自作したテスト用の Cookie は共有できなかった__こと。
これはホントに今でも解決策がわからんのです…。
ちゃんとブラウザだと Cookie 保存・読み込みできるのにぃー( ﾟ皿ﾟ)ｷｰｯ!!

Safari の Web インスペクタで確認しようとしたんだけど、
__SFSafariViewController で表示している内容は、対象外__みたいで、追うこともできず…。
どうやったら調べられるのか、目下検索中です。まる。

# 余談（使い方）
そういえば、コード書いてなかったなと思って。
使い方は、こんな感じです。
※ SafariServices.frameworkの追加が必要です。

ViewController.h

```objc
#import <UIKit/UIKit.h>

@import SafariServices;

@interface ViewController : UIViewController

@end
```

ViewController.m

```objc
#import "ViewController.h"

@interface ViewController () <SFSafariViewControllerDelegate>

/// 2度表示防止用のフラグ
@property BOOL isShowed;

@end

@implementation ViewController

- (void)viewDidAppear:(BOOL)animated {
    if (self.isShowed) {
        return;
    }
    
    // SFSafariViewController で Apple のページを表示
    SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:[self getURL]];
    [safariViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve]; // 遷移のアニメーションを変える
    [safariViewController setDelegate:self];
    
    [self presentViewController:safariViewController
                       animated:YES
                     completion:nil];
}


/**
 * 表示対象のURLを取得
 *
 * @return 表示させたいウェブページのURL
 */
- (NSURL *)getURL {
    return [NSURL URLWithString:@"http://www.apple.com/jp/"];
}

// --------------------------------------
//     SFSafariViewControllerDelegate
// --------------------------------------

// アクションボタン（下にあるボタン群）が押されたときに呼ばれる
- (NSArray<UIActivity *> *)safariViewController:(SFSafariViewController *)controller
                            activityItemsForURL:(NSURL *)URL
                                          title:(NSString *)title {
    NSLog(@"--- activityItemsForURL ---");
    return @[];
}

// 指定したページのロード完了したら呼ばれる
// （内部で遷移したとしても、最初のページのロード完了時しか呼ばれない）
- (void)safariViewController:(SFSafariViewController *)controller
      didCompleteInitialLoad:(BOOL)didLoadSuccessfully {
    NSLog(@"--- didCompleteInitialLoad ---");
}

// DONEをタッチしたら呼ばれる（閉じたら呼ばれる）
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller {
    NSLog(@"--- safariViewControllerDidFinish ---");
    
    // 閉じると viewDidAppear が実行されるので、処理スキップフラグをON
    self.isShowed = YES;
}

@end
```

viewDidLoad で処理すると、

```text
whose view is not in the window hierarchy!
```

って怒られるので、注意。

ちなみに、余計な装飾をとっぱらって表示させる「リーダーモード」を有効化させたい場合は、

```objc
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:[self getURL]];
```

を

```objc
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:[self getURL] entersReaderIfAvailable:YES];
```

にすると、対象のページが対応していた場合に切り替えてくれます。

# まとめ
 + 同期はできないけど、一方向の共有ができるよ！
 + でも、コントローラー経由じゃないと共有できないよ！
 + 心が折れたよ！

# 参考
 + SFSafariViewController Class Reference  
   https://developer.apple.com/library/prerelease/ios/documentation/SafariServices/Reference/SFSafariViewController_Ref/index.html
 + 今更ながらSFSafariViewControllerについてまとめてみた  
   http://nlogic.jp/?p=446
 + iOS 9 (XCode7) でできること、できないことまとめ  
   http://qiita.com/herablog/items/3ad682d011612bfcd814#webview%E3%81%A7safari%E3%81%A8cookie%E3%81%8C%E5%85%B1%E6%9C%89%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B
 + スマホブラウザでのセッションクッキーの有効期限  
   http://lab.informarc.co.jp/javascript/smartphone_session_cookie.html