Microsoft's Codepush hot update is very difficult to use as we all know, speed and the wall is no different.
On the other hand, we don't want to put the code on someone else's server. Write your own interface updates feel a little safer.
So, come and get yourself a hot update management tool for the React-native app. The name is hotdog.
/**************************************************/
First, we want to understand the principle of react-native startup, is to call the Jslocation Jsbundle file and assets resource file directly.
As a result, we can go through the server interface to determine the version, and download the latest and then replace the corresponding file, and then from this file call launch app. This is the same version of management as some of the previous h5app.
Take iOS as an example, we need to build this own RN upgrade plugin in the following steps:
First, set the default Jsbundle address (such as the Document folder):
1. First pack the time to put Jsbundle and assets into the copy bundle resource, after each boot, detects the document folder exists, does not exist copy to the document folder, and then to the RN framework read start.
We set up the following bundle file management classes:
MXBundleHelper.h
#import <Foundation/Foundation.h>@interface mxbundlehelper:nsobject+ (Nsurl * ) Getbundlepath; @end
Mxbundlehelper.m
#import "MXBundleHelper.h"#import "RCTBundleURLProvider.h"@implementationMxbundlehelper+ (Nsurl *) getbundlepath{#ifdefDEBUG Nsurl*jscodelocation = [[Rctbundleurlprovider sharedsettings] Jsbundleurlforbundleroot:@"Index.ios"Fallbackresource:nil]; returnjscodelocation;#else //document paths that need to be stored and read//Jsbundle AddressNSString *jscachepath = [NSString stringWithFormat:@"%@/\%@", Nssearchpathfordirectoriesindomains (NSDocumentDirectory, Nsuserdomainmask, YES) [0],@"Main.jsbundle"]; //Assets folder AddressNSString *assetscachepath = [NSString stringWithFormat:@"%@/\%@", Nssearchpathfordirectoriesindomains (NSDocumentDirectory, Nsuserdomainmask, YES) [0],@"Assets"]; //determine if jsbundle existsBOOL jsexist =[[Nsfilemanager Defaultmanager] fileexistsatpath:jscachepath]; //if it already exists if(jsexist) {NSLog (@"JS already exists:%@", Jscachepath); //If it does not exist}Else{nsstring*jsbundlepath = [[NSBundle mainbundle] Pathforresource:@"Main"OfType:@"Jsbundle"]; [[Nsfilemanager Defaultmanager] Copyitematpath:jsbundlepath topath:jscachepath Error:nil]; NSLog (@"JS has been copied to document:%@", Jscachepath); } //determine if assets existsBOOL assetsexist =[[Nsfilemanager Defaultmanager] fileexistsatpath:assetscachepath]; //if it already exists if(assetsexist) {NSLog (@"assets already exists:%@", Assetscachepath); //If it does not exist}Else{nsstring*assetsbundlepath = [[NSBundle mainbundle] Pathforresource:@"Assets"Oftype:nil]; [[Nsfilemanager Defaultmanager] Copyitematpath:assetsbundlepath topath:assetscachepath Error:nil]; NSLog (@"assets copied to document:%@", Assetscachepath); } return[Nsurl Urlwithstring:jscachepath];#endif}
Second, do upgrade detection, there are updates to download, and then replace the local file:
If we do not update immediately, can be updated after the replacement, and then will not affect the use of the app, the next time the use will default is the latest.
If it is updated immediately, it needs to be restarted using the Reload function in the Rctbridge class .
This is done by nsurlsession to download, and then zip decompression methods to implement the file replacement.
MXUpdateHelper.h
#import <Foundation/Foundation.h>void(^finishblock) (Nsinteger status,ID data); @interface mxupdatehelper:nsobject+ (void) checkupdate: (finishblock) finish; @end
Mxupdatehelper.m
#import "MXUpdateHelper.h"@implementationMxupdatehelper+(void) Checkupdate: (finishblock) finish{nsstring*url =@"http://www.xxx.com/xxxxxxx"; Nsmutableurlrequest*newrequest =[[Nsmutableurlrequest alloc] Initwithurl:[nsurl Urlwithstring:url]]; [Newrequest Sethttpmethod:@"GET"]; [Nsurlconnection sendasynchronousrequest:newrequest queue:[nsoperationqueue Mainqueue] CompletionHandler:^ (Nsurlresponse * response, NSData * data, Nserror *connectionerror) { if(Connectionerror = =Nil) { //request your server's API to determine if the current JS version is up to date /*{"Version": "1.0.5", "FileUrl": "Http://www.xxxx.com/xxx.zip" ," message ":" There is a new version, please update to our latest version "," ForceUpdate: "NO"}*/ //If you need to updateNSString *curversion =@"1.0.0"; NSString*newversion =@"2.0.0"; //in general, it's not the same, it's the old version. if(![curversion isequaltostring:newversion]) {Finish (1, data); }Else{Finish (0, nil); } } }];}@end
Iii. customization in Appdelegate, frame, direct force update, etc.
If you need to force a refresh of reload, the way we create a new rctview also needs to be changed slightly, by creating a new Rctbridge object.
Because there are reload interfaces in the Rctbridge can be used.
#import "AppDelegate.h"#import "RCTBundleURLProvider.h"#import "RCTRootView.h"#import "MXBundleHelper.h"#import "MXUpdateHelper.h"#import "MXFileHelper.h"#import "SSZipArchive.h"@interfaceAppdelegate () <UIAlertViewDelegate>@property (nonatomic,strong) Rctbridge*Bridge; @property (Nonatomic,strong) nsdictionary*Versiondic;@end@implementationappdelegate-(BOOL) Application: (UIApplication *) application didfinishlaunchingwithoptions: (Nsdictionary *) launchoptions{Nsurl*jscodelocation; Jscodelocation=[Mxbundlehelper Getbundlepath]; _bridge=[[Rctbridge alloc] Initwithbundleurl:jscodelocation moduleprovider:nil Launchoptions:launchoptions]; Rctrootview*rootview = [[Rctrootview alloc] Initwithbridge:_bridge modulename:@"Mxversionmanager"Initialproperties:nil]; Rootview.backgroundcolor= [[Uicolor alloc] initwithred:1.0fGreen1.0fBlue1.0fAlpha1]; Self.window=[[UIWindow alloc] Initwithframe:[uiscreen mainscreen].bounds]; Uiviewcontroller*rootviewcontroller = [UiviewcontrollerNew]; Rootviewcontroller.view=Rootview; Self.window.rootViewController=Rootviewcontroller; [Self.window makekeyandvisible]; __weak appdelegate*weakself =Self ; //Update detection[Mxupdatehelper checkupdate:^ (Nsinteger status,IDdata) { if(Status = =1) {Weakself.versiondic=data; /*here the specific user experience is a variety of ways, such as automatic immediate updating, the box immediately updated, automatically download the next open and then update. */Uialertview*alert = [[Uialertview alloc] Initwithtitle:@"Tips"message:data[@"message"]Delegate: Self Cancelbuttontitle:@"Cancel"Otherbuttontitles:@"Update Now", nil]; [Alert show]; //to download, and update//finished downloading, overlay JS and assets, and reload interface//[Weakself.bridge Reload]; } }]; returnYES;}- (void) Alertview: (Uialertview *) Alertview Clickedbuttonatindex: (nsinteger) buttonindex{if(Buttonindex = =1){ //Update[[Mxfilehelper Shared] downloadfilewithurlstring:_versiondic[@"FileURL"] finish:^ (Nsinteger status,IDdata) { if(Status = =1) {NSLog (@"Download Complete"); Nserror*error; NSString*filepath = (NSString *) data; NSString*despath = [NSString stringWithFormat:@"%@", Nssearchpathfordirectoriesindomains (NSDocumentDirectory, Nsuserdomainmask, YES) [0]]; [Ssziparchive unzipfileatpath:filepath todestination:despath overwrite:yes password:nil Error:&ERROR]; if(!error) {NSLog (@"Unzip successfully"); [_bridge Reload]; }Else{NSLog (@"Decompression failed"); } } }]; }}
The process is simple, request version via interface, then download to document to access. It needs to do zip decompression, as well as file copy and so on.
Run an iOS project to see the effect. Initial version 1.0.0, and then upgrade to 1.0.1 after update.
Demo:https://github.com/rayshen/mxhotdog
React-native farewell to Codepush, self-built Hot update Version upgrade environment