NativeScript / plugins

@nativescript plugins to help with your developments.
https://docs.nativescript.org/plugins/index.html
Apache License 2.0
190 stars 108 forks source link

NativeScript IOS: Photo uploads to server are randomly failing with slow networks(LTE 1 or 2 bars) #557

Open Yogendhar1919 opened 9 months ago

Yogendhar1919 commented 9 months ago

Nativescript version - 8.4.7, Angular 15, IOS 17.2

When uploading photos to server/Microsoft api in the slow network (LTE 1 or 2 bars). The photos are not uploading and returning below exception. Looks like the request is not even reaching the server.

error { "headers": { "normalizedNames": {}, "lazyUpdate": null, "headers": {} }, "status": 0, "statusText": "Unknown Error", "url": "https://abc", "ok": false, "name": "HttpErrorResponse", "message": "Http failure response for "https://abc": 0 Unknown Error", "error": {} }

When connected to a strong network LTE 3 or more bars ,5g or WIFI , I am able to upload more than 50 photos at once without any issues.

Here is my upload code

//component

uploadPhotos(): void {
    let message;
    this.parent = this.service.getPhotosToUpload();
    if (this.parent !== null && this.parent.length > 0) {
        const parentList$ = new Array<Observable<boolean[]>>;

        this.parent.forEach(p=> {
            const observable = this.submitPhoto(p)
            if (observable) {
                parentList$.push(observable);
            }

        });

        zip(parentList$)
            .pipe(
                switchMap((flags: boolean[][], index: number) => {
                    let totalNotuploaded = 0;
                    let totalUploaded = 0;
                    const results =[]
                    flags.forEach((flag) => {
                        totalNotuploaded += flag.filter(falses => !falses).length;
                        totalUploaded += flag.filter(trues => trues).length;
                    });
                    results.push([totalNotuploaded, totalUploaded])
                    return of(results);
                })
            ).subscribe((results: number[][]) => {
                let notuploaded = results[0][0] ;
                let uploaded = results[0][1];

                this.message = `Uploaded ${uploaded} Photo(s).`;

                if (notuploaded > 0) {
                    this.message += `Errors uploading ${notuploaded}`

                }
            });

    } else {
        this.message = `Uploaded 0 Photos.`;
    }
}

submitPhoto(photo: PhotoWrapper): Observable<boolean[]> {

    const photosToUpload$ = new Array<Observable<boolean>>();
    const photosToUpload = photo.filter((filter) => !filter.Uploaded);

    photosToUpload.forEach(photo => {

        photo.FileContent = this.servic.getFileAsBase64String(new Path(photo.File.directory as any, photo.File.relativePath));
        const observable = this.service.upload(photo);
        photosToUpload$.push(
            observable.pipe(
                catchError(() => {
                    return of(null)
                }),
                switchMap((result: PhotoResult) => {
                    return of(result && result?.flag ? true : false)
                })
            ));
    });
    if (photosToUpload$ && photosToUpload$.length) {
        return forkJoin(photosToUpload$);
    } else { return null; }

}

//upload service

upload(photosData: PhotoWrapper): Observable<PhotoResult> {
    let headers = this.authService.getAuthHeader();
    let url = this.configurationService.uploadPhotoUrl();

    return this.http.post<boolean>(this.configurationService.uploadPhotoUrl(), photoDto, { headers: headers, observe: 'response' }).pipe(
        map((result) =>( {flag: result.body, wrapper: photosData} as PhotoResult)),

        );
}

}

can anyone help me on how to fix this issue or is there any setting on the ios/nativescript/angular side which will prevent this to happen or is something wrong with my upload code?

edusperoni commented 9 months ago

Hello!

You're uploading photos as base64, which means your request is likely quite large for starters, and you're adding an extra layer of packet loss and slow connection. Random request failures are expected in any mobile connection, and they get obviously worse the worse the connection, which is why you should have retry logic built into your critical requests.

You should probably consider the klippa http plugin or the background http plugin as they're more suited for file uploads and likely have better resiliency in worse connections.