preface

Weex learning also has a period of time, about weeX in the use of three terminals, we have also done actual combat development, rendering time between 100-300ms, each platform experience compared to H5 have a great improvement, this article in the iOS perspective record some problems encountered in the development process, If you want to learn more about the front end and Android development you can refer to some of the articles written by my colleagues weeX Practices (front end perspective), WEEX practices (Android perspective)

The preparatory work

  • WeexSDK Access Weex iOS SDK official integration guide

  • WXDevtool Weex Debugging tool – Weex Devtools user manual

Order page combat (WEEX-ios related)

Next, I take the order page as an example to describe some WEEX related knowledge points, as described in the following figure

1. Initialize the SDK and register module, Protocol, and Component



/* Initialize weexSDK in appDelagate and register module, protocol, Component */- (void)initWeex{
    /* Initialize the SDK environment */
    [WXSDKEngine initSDKEnviroment];
    /* Custom module*/
    [WXSDKEngine registerModule:@"shopBase" withClass:[BaseModule class]];
    [WXSDKEngine registerModule:@"shopModal" withClass:[WXModuleAnno class]]./* Initializes Protocol*/
    [WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
    [WXSDKEngine registerHandler:[WXSJNetworkDefaultlmpl new] withProtocol:@protocol(WXNetworkProtocol)];
     /* Initializes Component*/
    [WXSDKEngine registerComponent:@"a" withClass:NSClassFromString(@"WXPushComponent")];
}Copy the code

2. To achieve the effect of similar tabs such as the picture of the first point describes the same viewController switch between multiple views, here the shop order and my order for different views, click to switch back and forth, to achieve the effect of similar tabs first post section rendering WEEx page basic code



/* Rendering weex with JS links produces a view*/- (void)renderWeexWithUrl:(NSString *)url{
    _instance = [[WXSDKInstance alloc] init];
    _instance.viewController = self;
    CGFloat width = self.view.frame.size.width;
    _instance.frame = CGRectMake(self.view.frame.size.width-width, 0, width, _weexHeight);
    _instance.onCreate= ^ (UIView *view) {
       /* Successful rendering of the page produces a view*/
    };
    _instance.onFailed= ^ (NSError *error) {
      
    };
    _instance.renderFinish= ^ (UIView *view) {

    };
    _instance.updateFinish= ^ (UIView *view) {
    };
    [_instance renderWithURL:[NSURL URLWithString:url] options:@{@"bundleUrl":url} data:nil];
}Copy the code

As mentioned above, we can process the generated view by adding simple pages directly to self.view. If you need to switch between multiple views, such as tabbar switch on the order page, I have done the following processing: Save each newly generated view into a dictionary, key is a link value is a newly generated view, each time before rendering the page, first check whether the view already exists by key, if there is a view to show the existing view, no render out the new view code modified as follows



- (void)renderWeexWithUrl:(NSString *)url{
    /* If the view already exists, it will not be re-rendered */
    if ([self.mdicViews objectForKey:url] && [[self.mdicViews objectForKey:url] isKindOfClass:[UIView class]]) {
        [self loadViewforKey:url];
    }else{__weak typeof(self) weakSelf = self;
        _instance = [[WXSDKInstance alloc] init];
        _instance.viewController = self;
        CGFloat width = self.view.frame.size.width;
        _instance.frame = CGRectMake(self.view.frame.size.width-width, 0, width, _weexHeight);
        _instance.onCreate= ^ (UIView *view) {
            /* Successful rendering of the page produces a view*/
            [weakSelf.mdicViews setValue:view forKey:url];
            [weakSelf loadViewforKey:url];
        };
        _instance.onFailed= ^ (NSError *error) {
            
        };
        _instance.renderFinish= ^ (UIView *view) {
            
        };
        _instance.updateFinish= ^ (UIView *view) {
        };
        [_instance renderWithURL:[NSURL URLWithString:url] options:@{@"bundleUrl":url} data:nil]; }}/* Display a view operation by key */- (void)loadViewforKey:(NSString *)mstrJs{
    self.weexView = [_mdicViews objectForKey:mstrJs];
    [self.view insertSubview:self.weexView atIndex:0];
    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.weexView);
    for (int i=0; i<self.view.subviews.count; i++) {
        UIView * mview = [self.view.subviews objectAtIndex:i];
        if (i==0) {
            mview.hidden=NO;
        }else{
            mview.hidden=YES; }}}Copy the code

3. Customize a tag Component to intercept the URL for skipping



#import <WeexSDK/WXComponent.h>

@interface WXPushComponent : WXComponent <UIGestureRecognizerDelegate>

@end


#import "WXPushComponent.h"

@interface WXPushComponent(a)

@property (nonatomic.strong) UITapGestureRecognizer *tap;
@property (nonatomic.strong) NSString *href;

@end

@implementation WXPushComponent


- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
{
    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
    if (self) {
        _tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openURL)];
        _tap.delegate = self;
        if (attributes[@"href"]) {
            _href = attributes[@"href"]; }}return self;
}

- (void)dealloc
{
    if (_tap.delegate) {
        _tap.delegate = nil; }} - (void)viewDidLoad
{
    [self.view addGestureRecognizer:_tap];
}

- (void)openURL
{
    if (_href && [_href length] > 0) {
        /* The jump link of the a tag can be jumped from this link */}} - (void)updateAttributes:(NSDictionary *)attributes
{
    if (attributes[@"href"]) {
        _href = attributes[@"href"]; }}#pragma mark
#pragma gesture delegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        return YES;
    }
    
    return NO;
}

@end
Copy the code

4. Customize Module to implement confirm, toast, and Alert



#import <Foundation/Foundation.h>
#import <WeexSDK/WXModuleProtocol.h>
#import <WeexSDK/WeexSDK.h>

@interface WXModuleAnno : NSObject<WXModuleProtocol>

@end

#import "WXModuleAnno.h"

@implementation WXModuleAnno

@synthesize weexInstance;

WX_EXPORT_METHOD(@selector(toast:))
WX_EXPORT_METHOD(@selector(alert:callback:))
WX_EXPORT_METHOD(@selector(confirm:callback:))

- (void)confirm:(NSDictionary *)param callback:(WXModuleCallback)callback
{
    NSString *message = [self stringValue:param[@"message"]].NSString *okTitle = [self stringValue:param[@"okTitle"]].NSString *cancelTitle = [self stringValue:param[@"cancelTitle"]].if (okTitle.length= =0) {
        okTitle = @ "confirm";
    }
    if (cancelTitle.length= =0) {
        cancelTitle = @ "cancel";
    }
    /* This is your own box component or system component */
    
    / * * /
    callback(okTitle);
   
}

- (void)toast:(NSDictionary *)param{
    NSString *message = [NSString stringWithFormat:@ "% @",param[@"message"]].if(! message)return;
    /* Here is your own toast component */
    
    / * * /

}

- (void)alert:(NSDictionary *)param callback:(WXModuleCallback)callback
{
    NSString *message = [self stringValue:param[@"message"]].NSString *okTitle = [self stringValue:param[@"okTitle"]]./* This is your own box component or system component */
    
    / * * /
    callback(okTitle);
}

// Get the current NVC- (UINavigationController *)currentNVC{
    return [weexInstance.viewController navigationController];
}
// Get the current VC- (UIViewController *)currentVC{
    return weexInstance.viewController;
}

- (NSString*)stringValue:(id)value
{
    if ([value isKindOfClass:[NSString class]]) {
        return value;
    }
    if ([value isKindOfClass:[NSNumber class]]) {
        return [value stringValue];
    }
    return nil;
}

@end


Copy the code

5. Customize image loading protocol to compress and cache images



#import <Foundation/Foundation.h>
#import <WeexSDK/WXImgLoaderProtocol.h>
@interface WXImgLoaderDefaultImpl : NSObject<WXImgLoaderProtocol>
@end

#import "WXImgLoaderDefaultImpl.h"
#import <SDWebImage/UIImageView+WebCache.h>

@interface WXImgLoaderDefaultImpl(a)

@end

@implementation WXImgLoaderDefaultImpl

#pragma mark -
#pragma mark WXImgLoaderProtocol

- (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)userInfo completed:(void(^) (UIImage *image,  NSError *error, BOOL finished))completedBlock
{
    if ([url hasPrefix:@"jpg"] || [url hasPrefix:@"png"]) {
       /* do the corresponding processing */
    }
    return (id<WXImageOperationProtocol>)[[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        
    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
        if(completedBlock) { completedBlock(image, error, finished); }}]; }@end
Copy the code

6. Customize NetworkProtocol to intercept and modify network requests



#import <Foundation/Foundation.h>
#import <WeexSDK/WeexSDK.h>
@interface WXSJNetworkDefaultlmpl : NSObject<WXNetworkProtocol.NSURLSessionDelegate>

@end

#import "WXSJNetworkDefaultlmpl.h"

@interface WXNetworkCallbackInfo : NSObject

@property (nonatomic.copy) void(^sendDataCallback)(int64_t, int64_t);
@property (nonatomic.copy) void(^responseCallback)(NSURLResponse *);
@property (nonatomic.copy) void(^receiveDataCallback)(NSData *);
@property (nonatomic.strong) NSMutableData *data;
@property (nonatomic.copy) void(^compeletionCallback)(NSData *, NSError *);

@end

