AndroidのJavaでRetrofit2のログを取るには

AndroidのJavaでRetrofit2のログを取る方法。

build.gradleでは各ライブラリのバージョンを以下のように設定している。

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.2.1'
}

ログを取るには、HttpLoggingInterceptorを使用する。

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

取得するログのレベルを設定する。

interceptor.level(HttpLoggingInterceptor.Level.BODY);

設定できるログのレベルは以下の通り。

  • NONE ログを取らない
  • BASIC リクエストとレスポンス
  • HEADERS リクエストとレスポンスとそれぞれのヘッダー
  • BODY リクエストとレスポンスとそれぞれのヘッダーと本文

HttpLoggingInterceptorを設定したOkHttpClientを作成する。

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(interceptor)
    .build();

RetrofitにOkHttpClientを設定する。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(HOST)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

全体のコードは次のようになる。

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
    interceptor.level(HttpLoggingInterceptor.Level.BODY);
} else {
    interceptor.level(HttpLoggingInterceptor.Level.NONE);
}
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(interceptor)
    .build();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(HOST)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

MathJax version 3 について調べたこと。

MathJax version 3 について調べたこと。
追記する予定。

MathJaxとは

ブラウザーで数式を表示するJavaScriptライブラリ。

HTMLで表現できない数式をSVGで表示する。

使い方

HTMLファイルに次のタグを追加する。

<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

表記法

Tex/LaTeX表記、MathML表記、AsciiMath表記を使用できる。

文中に数式を埋め込むインライン方式では、\$\$…\$\$のように、\$と\$で数式を囲む。

他の行から分離して数式を表示するときは、\[…\]のように、\[と\]で数式を囲む。

デフォルトでは、\$…\$のインライン方式は使用できない。

TeX/LaTeX数式をJavaScriptの文字列で使用する時、JavaScriptが\を特殊と文字として扱うため、エスケープが必要になる。

var math = '\\frac{1}{\\sqrt{x^2 + 1}}';

設定

MathJaxのファイルを読み込む前に、グローバル変数MathJaxに設定を登録する。

MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\(', '\\)']]
  }
};
</script>
<script id="MathJax-script" async
  src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>

複数ページで同じ設定を使用している場合は、設定をファイルに保存すると便利。

// mathjax-cofig.js
window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\(', '\\)']]
  }
};

読み込み処理

<script src="mathjax-config.js" defer></script>
<script type="text/javascript" id="MathJax-script" defer
  src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js">
</script>

MathJax設定ファイルでMathJaxをロードする方法。

// load-mathjax.js
window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\(', '\\)']]
  },
};

(function () {
  var script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';
  script.async = true;
  document.head.appendChild(script);
})();

読み込み処理

<script src="load-mathjax.js" async></script>

コンポーネントの読み込み

ロードするコンポーネントをloaderセクションで配列で設定し、スタートアップコンポーネントをロードする。

MathJax = {
  loader: {
    load: ['input/tex-base', 'output/svg', 'ui/menu', '[tex]/require']
  },
  tex: {
    packages: ['base', 'require']
  }
};
</script>
<script type="text/javascript" id="MathJax-script" async
  src="https://cdn.jsdelivr.net/npm/mathjax@3.0.0/es5/startup.js">
</script>

起動時の処理

MathJax起動プロセスにフックして、追加の設定や処理を実行することができる。

ready()関数は、MathJaxがすべてのコンポーネントがロードされたときにMathJaxが読み出す関数。
オーバーライドして、初期化の前後に処理を実行したりできる。

pageReady()関数は、MathJaxの準備が整い、ページ自体の準備ができているときに実行される。

window.MathJax = {
  startup: {
    ready: () => {
      console.log('MathJax is loaded, but not yet initialized');
      MathJax.startup.defaultReady();
      console.log('MathJax is initialized, and the initial typeset is queued');
    }
  }
};

MathJaxの最初の組版が終わったあとに実行する

window.MathJax = {
  startup: {
    ready: () => {
      MathJax.startup.defaultReady();
      MathJax.startup.promise.then(() => {
        console.log('MathJax initial typesetting complete');
      });
    }
  }
};

動的な処理

