Xamarin.FormsでURLにBasic認証を組み込んでWebViewを表示した。

#概要
Xamarin.Forms使ってiOSアプリ開発していた時、アプリ内でBasic認証付きのページをWebViewで表示することになった。
半日探して情報がなかった為忘備録的に投稿。

###まず普通にForms側でWebViewを作る。
```TestView.cs
using Xamarin.Forms;

namespace SumpleApp
{
    public class TestView : ContentPage    
    {
        public TestView()
        {
            WebView view = new WebView();
            view.Source = "https://www.google.com";
            this.Content = view;
        }
    }
}
```
これは普通に表示される。
※iOSの場合は”http://”だと弾かれる。これはアプリ側ではどうすることもできない。

###ということで次はBasic認証が設定されたページにアクセスしたい。
しかも、URLに直接UserとPass乗せて手入力作業がないようにしたい。
URLに仕込んだら自動的に入力されて認証通るってどこかで読んだし！楽したいし！

```TestView.cs
using Xamarin.Forms;

namespace SumpleApp
{
    public class TestView : ContentPage    
    {
        public TestView()
        {
            WebView view = new WebView();
            //Basic認証情報が
            //User : user
            //Password : pass　の場合
            view.Source = "https://user:pass@test.testpage.jp";
            this.Content = view;
        }
    }
}
```
##oh...真っ白だぜ
テスト用のアドレスなのでどこにも繋がりませんが、実際は画面真っ白でした。
おいおい、どういうことなんだぜ？いっそクラッシュしてくれた方がまだ気持ちがいい。
ログを確認すると↓

>2018-10-25 16:03:39.927674+0900 testproject.iOS[17153:12585805] CredStore - performQuery - Error copying matching creds.  Error=-25300, query={
    class = inet;
    "m_Limit" = "m_LimitAll";
    ptcl = htps;
    "r_Attributes" = 1;
    sdmn = "Please Enter your ID & Password";
    srvr = “test.testpage.jp”;
    sync = syna;
}

見覚えのないエラーが吐かれていました。
>    sdmn = "Please Enter your ID & Password";

どうやらURLに仕込んだはずのUserIDとPasswordを認識してくれないようだ。
ざっと調べるとiOS11あたりから出てくるようになったエラーだとか。知らんし。
動作としてはForms側のWebViewのSourceに指定した文字列を内部的にNSUrlに変換してネイティブのブラウザを呼び出して表示してるのだと思う。
しかしどうやらこのネイティブ変換が問題らしい。

##iOS固有の問題だというのであればXamarin.iOSで対応してやればいいんじゃろ？
みんな大好きカスタムレンダラー。でもよかったのですが。
Effectで解決できました。※Effectの使い方は検索するといいと思うよ。
###iOS側に以下のファイルを作成

```ExWebViewEffect.cs
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using SumpleApp.iOS;

[assembly: ResolutionGroupName ("YourCompany")]
[assembly: ExportEffect(typeof(ExWebViewEffect), "ExWebViewEffect")]
namespace SumpleApp.iOS
{
    public class ExWebViewEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            UIWebView view = this.Control as UIWebView;
            if (view == null) { return; }
            string user = "user";
            string pass = "pass";
            var path = "https://test.testpage.jp";
            path = path.Replace("https://", "https://" + user + ":" + pass + "@");
            view.LoadRequest(NSUrlRequest.FromUrl(NSUrl.FromString(path)));
        }
        protected override void OnDetached()
        {
        }
    }
}
```
###こいつをForms側のWebViewのEffectにAddするんじゃあ！

```TestView.cs
using Xamarin.Forms;

namespace SumpleApp
{
    public class TestView : ContentPage    
    {
        public TestView()
        {
            WebView view = new WebView();
            view.Effects.Add(Effect.Resolve("YourCompany.ExWebViewEffect"));
            this.Content = view;
        }
    }
}
```
UIWebViewのLoadRequestメソッドを利用しています。
このメソッドはただ普通に指定したページを読み込む処理ですが、NSUrlRequest.FromUrl()でBasic認証情報を仕込んだURLを作成して渡しています。
全く同じ文字列をForms側のWebViewのSoruceに渡してるのにうんともすんとも言わないのはおそらくエンコードとかそこらへんの問題なんじゃないかなーと思っております。
とりあえずこれで手入力せずBasic認証を通過出来ました。
URLやら認証情報やらを動的に変更したいのであればバインドか何かで値の変更を通知してOnElementPropertyChangedで拾ってあげるといいんじゃないかなー。