PWA를 기본 iOS/Android 애플리케이션에 내장하는 방법

본고에서, 나는 당신에게 점진적인 웹 응용 프로그램 (PWA) 이나 그 어떠한 웹 사이트를 (기존) 원본 응용 프로그램에 삽입하는 방법을 보여 드리겠습니다.그들은 어떻게 서로 의사소통과 내비게이션 처리를 하는가.
PWA는 Nuxt로 구축되기 때문에 예시 코드는 Nuxt가 특정하지만 원칙은 각 PWA에 적용된다.

카탈로그📖

  • 🤔 Why?
  • 🍪 Storage & Login Session
  • 📨 Communication
  • 🧭 Navigation
  • 왜?🤔


    잘 물었습니다.당신은 다음과 같은 몇 가지를 고려할 수 있습니다.
  • 🏗 기존의 iOS/Android 프로그램을 한 걸음 한 걸음 PWA로 바꾸거나 네트워크에서도 사용할 수 있는 새로운 기능을 구축하고 싶을 수도 있다
  • 🤑 이것은 개발자의 업무량을 줄여서 한 플랫폼에서 한 번만 개발할 수 있다
  • 🤖 새로운 기능은 검색엔진에 인덱스될 수 있을 것 같다
  • 🏃‍♂️ 새로운 기능/수정 사항은 발표 과정을 거치지 않아도 되기 때문에 더욱 신속하게 발표할 수 있습니다
  • .
  • 🏠 App/Play 스토어에서는 여전히 App을 제공하므로 사용자는 다양한 채널
  • 을 통해 App/Play 스토어를 찾아 설치할 수 있습니다.
  • 🦄 달력, 연락처, 문자, 전화 차단 등 PWA가 할 수 없는 일을 하고 싶을지도 모른다.API가 여전히 부족하기 때문에 iOS Safari가 할 수 없는 일을 하거나 (예를 들어 백엔드 동기화)
  • 스토리지 및 로그인 세션🍪


    PWA가 WKWebView(>=iOS 8)에 표시됩니다.이 프로그램의 iframe로 볼 수 있습니다.모든 WKWebView는 자신의 저장 데이터(Cookie, local Storage, IndexedDB 등)를 가지고 있으며, 이 데이터는 닫히고 다시 열면 복구됩니다.그러나 네이티브 애플리케이션은 WebView와 자체 쿠키를 공유하지 않습니다.
    따라서 사용자가 로그인해야 할 경우 WebView에서 PWA를 연 후 다시 로그인하지 않도록 로그인 세션을 수동으로 재사용해야 합니다.
    이를 위해 프로그램 개발자는 초기 프로그램 로그인 화면이나 지문에서 얻은 세션/영패 설정 쿠키를 사용할 수 있다.WebView에 쿠키를 설정하려면 다음을 사용할 수 있습니다WKHTTPCookieStore.

    커뮤니케이션📨


    PWA와 네이티브 애플리케이션이 서로 대화하고 작업을 수행하도록 알리기를 원할 수도 있습니다.그래서 우리는 다리를 건설해야 한다. 그곳에서 그들은 서로 이야기를 나눌 수 있다.

    이를 위해, 우리는 두 가지 방법으로 전역 대상 (window.bridge 을 추가할 것이다.하나는 PWA 내부에서 본체 응용 프로그램의 조작을 호출하는 데 사용되고, 다른 하나는 메시지와 명령을 수신하는 데 사용되며, 이 메시지와 명령은 본체 응용 프로그램에서 실행될 것이다.이 방법에서 우리는 정보를 Vuex 저장소에 넣어서 그것들을 관찰할 수 있다.
    방법 이름, iOS/Android 개발자에게 전달되는 데이터 구조, 수신된 데이터는 사용자와 응용 프로그램 개발자에 달려 있습니다.
    응용 개발자는 JS 코드를 WebView에 주입할 수 있습니다.우리의 예에서, 그는 JSON 문자열을 수신하는 전역 방법 invokeNative 을 정의해야 한다.우리는 이 기능을 검사해서 우리가 응용 프로그램에 있는지 일반 브라우저에 있는지 검사할 수 있다.
    다음은 Nuxt 플러그인에 포함된 브리지 코드입니다.
    // bridge.client.js nuxt plugin
    export default (context, inject) => {
        // force app mode with ?app param to be able to test
        const { app } = context.query;
        // determine whether the app is opened inside native app
        const inApp = !!window.invokeCSharpAction
            || typeof app !== 'undefined';
    
        // inject global $inApp variable and 
        inject('inApp', inApp);
        context.store.commit('setInApp', inApp);
    
        // the bridge object in the global namespace
        window.bridge = {
            // invoke native function via PWA
            invokeNative(data) {
                if (!window.invokeCSharpAction) {
                    return;
                }
    
                window.invokeCSharpAction(
                    JSON.stringify(data)
                );
            },
            // called by native app
            invokedByNative(data) {
                // write passed data to the store
                context.store.commit(
                    'addAppMessage',
                    JSON.parse(data)
                );
            }
        }
    
        inject('bridge', window.bridge);
    }
    
    브리지를 설치한 후에는 다음과 같이 PWA에서 네이티브 작업을 호출할 수 있습니다.
    // callable in stores, components & plugins
    this.$bridge.invokeNative({
        function: 'Close'|'SaveSomething'
        payload: {
            lang, title, ...
        }
    });
    
    네이티브 애플리케이션 개발자는 다음 JS 코드를 실행하여 PWA 작업을 호출할 수 있습니다.
    // callable in native app
    this.$bridge.invokedByNative({
        function: 'GoBack'|'HasSavedSomething'
        response: {
            success, ...
        }
    });
    
    Vuex 스토리지의 저장 작업은 다음과 같습니다.
    async saveSomething({ state, commit, rootGetters }) {
        // prevent saving while it's already saving 
        if (state.isSaving) {
            return;
        }
    
        commit('setIsSaving', true);
    
        // data we want to pass to the app or to our API
        const payload = { ... };
    
        // call the bridge method inside app
        if (this.$inApp) {
            this.$bridge.invokeNative({
                function: 'SaveSomething',
                payload
            });
        // otherwise we will call the API like we're used to
        } else {
            // await POST or PUT request response ...
    
            // finish saving and set response id
            if (response.success) {
                commit('setId', response.id);
            } else {
                // Failed, inform user 😢
            }
    
            commit('setIsSaving', false);
        }
    }
    
    일반 API 호출에서 얻은 것처럼 브리지 연결 방법에서 직접적인 응답을 받지 못할 수도 있습니다.응용 프로그램이 언제 실행되었는지, 성공했는지 알 수 있도록, 본 프로그램의 호출 invokedByNative 방법을 통해 우리에게 통지해야 합니다.PWA에서는 다음과 같은 메시지를 들을 수 있습니다.
    // inside app / layout component
    import { mapState, mapMutations } from 'vuex';
    
    export default {
        computed: {
            // map messages and isSaving to show a loader e.g.
            ...mapState(['appMessages', 'isSaving'])
        },
        methods: {
            // map necessary mutations
            ...mapMutations(['setId', 'setIsSaving'])
        },
        watch: {
            // watch the messages from the store for any changes
            appMessages(mgs) {
                // get last received message
                const lastMsg = mgs[mgs.length - 1];
                const appFunction = lastMsg.function;
                const response = lastMsg.response || {};
    
                // check if last message belongs to function we expect a response from
                if (appFunction === 'HasSavedSomething') {
                    if (response.success) {
                        this.setId(response.id);
                    } else {
                        // Failed, inform user 😢
                    }
    
                    this.setIsSaving(false);
                }
            }
        }
    };
    
    지금 우리는 이미 다리를 놓아서 서로 명령을 발송할 수 있다.

    항행🧭


    PWA가 기본 응용 프로그램의 일부로서 WebView에서 실행되는 경우 전체 응용 프로그램을 닫지 않고도 항상 응용 프로그램으로 돌아갈 수 있는지 확인합니다.
    이전에 Nuxt 플러그인에 설정한 전역 window.invokeCSharpAction 변수를 사용하여 템플릿과 구성 요소를 변경할 수 있습니다.WebView에서 PWA를 열면 메뉴 표시줄에 닫기 버튼을 표시하거나 다른 설계를 조정할 수 있습니다.

    또한 응용 프로그램 개발자는 404나 500 등 HTTP 오류를 포획하고 WebView를 닫으며 사용자에게 알림 메시지를 표시할 수 있도록 해야 한다.
    또 다른 도전은 후퇴 버튼/내비게이션을 실현하는 것이다.Android에서는 일반적으로 아래쪽에 후퇴 버튼이 있습니다.iOS에는 없지만 슬라이딩 제스처를 사용할 수 있습니다.
    사용자가 돌아올 때 이전에 방문한 사이트가 있으면 PWA 내부에서 호출해야 한다. 그렇지 않으면 WebView를 닫아야 한다.
    불행하게도, invokedByNative API는 현재 기록 항목의 위치를 감지하거나 접근할 수 없습니다.또한 $inApp가 PWA에서 URL 업데이트에 사용될 때canGoBack 속성도 작동하지 않는 것 같습니다.
    PWA 내부에서 당사의 내역/역추적 목록을 구현하여 이 문제를 해결할 수 있습니다.
    // inside app / layout component
    export default {
        data() {
            return {
                history: []
            }
        },
        watch: {
            // watch route which updates when URL has changed
            '$route'(to, from) {
                // find if target page has been visited
                const entry = this.appRouteHistory
                    .findIndex(
                        entry => to.path === entry.from.path
                    );
    
                if (entry > -1) {
                    // back navigation:
                    // remove every entry that is
                    // after target page in history
                    this.appRouteHistory.splice(entry);
                } else {
                    // forward navigation
                    this.appRouteHistory.push({ to, from });
                }
            }
        },
        methods: {
            goBack() {
                const lastAppHistoryEntry = this.appRouteHistory.length > 0
                    ? this.appRouteHistory[this.appRouteHistory.length-1]
                    : null;
    
                if (lastAppHistoryEntry) {
                    // go back to last entry
                    this.$router.push(lastAppHistoryEntry.from);
                } else {
                    // tell the app it should close the WebView
                    this.$bridge.invokeNative({
                        function: 'Close'
                    })
                }
            }
        }
    }
    
    개발자는 응용 프로그램 내에서 후퇴 버튼 기능을 덮어쓰고 다음 JS 코드를 호출할 수 있습니다.
    // callable in native app to go back in PWA or close WebView
    this.$bridge.invokedByNative({ function: 'GoBack' });
    
    마지막으로 감청history.back 방법 중의 window.history 메시지(상기 통신 부분의 실현 참조)를 확보하고 pushState 방법을 호출해야 한다.
    if (appFunction === 'GoBack') {
        this.goBack();
    }
    

    결말🔚


    나는 이 글이 너로 하여금 PWA와 (기존) 원본 응용 프로그램 사이의 연결을 어떻게 하는지 신속하게 이해하게 할 수 있기를 바란다.만약 당신에게 어떤 문제가 있다면, 메시지를 남겨 주세요.

    좋은 웹페이지 즐겨찾기