MathJaxが読み込まれると、Webページの数式を処理する。
MathJaxが処理を実行したあとに、Webページに数式を追加してもMathJaxは自動的に処理しない。

ページに挿入された数式がある場合は、MathJaxに組版を指示する必要がある。
その方法には、MathJax.typeset()とMathJax.typesetPromise()がある。

MathJax.typeset()は組版を同期的に行う。呼び出しが終了すると組版は完了する。
MathJax.typesetPromise()は組版を非同期に行う。組版が完了すると解決されるPromiseを返す。

どちらの関数もオプションの引数を受け取る。
引数は、コンテンツを処理するDOM要素・CSSセレクタ文字列。
引数を指定すると、組版はそれらの要素に制限される。

typeset(() => {
  const math = document.querySelector('#math');
  math.innerHTML = '$$\\frac{a}{1-a^2}$$';
  return math;
});

自動採番の初期化

TeXは方程式に自動的に番号をつけることができる。
ページを変更すると、番号のついた方程式が追加・削除され、問題を引き起こす可能性がある。
次の関数で、方程式の番号をリセットできる。

MathJax.texReset([start])

数式の削除

数式を削除するときは、MathJaxに通知する必要がある。
MathJax.typesetClear()メソッドを使用する。

引数無しで呼び出した場合、MathJaxはすべての数式を忘れる。

ページから一部の数式を削除する場合、MathJax.typesetClear()の引数に要素の配列を渡す。

const node = document.getElementById('has-math');
MathJax.typesetClear([node]);

数式の文字列の変換

数式を含む文字列を別の形式に変換する。

MathJaxをロードするとロードした入力形式から出力形式に変換するメソッドが作成される。

例。MathML入力とSVG出力コンポーネントをロードした場合。

MathJax.mathml2svg(math[,options])
MathJax.mathml2svgPromise(math[,options])
MathJax.mathml2mml(math[,options])
MathJax.mathml2mmlPromise(math[,options])

オプション

  • display 数学が表示モードかどうかを指定するブール値(TeX入力の場合)。デフォルトはtrue。
  • em フォントのemのピクセル数。デフォルトは16
  • px フォントのexのピクセル数。デフォルトは8
  • containerWidth コンテナーの幅(ピクセル単位)。デフォルトはex値の80倍
  • lineWidth 改行幅をem単位で示す数値。デフォルトは非常に大きな数(100000)のため、実質的に改行されない。
  • scale 結果の変換に適用するスケーリング係数。デフォルトは1です。

let html = MathJax.tex2chtml('\\sqrt{x^2+1}', {em: 12, ex: 6, display: false});

SVGスタイルシートを取得する。

MathJax.svgStylesheet();

HTMLスタイルシートを取得する。
数式を組版したあとで呼び出すこと。

MathJax.chtmlStylesheet();

スタイルシートをリセットする。

MathJax.startup.output.clearCache();

TeXサポート

TeXとの違い

MathJaxは、TeX・LaTeXのmath-modeマクロのみサポートする。
text-modeマクロはサポートしない。
例外として、\refマクロと、math-mode内にテキストを追加するいくつかのマクロをサポートする。

区切り文字

  • インライン数式モード
    • \(…\)
  • ディスプレイモード
    • \[…\]
    • $$…$$

デフォルトでは、$…$をインラインの区切り文字として認識しない。
有効にするには、明示的に設定する。

window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\(', '\\)']]
  }
};

HTML特殊文字

<および>がブラウザーにHTMLタグとして解釈されないように注意する必要がある。
例えば、<と>の前後にスペースを配置する。

... when $x < y$ we have ...

\lt と \gt マクロが定義されている。

... when $x \lt y$ we have ...

TeX・LaTeX拡張

使用頻度の低いコマンドの中には、拡張機能で定義されているものがある。
例えば、\colorマクロはcolor拡張機能で実装されている。

コンテキストメニュー

ui/menuコンポーネントは、コンテキストメニューを実装する。

コンテキストメニューを無効にするには、enableMenuオプションをfalseにする。

MathJax = {
  options: {
    enableMenu: false,
  }
}

iOSアプリのWKWebViewからdeep linkがタップされたときに処理を行う

