Ionic – sprawdzanie połączenia z internetem i z serwerem.

Chciałbym wam pokazać coś co wydaje się dość oczywiste, a jednak sporo jest ludzi pyta o to na różnych forach. Mowa o sprawdzaniu w Ionic’u czy urządzenie ma połączenie z internetem oraz czy serwer API odpowiada.

Ja rozwiązałem to w głównym komponencie aplikacji (app.component.ts) w następujący sposób:

import {Component} from '@angular/core';
import {Platform, Events} from 'ionic-angular';
import {StatusBar} from '@ionic-native/status-bar';
import {SplashScreen} from '@ionic-native/splash-screen';
import {HomePage} from '../pages/home/home';
import {OfflinePage} from '../pages/offline/offline';
import {ApiErrorPage} from '../pages/api-error/api-error';
import {Network} from '@ionic-native/network';
import {NetworkProvider} from '../providers/network-provider';
import {ApiService} from "../services/api-service";

@Component({
    templateUrl: 'app.html'
})
export class MyApp {
    private mainPage: any = HomePage;
    private offlinePage: any = OfflinePage;
    private apiErrorPage: any = ApiErrorPage;
    public currentPage: any;
    public apiStatus = null;
    public online: boolean = true;

    constructor(platform: Platform,
                statusBar: StatusBar,
                splashScreen: SplashScreen,
                public events: Events,
                public network: Network,
                public networkProvider: NetworkProvider,
                private apiService: ApiService
    ) {
        splashScreen.show();

        platform.ready().then(() => {
            statusBar.styleDefault();
            splashScreen.hide();
            this.initializeApp();
        });
    }

    initializeApp() {
        this.networkProvider.initializeNetworkEvents();
        this.checkConnection();

        this.events.subscribe('network:offline', () => {
            this.online = false;
            this.chooseCurrentPage();
        });

        this.events.subscribe('network:online', () => {
            this.online = true;
            this.checkApi();
        });
    }

    checkConnection() {
        this.online = this.networkProvider.network.type !== 'none';

        if (this.online) {
            this.checkApi();
        } else {
            this.chooseCurrentPage();
        }
    }

    checkApi() {
        this.apiService.checkApi(
            () => {
                this.apiStatus = true;
                this.chooseCurrentPage();
            },
            () => {
                this.apiStatus = false;
                this.chooseCurrentPage();
            }
        );
    }

    private chooseCurrentPage() {
        if (this.online) {
            if (this.apiStatus !== false) {
                this.currentPage = this.mainPage;
            } else {
                this.currentPage = this.apiErrorPage;
            }
        } else {
            this.currentPage = this.offlinePage;
        }
    }
}

Jak widać mam pola w klasie które trzymają trzy strony: mainPage, offlinePage, i apiErrorPage. W zależności od statusu połączenia z internetem i z serwerem odpowiednia strona podstawiana jest w polu currentPage.

Zawartość pola currentPage jest podstawiona w jedynej linijce kodu w pliku app.html:

<ion-nav [root]="currentPage"></ion-nav>

Połączenie jest sprawdzane przy uruchomieniu aplikacji oraz przy pomocy obserwowania zdarzeń, którego implementacja opiera się na klasie NetworkProvider, której zawartość pochodzi z tego wątku i wygląda następująco:

import { Injectable } from '@angular/core';
import { Events } from 'ionic-angular';
import { Network } from '@ionic-native/network';

export enum ConnectionStatusEnum {
    Online,
    Offline
}

@Injectable()
export class NetworkProvider {
    private previousStatus;

    constructor(public network: Network,
                public eventCtrl: Events) {
        this.previousStatus = ConnectionStatusEnum.Online;

    }

    public initializeNetworkEvents(): void {
        this.network.onDisconnect().subscribe(() => {
            if (this.previousStatus === ConnectionStatusEnum.Online) {
                this.eventCtrl.publish('network:offline');
            }
            this.previousStatus = ConnectionStatusEnum.Offline;
        });
        this.network.onConnect().subscribe(() => {
            if (this.previousStatus === ConnectionStatusEnum.Offline) {
                this.eventCtrl.publish('network:online');
            }
            this.previousStatus = ConnectionStatusEnum.Online;
        });
    }
}

Z kolei do sprawdzania czy serwer api odpowiada zaimplementowałem prostyendpoint po stronie api, który ma za zadanie jedynie zwrócenie statusu http 200. Natomiast po stronie aplikacji używam metody w serwisie ApiService, które wspomniany endpoint sprawdza. Serwis ten, po usunięciu kodu niezwiązanego z tym artykułem, wygląda tak:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ConfigService } from './config-service'

@Injectable()
export class ApiService {
    private endpointApiTest = '/api/test';

    constructor(public http: HttpClient, public config: ConfigService ) {

    }

    public checkApi(successCallback, errorCallback) {
        this.subscribePromise(
            this.http.get(this.config.apiUrl + this.endpointApiTest),
            successCallback,
            errorCallback
        );
    }

    private subscribePromise(promise, successCallback, errorCallback)
    {
        promise.subscribe(
            (data => {
                if (typeof successCallback === 'function') {
                    successCallback(data);
                }
            }),
            (error => {
                if (typeof errorCallback === 'function') {
                    errorCallback(error);
                }
            }),
        );
    }
}

W moim projekcie pobieram adres url API z serwisu ConfigService, u siebie możecie zrobić to jak wam będzie najwygodniej.

Skomentuj