Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Unlocking new capabilities for PWA

Avatar for Takepepe Takepepe
February 01, 2020

Unlocking new capabilities for PWA

Avatar for Takepepe

Takepepe

February 01, 2020
Tweet

More Decks by Takepepe

Other Decks in Technology

Transcript

  1. About Me ▪ Takefumi Yoshii / @Takepepe ▪ DeNA /

    DeSC Healthcare ▪ Frontend Developer ▪ TypeScript Meetup JP member 2
  2. Agenda ▪ 1. PWA や Web App で利甚可胜な、実隓的機胜に぀いお ▪ 2.

    ずある実隓的機胜 を利甚しおアプリを䜜った話 ▪ 3. ずある実隓的機胜 の API 抂芁 ▪ 4. API 取り扱いポむント・React Redux に統合する話 3
  3. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features その快適なレスポンスから、

    Web アプリケヌション開発技術で、 Native アプリず同等機胜を提䟛するこずに、 倚くの期埅が寄せられおいたす。
  4. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features しかし、Web

    ブラりザに蚱容されおいる 機胜は限定的です。端末固有の機胜に アクセスする Low-Level API の倚くは、 JavaScript には解攟されおいたせん。
  5. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features その状況を前進するべく、

    Google Chrome では Low-Level API ぞの橋枡しを 積極的に行なっおいたす。 Experimental Features実隓的機胜 には、そんな機胜が倚数控えおいたす。
  6. 1-2. Origin Trial 1. Web Experimental Features chrome://flags で実隓的機胜を有効にすれば、 その機胜を詊すこずができたす。

    そしお、実隓的機胜を期限付き・ドメむン制限で 提䟛する「Origin Trial」もありたす。 https://developers.chrome.com/origintrials
  7. 1-2. Origin Trial 1. Web Experimental Features 昚幎 5月に開催された「Google I/O」

    昚幎 11月に開催された「Chrome Dev Summit」で、 この「Origin Trial」に぀いお玹介がありたした。 可胜性の広がりに、期埅が高たりたすね
  8. 2-1. Web NFC Media MEMO デモアプリは配信もしおいたすので、 NFC 機胜付き Android をお持ちの方は

    詊しおいただけるず嬉しいです。 github にコヌドも公開しおいたす。 https://webnfc-media-memo.netlify.com/ https://github.com/takefumi-yoshii/webnfc-media-memo 2. Devlop Trial
  9. 2-1. Web NFC Media MEMO このアプリを起動しおいる時、 端末で怜出された NFC Tag の凊理は

    フォアグラりンドのブラりザに委ねられたす。 2. Devlop Trial
  10. 2-1. Web NFC Media MEMO 2. Devlop Trial テキストメモ機胜を利甚しお NFC

    Tag に曞き蟌んだテキストは、 NFC Tag に氞続化されたす。
  11. 2-1. Web NFC Media MEMO 2. Devlop Trial 音声メモ機胜は、 録音した埌に

    NFC Tag にタッチするず、 保存するこずができたす。
  12. 2-2. Prepare 2. Devlop Trial 䞀番早く API を詊すこずが出来る方法を玹介したす。 NFC 機胜が搭茉された

    Android端末に、 Google Chrome Canary をむンストヌル。 chrome://flags を URL バヌに入力し、 Web-NFC を有効にしたす。 端末の NFC機胜を有効にするこずも忘れずに
  13. 2-2. Prepare 2. Devlop Trial localhost で挙動を確認するため、 この Android 端末を

    PC に接続したす。 PC の Google Chrome を起動し、 More tools > Remote devices から、 接続しおいる該圓端末を遞択。
  14. 2-2. Prepare 2. Devlop Trial Port forwarding に、開発サヌバヌの localhost を指定。

    モバむル端末の Developer Console にアクセスしたす。
  15. 3-1. About NFC Tag Origin Trials の description を匕甚したす。 Low-level

    I/O operation などは、察応未定ずのこず。 3. API Overview Low-level I/O operations (e.g. ISO-DEP, NFC-A/B, NFC-F) and Host-based Card Emulation (HCE) are not supported within the current scope.
  16. 3-1. About NFC Tag NPX 瀟の「NTAG 215 チップNFC-A 」 が採甚された

    NFC Tag は倚く流通しおおり、 安䟡に入手するこずができたす。 3. API Overview 䟋カヌドタむプ / Â¥600 / 12枚、シヌルタむプ / Â¥790 / 11枚
  17. 3-1. About NFC Tag 圢だけでなく、容量も様々なものがありたす。 そんなに倧容量ではありたせん。 NTAG 213: 144Byte NTAG

    215: 504Byte NTAG 216: 888Byte 3. API Overview 䟋カヌドタむプ / Â¥600 / 12枚、シヌルタむプ / Â¥790 / 11枚
  18. 3-2. NDEFReader 簡単な読み蟌み䟋を確認しおいきたしょう。 3. API Overview const reader = new

    NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  19. NDEFReader が NFC 読み蟌みに必芁なむンスタンスです。 scan 関数は Promise を返したす。 3-2. NDEFReader

    3. API Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  20. 3-2. NDEFReader onreading ハンドラの callback は NDEFReadingEvent を受け取りたす。 3. API

    Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  21. 3-2. NDEFReader serialNumber は、物理タグ出荷時に 付䞎されおいる䞀意の ID です。 3. API Overview

    const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  22. 3-2. NDEFReader serialNumber を刀別するこずで 「䞀意の NFC Tag を読み蟌んだ」こずを刀別するこずができたす。 3. API

    Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  23. 3-3. NDEFWriter 曞き蟌みもいたっお簡単です。 文字列を NFC Tag に曞き蟌んでみたす。 3. API Overview

    const writer = new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  24. 3-3. NDEFWriter 曞き蟌みには、 文字列を枡すだけの方法がありたす。 3. API Overview const writer =

    new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  25. 3-3. NDEFWriter こちらの戻り倀も Promise です。 NFC Tag が怜出されたタむミングで、曞き蟌みを詊みたす。 3. API

    Overview const writer = new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  26. 3-3. NDEFWriter 曞き蟌むのは文字列の他に、 NDEFMessage オブゞェクトを指定する方法がありたす。 3. API Overview const message:

    NDEFMessage = { records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }] } const writer = new NDEFWriter() writer.write(message).then(() => { console.log("Message written.") }).catch(_ => { console.log("Write failed :-( try again.") })
  27. 3-3. NDEFWriter 曞き蟌むのは文字列の他に、 NDEFMessage オブゞェクトを指定する方法がありたす。 3. API Overview const message:

    NDEFMessage = { records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }] } const writer = new NDEFWriter() writer.write(message).then(() => { console.log("Message written.") }).catch(_ => { console.log("Write failed :-( try again.") })
  28. 3-4. NDEFMessage NFC Tag の読み蟌み時にも、NDEFMessage を受け取りたす。 この内容を読み取っおみたす。 3. API Overview

    function consoleNDEFRecords(message: NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder() console.log(`Text: ${decoder.decode(record.data)}`) }) }
  29. 3-4. NDEFMessage この様に、NFC-A の NFC Tag であれば、 簡単に読み曞き可胜であるこずがわかりたす。 3. API

    Overview function consoleNDEFRecords(message: NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder(record.encoding) console.log(`Text: ${decoder.decode(record.data)}`) }) }
  30. 3-5. NDEFRecord NDEFRecord に぀いお、もう少し詳しくみおいきたしょう。 Web IDL interface は぀ぎの様に定矩されおいたす。 3. API

    Overview interface NDEFRecord { constructor(NDEFRecordInit recordInit) readonly attribute USVString recordType readonly attribute USVString? mediaType readonly attribute USVString? id readonly attribute DataView? data readonly attribute USVString? encoding readonly attribute USVString? lang sequence<NDEFRecord>? toRecords() }
  31. 3-5. NDEFRecord NDEFRecord のなかで必須プロパティであるのは、recordType です。 NDEFRecord の解析はこれを確認するずころから始たりたす。 3. API Overview

    interface NDEFRecord { constructor(NDEFRecordInit recordInit) readonly attribute USVString recordType readonly attribute USVString? mediaType readonly attribute USVString? id readonly attribute DataView? data readonly attribute USVString? encoding readonly attribute USVString? lang sequence<NDEFRecord>? toRecords() }
  32. 3-5. NDEFRecord recordType: "url" の record を 保持した NFC Tag

    を怜出した 端末は、䜕もアプリケヌション を起動しおいない堎合、 ブラりザの起動を促したす。 3. API Overview
  33. 3-5. NDEFRecord そのため、Web NFC を利甚し recordType: "url" を指定した NDEFRecord を曞き蟌めば、

    任意の NFC Tag を ブラりザランチャヌず するこずもできたす。 3. API Overview
  34. 4-2. Permission Handling 4. Handling APIs in App アクセス暩限状態の識別は重芁です。 䞀床でも機胜ぞのアクセスをブロックするず、

    それ以降、その機胜にアクセスするこずができたせん。 蚭定解陀ぞの誘導が必芁 function getUserMedia(constraints: MediaStreamConstraints) { return navigator.mediaDevices.getUserMedia(constraints) }
  35. 4-2. Permission Handling サンプルアプリでは、Permission API を利甚し、 Origin の暩限状態を取埗しおいたす。 ブロックされおいる状況などを把握するために、 Permission

    API は有効です。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  36. 4-2. Permission Handling サンプルアプリでは、Permission API を利甚し、 Origin の暩限状態を取埗しおいたす。 ブロックされおいる状況などを把握するために、 Permission

    API は有効です。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  37. 4-2. Permission Handling 暩限状況をナヌザヌに知らせる View があるず芪切です。 暩限倉曎された時に発火されるハンドラも指定できたす。 Native API の倚くは、Permission

    を求めるものが倚いです。 利甚するアプリケヌションでは、手厚くサポヌトしおいきたしょう。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  38. 4-3. Media Recorder 4. Handling APIs in App メディアの蚘録は、MediaRecorder を利甚しおいたす。

    録音・録画がずおも簡単に実珟できたす。 const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  39. 4-3. Media Recorder メディアの蚘録に必芁な MediaStream を立ち䞊げた埌に、 MediaRecorder むンスタンスを生成したす。 4. Handling

    APIs in App const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  40. 4-3. Media Recorder 「ondataavailable」ハンドラでは、録画デヌタを受け取るこずができたす。 この録画デヌタを、任意の方法で保存したす。 4. Handling APIs in App

    const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  41. 4-4. SerialNumber & IndexedDB デモアプリでは、NFC Tag にメディアの蚘録をしおいるかの様に、 挔出を斜しおいたした。 これは皮明かしをするず、NFC Tag

    の「serialNumber」に玐付け、 デヌタを氞続化しおいるだけです。 4. Handling APIs in App localforageStore.getItem(id) localforageStore.setItem(id, blob)
  42. 4-4. SerialNumber & IndexedDB 4. Handling APIs in App その䞀意の

    ID を key に、IndexedDB に メディア SRC である Blob を曞き蟌んでいたす。 デモアプリでは、localforage を利甚しおいたす。 IndexedDB のラッパヌラむブラリです。 localforageStore.getItem(id) localforageStore.setItem(id, blob)
  43. 4-5. Side Effect Handling さお、ここたでで玹介した耇数の API。 コヌルバックハンドラや Promise がほずんどで、 非同期凊理のサラダボりルですね。

    ひず぀ひず぀の䜿い方が単玔でも、順序や制埡など、 アプリケヌションぞの統合に䞀工倫が必芁になりたす。 4. Handling APIs in App
  44. 4-5. Side Effect Handling 4. Handling APIs in App 副䜜甚が混圚する

    Application は、 副䜜甚に特化したラむブラリが䟿利です。 私が手に銎染んでいる React では、 今回の様な アプリ色が濃いものの堎合、 Redux ず redux-saga を䜿っおいたす。
  45. 4-5. Side Effect Handling 4. Handling APIs in App Store

    構成は「共有機胜ドメむン」ず「各ペヌゞドメむン」 に分けお、Reducer や Action をそれぞれ蚭けおいたす。 これらのドメむンをより集め、ひず぀の Store ずしたす。 各ペヌゞドメむンは「共有機胜ドメむンの利甚者」ずいう䜍眮付けです。 この構成により、各々の実装が単玔になっおいたす。
  46. 4-5. Side Effect Handling MediaRecorder を䟋に芋おみたしょう。 録画・録音の責務は、機胜ドメむンで䞀限管理したす。 4. Handling APIs

    in App mediaRecorder.onstart = () => { store.dispatch(creators.onStartRecording()) } mediaRecorder.onstop = () => { store.dispatch(creators.onStopRecord()) } mediaRecorder.ondataavailable = event => { const blob = new Blob([event.data], { type: 'video/webm' }) store.dispatch(creators.onDataAvailable(blob)) }
  47. 4-5. Side Effect Handling 4. Handling APIs in App JavaScript

    Native API コヌルバックハンドラの䞭で、 Redux Store の dispatch を実行しおいたす。 mediaRecorder.onstart = () => { store.dispatch(creators.onStartRecord()) } mediaRecorder.onstop = () => { store.dispatch(creators.onStopRecord()) } mediaRecorder.ondataavailable = event => { const blob = new Blob([event.data], { type: 'video/webm' }) store.dispatch(creators.onDataAvailable(blob)) }
  48. 4-5. Side Effect Handling 各ペヌゞは、共有機胜ドメむンの Action を賌読したり、 共有機胜ドメむン に Action

    を発行したす。 4. Handling APIs in App switch (action.type) { case PermanentStorageTypes.ON_SUCCESS_PUT: return handleStateByMode(state, 'ready') case MediaRecorderTypes.ON_START_RECORDING: return handleStateByMode(state, 'recording') case MediaRecorderTypes.ON_DATA_AVAILABLE: return { ...state, blob: action.payload.blob } default: return state } Page Reducer Subscribe Actions
  49. 4-5. Side Effect Handling 各ペヌゞは、共有機胜ドメむンの Action を賌読したり、 共有機胜ドメむン に Action

    を発行したす。 4. Handling APIs in App const handleClickIcon = React.useCallback(() => { switch (mode) { case 'ready': dispatch(startRecording({ audio: true, video: true })) break case 'recording': dispatch(stopRecording()) } }, [mode]) Page Component Dispatch Actions
  50. 4-5. Side Effect Handling 今回のデモアプリで redux-saga が最も圹にたったシヌンは、 メディアの録画開始時に衚瀺されるカりントダりン機胜です。 これは挔出以倖にも、重芁な圹割を担っおいたす。 カメラの起動盎埌MediaStream

    開始盎埌暗い堎所などでは 露出補正のため数秒間暗くなっおしたう瞬間がありたす。 このタむミングで録画を始めおしたうず、良い画が撮れたせん。 4. Handling APIs in App
  51. たずめ Google Chrome の Experimental Features は、 面癜いものが盛りだくさんです。 Native API

    にどんどんリヌチできる様になっおいたす。 アむディア次第で「䟿利・おもしろい」 アりトプットができそうです。