概要

WKWebViewを使ったiOSアプリがある。
WKWebViewの中でdeep linkがタップされたとき、アプリ側で処理を行いたい。
deep linkのタップを知るにはどうすればいいか。

環境

  • Xcode version 11.6
  • Objective-C

WKNavigationDelegate

WKNavigationDelegateを使うと、WKWebViewの画面遷移をフックして処理を行うことができる。

- (void)setupWKWebView
{
    WKWebViewConfiguration* webConfig = [[WKWebViewConfiguration alloc] init];
    WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:webConfig];
    [self.view addSubview:webView];
    // 画面遷移をフックする
    webView.navigationDelegate = self;
    NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:kUrl]];
    [webView loadRequest:request];
}

WKNavigationDelegate

WKNavigationDelegatewebView:decidePolicyForNavigationAction:decisionHandler:は、画面遷移を行うかどうかを決めることができる。

画面遷移の前にリンクのURLを確認して、deep linkなら画面遷移を中止して特別な処理を行う。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSLog(@"URL    = %@", navigationAction.request.URL.absoluteString);
    NSLog(@"scheme = %@", navigationAction.request.URL.scheme);
    NSURLComponents* components = [[NSURLComponents alloc] initWithURL:navigationAction.request.URL resolvingAgainstBaseURL:YES];
    NSLog(@"query  = %@", components.queryItems);

    if ([navigationAction.request.URL.scheme.lowercaseString isEqualToString:@"my-app"]) {
        // my-app://〜 のときは画面遷移をキャンセルして特別な処理を行う
        decisionHandler(WKNavigationActionPolicyCancel);
        // 特別な処理
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

WKWebViewのユーザーエージェントを変更する

  • 環境
    • Xcode Version 11.6
  • 言語
    • Objective-C

WKWebViewのユーザーエージェントの初期値を変更する

WKWebViewのユーザーエージェントの初期値を指定した名前に置き換える方法です。

#import <WebKit/WebKit.h>

// アプリのバージョン番号を取得
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// 設定するユーザーエージェントの名前
NSString* userAgent = [NSString stringWithFormat:@"MyApp Version=%@", version];
// ユーザーエージェントの初期値を変更する
NSDictionary* registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:userAgent, @"UserAgent", nil];
[NSUserDefaults.standardUserDefaults registerDefaults: registrationDictionary];

WKWebViewConfiguration* webConfig = [[WKWebViewConfiguration alloc] init];
WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:webConfig];

ユーザーエージェントは次のようになりました。

MyApp Version=1.0

WKWebViewのユーザーエージェントを変更する

WKWebViewのユーザーエージェントに指定した名前に置き換える方法です。

WKWebViewのcustomUserAgentにユーザーエージェントを設定します。

// アプリのバージョン番号を取得
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// 設定するユーザーエージェントの名前
NSString* userAgent = [NSString stringWithFormat:@"MyApp Version=%@", version];
// WKWebViewを作成する
WKWebViewConfiguration* webConfig = [[WKWebViewConfiguration alloc] init];
WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:webConfig];
// ユーザーエージェントを変更する
webView.customUserAgent = userAgent;

ユーザーエージェントは次のようになりました。

MyApp Version=1.0

customUserAgentで設定したユーザーエージェントは、NSUserDefaultsで設定したユーザーエージェントよりも優先されます。

WKWebViewのユーザーエージェントに追記する

WKWebViewのユーザーエージェントに指定した名前を追記する方法です。

WKWebViewConfigurationのapplicationNameForUserAgentに追記する名前を設定します。

// アプリのバージョン番号を取得
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// 追記するユーザーエージェントの名前
NSString* userAgent = [NSString stringWithFormat:@"MyApp Version=%@", version];
// WKWebViewを作成する
WKWebViewConfiguration* webConfig = [[WKWebViewConfiguration alloc] init];
webConfig.applicationNameForUserAgent = userAgent;
WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:webConfig];

ユーザーエージェントは次のようになりました。

Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) MyApp Version=1.0

NSUserDefaultsやcustomUserAgentでユーザーエージェントを設定している場合、applicationNameForUserAgentの設定は適用されません。