@implementation WXSJNetworkDefaultlmpl
{
    NSMutableDictionary *_callbacks;
    NSURLSession *_session;
}
- (id)sendRequest:(NSURLRequest *)request withSendingData:(void (^)(int64_t, int64_t))sendDataCallback
     withResponse:(void (^)(NSURLResponse *))responseCallback
  withReceiveData:(void(^) (NSData *))receiveDataCallback
  withCompeletion:(void(^) (NSData *, NSError *))compeletionCallback
{
    /* Block the URL if there is no domain name when adding the domain name in order to keep the three ends in sync use our domain name on each end add */
    if(! [request.URL.absoluteString hasPrefix:@"http"]) {
        request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@ % @ % @ "".@ "",request.URL.absoluteString]]];
    }
    WXNetworkCallbackInfo *info = [WXNetworkCallbackInfo new];
    info.sendDataCallback = sendDataCallback;
    info.responseCallback = responseCallback;
    info.receiveDataCallback = receiveDataCallback;
    info.compeletionCallback = compeletionCallback;
    
    if(! _session) { _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                 delegate:self
                                            delegateQueue:[NSOperationQueue mainQueue]];
    }
    
    NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
    if(! _callbacks) { _callbacks = [NSMutableDictionary dictionary];
    }
    [_callbacks setObject:info forKey:task];
    [task resume];
    
    return task;
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
    if (info.sendDataCallback) {
        info.sendDataCallback(totalBytesSent, totalBytesExpectedToSend); }} - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
    if (info.responseCallback) {
        info.responseCallback(response);
    }
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data
{
    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
    if (info.receiveDataCallback) {
        info.receiveDataCallback(data);
    }
    
    NSMutableData *mutableData = info.data;
    if(! mutableData) { mutableData = [NSMutableData new]; info.data = mutableData;
    }
    
    [mutableData appendData:data];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
    if (info.compeletionCallback) {
        info.compeletionCallback(info.data, error);
    }
    [_callbacks removeObjectForKey:task];
}

@endCopy the code

Weex Native WebView seamless jump

This article mainly explains how to realize the jump between WEEX Native WebViews, which can not only jump at will but also replace the native page. The following content is derived from the weeX support scheme design of the jump rule of our Android weeX practice (Android perspective) App. The jump rule is as follows. If you cannot see it clearly, you can view it on the new page. The following two parameters are introduced: 1. InterceptUrlList parameters can be configured dynamically need to intercept the h5 links, then generate a unified jump address showjoyshop: / / page. Sh/order sample is as follows:



[{"page":"order","url":"https://dshdjshjbx"
    }, {"page":"detail","url":"https://dsdsds"
    }]Copy the code

2. Then use order to find the corresponding JS information in the parameter weexPages, and render the following example:



[{"page":"order","url":"https://dshdjshjbx.js","md5":"323827382huwhdjshdjs","h5":"http://dsds.html"
       "v":"1.5.0"
    }, {"page":"detail","url":"https://dsdsds.js","md5":"323827382huwhdjshdjs","h5":"http://dsds.html"
       "v":"1.5.0"
    }]Copy the code

Url: js to render

Md5: Indicates the MD5 value of the JS file for verification

H5: Demote scheme after rendering failure

V: indicates the lowest supported version number

This achieves the purpose of dynamic interception and dynamic on-line WEEX

Preloading weeX-JS pages improves rendering speed

In order to improve the rendering efficiency, we will download the JS file in advance to the local, directly load the local file, download the logic as follows: First, we will have a place to input the following format of JSON data



[{"page":"Page name","url":"Js Download link","md5":"Js file MD5","h5":"Corresponding H5 page"
       "v":"Version Number"
    }, {"page":"shoporder","url":"https://xxxx.js","md5":"Js file MD5","h5":"http://xxxx.html"
       "v":"1.7.0"
    }]Copy the code

Page: Path corresponding to the unified jump (temporarily the page name)

Url: js to render

Md5: Indicates the MD5 value of the JS file for verification

H5: Demote scheme after rendering failure

V: indicates the lowest supported version number

Then perform the following operations based on the configuration file

  1. After updating the configuration file each time, check whether the page_XXx. js file is md5 consistent. If not, update it

  2. After downloading, save the file in xxx.js format. If the md5 verification is the same and the last modification time of the file is different, delete the downloaded file, download it again, and repeat the verification process

  3. Supports the unified jump protocol. Page corresponds to the page in the unified jump protocol of the current APP terminal. When necessary, the original Native page can be replaced to solve the problem that the errors of the native page cannot be repaired in time. If loading fails, open the H5 page

  4. Each time when opening the specified page, first check whether there is a corresponding page file in the local, and then check whether the last modification time is consistent with the record. If the loading is inconsistent, use the line URL



    The unified jump protocol mentioned in the third article is a way we use to decouple each module, which can be changed according to their own business. Ours is similar: showjoyshop://page.sh/weex
    showjoyshop://page.sh/webviewWeex corresponds to weeX vc WebView corresponds to webView VC WEEx and WebView is the page mentioned in article 3Copy the code