mizdra's blog

ぽよぐらみんぐ

ゆかりスロットをWeb Share APIに対応させた

iOSのSafariにWeb Share APIが来るという話を聞いたので, 趣味で作っている「ゆかりスロット」で試してみることにした. 2月2日時点でまだiOS 12.2はリリースされていないので, Stableな環境で動かしたいならChrome 61+がインストールされたAndroid端末が必要.

developers.google.com

yukari-slot.mizdra.net

www.mizdra.net

実装の詳細

navigator.sharetitle, url, text の3つのoptionalなパラメータを渡して呼び出すと, 共有可能なアプリ一覧が載ったOSネイティブのあのシートが画面下から生えてくる. それぞれのパラメータの対応状況は共有先のアプリに依存していて, 処理されたり無視されたりする. 自分が試した限りではSlack for Androidは3つとも見てくれたけど, Twitter for Androidは texturl しか見てくれなかった.

function shareResult(leftEye: number, rightEye: number) {
  const text =
    leftEye === 1 && rightEye === 1
      ? 'ゆかりちゃん完成!!!'
      : 'ゆかりスロット失敗 😥'
  const url = `https://yukari-slot.mizdra.net/share/${leftEye}${rightEye}`

  navigator
    .share({
      title: 'ゆかりスロット',
      text,
      url,
    })
    .then(() => console.log('shared!'))
    .catch((e) => console.error(e))
}

navigator.share に渡す引数が不正の場合は TypeError , ユーザアクション契機による呼び出しでなければ NotAllowedError , シェア先のアプリが無い場合やユーザがシェアをキャンセルした場合は AbortError が投げられる. ゆかりスロットでは navigator.share がない環境やシェアに失敗した場合はは Twitter Web Intentでシェアするようfallbackしている.

function createShareData(leftEye: number, rightEye: number) {
  const text =
    leftEye === 1 && rightEye === 1
      ? 'ゆかりちゃん完成!!!'
      : 'ゆかりスロット失敗 😥'
  const url = `https://yukari-slot.mizdra.net/share/${leftEye}${rightEye}`
  return { text, url }
}

function createTweetLink(text: string, url: string) {
  const encodedText = encodeURIComponent(text)
  const encodedHashtags = encodeURIComponent('ゆかりスロット')
  const encodedUrl = encodeURIComponent(url)
  // Twitter Web Intentの場合は hashtags パラメータでハッシュタグを設定する
  return `https://twitter.com/intent/tweet?text=${encodedText}&hashtags=${encodedHashtags}&url=${encodedUrl}`
}

async function share (
  leftEye: number | undefined,
  rightEye: number | undefined,
) {
  const { text, url } = createShareData(leftEye, rightEye)

  try {
    await navigator
      // ハッシュタグを付加して共有
      .share({ text: `${text} #ゆかりスロット`, url })
  } catch (e) {
    // AbortError はユーザがシェアをキャンセルした場合の
    // エラーなので無視する
    if (e.name === 'AbortError') return

    // navigator.share がない環境やシェアに失敗した場合は
    // Twitter Web Intentにfallbackする
    window.open(createTweetLink(text, url))
  }
}

今の所雑に text パラメータにハッシュタグを含めているのでSlackにシェアするとチャンネルへのリンクになって残念なことになっている. 真面目に実装するならTwitterにシェアするボタンとWeb Share APIを呼び出すボタンは別々にしてユーザが選択できるようにすると良さそう.

f:id:mizdra:20190202020352p:plain:w300
Slackにシェアした様子. ハッシュタグが上手く機能していないことが分かる.

ローカルでデバッグする

Web Share APIは (現状Stableなバージョンでは) Chrome for Androidでしか動かないため, Android端末を用いてデバッグすることになる. またAPIはhttpsなホスト or localhost にのみ提供されており, Web Share APIに対応しているブラウザであってもhttpなホスト上で navigator.share を参照すると undefined が返ってくる. つまり, APIをローカルでデバッグするには開発マシン上の開発サーバに対して, Android端末から localhost ホストでアクセスする必要がある.

これはChrome Dev ToolsのPort Fowarding機能を使うと簡単に実現できる. Remote devices タブの Settings から以下のように開発マシンのportとモバイルデバイスのportのマッピングを指定できる.

f:id:mizdra:20190202020023p:plain
開発マシンの localhost:8080 とAndroid端末の localhost:8080 をマッピングしている様子

developers.google.com