The practice of real native incremental update for iOS client

Existing solutions in the market: Microsoft's CodePush And react native Pushy , you can choose according to the actual situation of the company.
In consideration of security, the company is forbidden to upload source related files to the third-party platform, so it is necessary to implement it by itself.

Main implementation process of incremental update
1. Use the react native bundle command to package and compress the package file
2. Use bsdiff to generate the patchFile of the compressed file of the new and old RN versions
3. Compress the patchFile file downloaded by merging the old RN version of the client, verify the MD5 value of the file, decompress the merged file, reload the jsbundle, and complete the update
4. Add version control and script 1 and 2 processes

common problem:
1, The use of bsdiff
Download address: http://www.daemonology.net/bsdiff/
1. Decompress the download file

$ cd bsdiff-4.3

2. Modify the format of the makefile file as follows. The last three lines of the original file are not in the correct indentation format:

CFLAGS        +=    -O3 -lbz2

PREFIX        ?=    /usr/local
INSTALL_PROGRAM    ?=    ${INSTALL} -c -s -m 555
INSTALL_MAN    ?=    ${INSTALL} -c -m 444

all:        bsdiff bspatch
bsdiff:        bsdiff.c
bspatch:    bspatch.c

install:
    ${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin
    .ifndef WITHOUT_MAN
    ${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1
    .endif

3. Add header file to bspatch.c file

#include<sys/types.h>

4. The terminal executes the make command to generate bsdiff and bspatch files

$ make

5. Copy bsdiff and bspatch files to usr/local/bin directory
6. Usage:

//Generate difference file
$ bsdiff oldFilePath newFilePath patchFilePath
//Merged documents
$ bspatch oldFilePath newFilePath patchFilePath

2, The client merges the files. The project needs to add libbz2.tbd
1.bsdiff.h

#ifndef bsdiff_h
#define bsdiff_h

#define LBD_OK 0

#define LBD_ERR_OPEN 1000
#define LBD_ERR_CLOSE 1005
#define LBD_ERR_MALLOC 1010
#define LBD_ERR_SEEK 1015
#define LBD_ERR_TELL 1020
#define LBD_ERR_READ 1025
#define LBD_ERR_WRITE 1030
#define LBD_ERR_CORRUPT 1035

#define LBD_ERR_BZ 2000

#include <stdio.h>

int ff_patch(const char *oldf, const char *patchf, const char *newf);

#endif /* bsdiff_h */

2.bsdiff.c

#include "bsdiff.h"
#include <stdio.h>
#include <stdint.h>
#include <bzlib.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>

#define MIN(x,y) (((x)<(y)) ? (x) : (y))

static off_t offtin(u_char *buf) {
    off_t y;

    y=buf[7]&0x7F;
    y=y*256;y+=buf[6];
    y=y*256;y+=buf[5];
    y=y*256;y+=buf[4];
    y=y*256;y+=buf[3];
    y=y*256;y+=buf[2];
    y=y*256;y+=buf[1];
    y=y*256;y+=buf[0];

    if(buf[7]&0x80) y=-y;

    return y;
}

#pragma mark - Public

int ff_patch(const char *oldf, const char *patchf, const char *newf) {
    FILE *f, *cpf, *dpf, *epf;
    BZFILE *cpfbz2, *dpfbz2, *epfbz2;
    int cbz2err, dbz2err, ebz2err;
    int fd;
    ssize_t oldsize, newsize;
    ssize_t bzctrllen, bzdatalen;
    uint8_t header[32], buf[8];
    uint8_t *old, *new;
    off_t oldpos, newpos;
    off_t ctrl[3];
    off_t lenread;
    off_t i;

    if ((f = fopen(patchf, "r")) == NULL)
    {
        return LBD_ERR_OPEN;
    }

    /* Read header */
    if (fread(header, 1, 32, f) < 32)
    {
        if (feof(f))
        {
            return LBD_ERR_CORRUPT;
        }

        return LBD_ERR_READ;
    }

    /* Check for appropriate magic */

    // had to change this to use patches created with command line bsdiff
    //    if (memcmp(header, "LBDIFFXX", 8) != 0)
    if (memcmp(header, "BSDIFF40", 8) != 0)
    {
        return LBD_ERR_CORRUPT;
    }

    /* Read lengths from header */
    bzctrllen = offtin(header + 8);
    bzdatalen = offtin(header + 16);
    newsize = offtin(header + 24);
    if ((bzctrllen < 0) || (bzdatalen < 0) || (newsize < 0))
    {
        return LBD_ERR_CORRUPT;
    }

    /* Close patch file and re-open it via libbzip2 at the right places */
    if (fclose(f))
    {
        return LBD_ERR_CLOSE;
    }

    if ((cpf = fopen(patchf, "r")) == NULL)
    {
        return LBD_ERR_OPEN;
    }

    if (fseeko(cpf, 32, SEEK_SET))
    {
        return LBD_ERR_SEEK;
    }

    if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
    {
        return LBD_ERR_BZ;
    }

    if ((dpf = fopen(patchf, "r")) == NULL)
    {
        return LBD_ERR_OPEN;
    }

    if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
    {
        return LBD_ERR_SEEK;
    }

    if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
    {
        return LBD_ERR_BZ;
    }

    if ((epf = fopen(patchf, "r")) == NULL)
    {
        return LBD_ERR_OPEN;
    }

    if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
    {
        return LBD_ERR_SEEK;
    }

    if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
    {
        return LBD_ERR_BZ;
    }

    if (((fd = open(oldf, O_RDONLY, 0)) < 0) ||
        ((oldsize = lseek(fd, 0, SEEK_END)) == -1) ||
        ((old = malloc(oldsize + 1)) == NULL) ||
        (lseek(fd, 0, SEEK_SET) != 0) ||
        (read(fd, old, oldsize) != oldsize) ||
        (close(fd) == -1))
    {
        return LBD_ERR_OPEN;
    }

    if ((new = malloc(newsize + 1)) == NULL)
        return LBD_ERR_MALLOC;

    oldpos = 0;
    newpos = 0;
    while (newpos < newsize)
    {
        /* Read control data */
        for (i = 0;i <= 2; i++)
        {
            lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);

            if ((lenread < 8) || ((cbz2err != BZ_OK) &&
                                  (cbz2err != BZ_STREAM_END)))
            {
                return LBD_ERR_CORRUPT;
            }

            ctrl[i] = offtin(buf);
        }

        /* Sanity-check */
        if (newpos + ctrl[0] > newsize)
        {
            return LBD_ERR_CORRUPT;
        }

        /* Read diff string */
        lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
        if ((lenread < ctrl[0]) ||
            ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
        {
            return LBD_ERR_CORRUPT;
        }

        /* Add old data to diff string */
        for (i=0; i < ctrl[0]; i++)
            if ((oldpos + i >= 0) && (oldpos + i < oldsize))
                new[newpos + i] += old[oldpos + i];

        /* Adjust pointers */
        newpos += ctrl[0];
        oldpos += ctrl[0];

        /* Sanity-check */
        if (newpos + ctrl[1] > newsize)
        {
            return LBD_ERR_CORRUPT;
        }

        /* Read extra string */
        lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
        if ((lenread < ctrl[1]) ||
            ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
        {
            return LBD_ERR_CORRUPT;
        }

        /* Adjust pointers */
        newpos += ctrl[1];
        oldpos += ctrl[2];
    }

    /* Clean up the bzip2 reads */
    BZ2_bzReadClose(&cbz2err, cpfbz2);
    BZ2_bzReadClose(&dbz2err, dpfbz2);
    BZ2_bzReadClose(&ebz2err, epfbz2);
    if (fclose(cpf) || fclose(dpf) || fclose(epf))
    {
        return LBD_ERR_CLOSE;
    }

    /* Write the new file */
    if (((fd = open(newf, O_CREAT|O_TRUNC|O_WRONLY, 0666)) < 0) ||
        (write(fd, new, newsize) != newsize) || (close(fd) == -1))
    {
        return LBD_ERR_OPEN;
    }

    free(new);
    free(old);

    return LBD_OK;
}

3, iOS client decompression tool: SSZipArchive

4, Reference items react-native-pushy

5, Version management scripting to be updated

Keywords: React Makefile iOS

Added by deffe on Tue, 14 Apr 2020 21:56:20 +0300