mirror of
https://github.com/wu736139669/ASHReactNative.git
synced 2026-08-06 06:15:01 +00:00
init
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// ASHReactNative.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASHRNBridgeManagerProtocol.h"
|
||||
#import "ASHRNViewFactoryProtocol.h"
|
||||
#import "ASHRNBundleManagerProtocol.h"
|
||||
@interface ASHReactNative : NSObject
|
||||
|
||||
/// js bundle 加载管理器
|
||||
@property (nonatomic, strong, readonly) id<ASHRNBundleManagerProtocol> bundleManager;
|
||||
|
||||
/// rn view 生成工厂
|
||||
@property (nonatomic, strong, readonly) id<ASHRNViewFactoryProtocol> viewFactory;
|
||||
|
||||
/// rn bridge 生成工厂
|
||||
@property (nonatomic, strong, readonly) id<ASHRNBridgeManagerProtocol> rnbridgeFactory;
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
|
||||
/**
|
||||
url加载页面,model:url上读取,如果没有默认加载model:index页面
|
||||
|
||||
@param URL 包地址
|
||||
*/
|
||||
|
||||
- (void)pushRNViewControllerWithURL:(NSString *)URL;
|
||||
/**
|
||||
url加载页面,默认加载model:index页面
|
||||
|
||||
@param URL 包地址
|
||||
*/
|
||||
- (void)pushRNViewControllerWithURL:(NSString *)URL modelName:(NSString*)model;
|
||||
|
||||
|
||||
/**
|
||||
加载本地包地址
|
||||
|
||||
@param bundleName 包名称
|
||||
@param model 页面名称
|
||||
*/
|
||||
- (void)pushRNViewControllerWithBundleName:(NSString *)bundleName modelName:(NSString *)model;
|
||||
@end
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// ASHReactNative.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHReactNative.h"
|
||||
#import "ASHRNBundleManagerImpl.h"
|
||||
#import "ASHRNViewFactoryImpl.h"
|
||||
#import "ASHRNBridgeManagerImpl.h"
|
||||
#import "NSString+Util.h"
|
||||
@implementation ASHReactNative
|
||||
|
||||
+ (instancetype)shareInstance
|
||||
{
|
||||
static id instance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_bundleManager = [ASHRNBundleManagerImpl new];
|
||||
_viewFactory = [ASHRNViewFactoryImpl new];
|
||||
_rnbridgeFactory = [ASHRNBridgeManagerImpl new];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)pushRNViewControllerWithURL:(NSString *)URL
|
||||
{
|
||||
NSDictionary* dic = [URL ash_queryDictionary];
|
||||
[self pushRNViewControllerWithURL:URL modelName:dic[@"model"]];
|
||||
}
|
||||
- (void)pushRNViewControllerWithURL:(NSString *)URL modelName:(NSString *)model
|
||||
{
|
||||
UIViewController *rnVC = nil;
|
||||
rnVC = [[ASHReactNative shareInstance].viewFactory rnViewControllerWithBundleURL:URL modelName:model];
|
||||
[[self getUsingViewController].navigationController pushViewController:rnVC animated:YES];
|
||||
}
|
||||
- (void)pushRNViewControllerWithBundleName:(NSString *)bundleName modelName:(NSString *)model
|
||||
{
|
||||
UIViewController *rnVC = nil;
|
||||
rnVC = [[ASHReactNative shareInstance].viewFactory rnViewControllerWithBundleName:bundleName modelName:model];
|
||||
[[self getUsingViewController].navigationController pushViewController:rnVC animated:YES];
|
||||
}
|
||||
- (UIViewController*)getUsingViewController
|
||||
{
|
||||
UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
if ([appRootVC isKindOfClass:[UINavigationController class]]) {
|
||||
return [(UINavigationController*)appRootVC visibleViewController];
|
||||
}
|
||||
if ([appRootVC isKindOfClass:[UIViewController class]]) {
|
||||
return appRootVC;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// ASHRNViewController.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class ASHRNView;
|
||||
@interface ASHRNViewController : UIViewController
|
||||
|
||||
/**
|
||||
通过包url初始化
|
||||
|
||||
@param bundleURL 包地址
|
||||
@return IMYRNViewController
|
||||
*/
|
||||
- (instancetype)initWithBundleURL:(NSString *)bundleURL;
|
||||
|
||||
|
||||
/**
|
||||
通过包url读取包地址,并且展示 modelName 的内容。
|
||||
|
||||
@param bundleURL 包地址
|
||||
@param modelName 页面名称
|
||||
@return IMYRNViewController
|
||||
*/
|
||||
- (instancetype)initWithBundleURL:(NSString *)bundleURL modelName:(NSString *)modelName;
|
||||
|
||||
|
||||
/**
|
||||
加载本地bundle包
|
||||
|
||||
@param bundleName 本地的bundle名称
|
||||
@return IMYRNViewController
|
||||
*/
|
||||
- (instancetype)initWithBundleName:(NSString *)bundleName;
|
||||
|
||||
/**
|
||||
加载本地bundle包
|
||||
|
||||
@param bundleName bundleName 本地的bundle名称
|
||||
@param modelName 页面名称
|
||||
@return IMYRNViewController
|
||||
*/
|
||||
- (instancetype)initWithBundleName:(NSString *)bundleName modelName:(NSString *)modelName;
|
||||
/**
|
||||
网络路径
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleURL;
|
||||
|
||||
/**
|
||||
读取的页面,默认是 index。
|
||||
*/
|
||||
@property(nonatomic,copy)NSString *moduleName;
|
||||
|
||||
@property(nonatomic,copy)NSString *navTitle;
|
||||
//本地包名
|
||||
@property(nonatomic,copy)NSString *bundleName;
|
||||
@property(nonatomic,strong)NSDictionary *initialProperties;/**< RN要用的参数:请求参数,埋点,自定义... */
|
||||
@property(nonatomic,assign)BOOL isNeedReloadPage;
|
||||
@property(nonatomic,strong,readonly)ASHRNView *rnView;
|
||||
@end
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// ASHRNViewController.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHRNViewController.h"
|
||||
#import "ASHRNView.h"
|
||||
#import "NSString+Util.h"
|
||||
#import <MBProgressHUD.h>
|
||||
@interface ASHRNViewController ()<ASHRNViewDelegate>
|
||||
@property(nonatomic,strong)ASHRNView *rnView;
|
||||
@property(nonatomic,strong)UIActivityIndicatorView *indicatorView;
|
||||
@end
|
||||
|
||||
@implementation ASHRNViewController
|
||||
|
||||
@synthesize bundleURL = _bundleURL;
|
||||
@synthesize moduleName = _moduleName;
|
||||
|
||||
- (instancetype)initWithBundleURL:(NSString *)bundleURL
|
||||
{
|
||||
NSDictionary* dic = [bundleURL ash_queryDictionary];
|
||||
NSString* model = dic[@"model"];
|
||||
if (!model.length) {
|
||||
model = @"index";
|
||||
}
|
||||
return [self initWithBundleURL:bundleURL modelName:model];
|
||||
}
|
||||
|
||||
- (instancetype)initWithBundleURL:(NSString *)bundleURL modelName:(NSString *)modelName
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_bundleURL = bundleURL;
|
||||
if (!modelName.length) {
|
||||
NSDictionary* dic = [bundleURL ash_queryDictionary];
|
||||
modelName = dic[@"model"];
|
||||
if (!modelName.length) {
|
||||
modelName = @"index";
|
||||
}
|
||||
}
|
||||
_moduleName = modelName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBundleName:(NSString *)bundleName
|
||||
{
|
||||
return [self initWithBundleName:bundleName modelName:@"index"];
|
||||
}
|
||||
- (instancetype)initWithBundleName:(NSString *)bundleName modelName:(NSString *)modelName
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_bundleName = bundleName;
|
||||
_moduleName = modelName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
[self setupRNView];
|
||||
[self setupCaptionView];
|
||||
}
|
||||
|
||||
- (void)setupRNView
|
||||
{
|
||||
self.rnView = [[ASHRNView alloc] initWithFrame:self.view.bounds];
|
||||
self.rnView.viewDelegate = self;
|
||||
self.rnView.initialProperties = self.initialProperties;
|
||||
self.rnView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[self.view addSubview:self.rnView];
|
||||
}
|
||||
|
||||
- (void)setupCaptionView
|
||||
{
|
||||
self.indicatorView = [UIActivityIndicatorView new];
|
||||
self.indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
if (self.navTitle) {
|
||||
self.navigationItem.title = self.navTitle;
|
||||
}
|
||||
|
||||
[self loadBundle];
|
||||
}
|
||||
|
||||
- (void)loadBundle
|
||||
{
|
||||
if (self.bundleURL) {
|
||||
[self.rnView loadBundle:self.bundleURL withModelName:self.moduleName];
|
||||
} else {
|
||||
[self.rnView loadBundleName:self.bundleName modelName:self.moduleName];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark IMYRNViewDelegate
|
||||
|
||||
- (void)rnViewDidCreated:(ASHRNView *)weexView
|
||||
{
|
||||
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
|
||||
}
|
||||
- (void)rnView:(ASHRNView *)rnView didFailed:(NSError *)error
|
||||
{
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
}
|
||||
- (void)rnViewDidRenderFinish:(ASHRNView *)rnView
|
||||
{
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ASHRNBridgeManagerImpl.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASHRNBridgeManagerProtocol.h"
|
||||
@interface ASHRNBridgeManagerImpl : NSObject<ASHRNBridgeManagerProtocol>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// ASHRNBridgeManagerImpl.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHRNBridgeManagerImpl.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
@interface ASHRNBridgeManagerImpl()
|
||||
@property(nonatomic, strong)NSMutableArray* bridgeArr;
|
||||
@end
|
||||
|
||||
@implementation ASHRNBridgeManagerImpl
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.bridgeArr = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RCTBridge*)bridgeWithBundleUrl:(NSURL *)bundleUrl launchOptions:(NSDictionary *)initialProperties
|
||||
{
|
||||
if (!bundleUrl) {
|
||||
return nil;
|
||||
}
|
||||
for (RCTBridge* obj in self.bridgeArr) {
|
||||
if ([[obj.bundleURL absoluteString] isEqualToString:[RCTConvert NSURL:bundleUrl.absoluteString].absoluteString]) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
RCTBridge* bridge = [[RCTBridge alloc] initWithBundleURL:bundleUrl
|
||||
moduleProvider:nil
|
||||
launchOptions:initialProperties];
|
||||
if (bridge) {
|
||||
[self.bridgeArr addObject:bridge];
|
||||
}
|
||||
return bridge;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ASHRNBundleManagerImpl.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASHRNBundleManagerProtocol.h"
|
||||
@interface ASHRNBundleManagerImpl : NSObject<ASHRNBundleManagerProtocol>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// ASHRNBundleManagerImpl.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHRNBundleManagerImpl.h"
|
||||
#import <ZipArchive.h>
|
||||
#import <AFNetworking.h>
|
||||
#import "ASHDownFileUtil.h"
|
||||
@interface ASHRNBundleManagerImpl()
|
||||
@property (nonatomic, copy) NSString *dirPath;
|
||||
@property (nonatomic, strong) NSMutableDictionary *callbackMaps;
|
||||
@end
|
||||
@implementation ASHRNBundleManagerImpl
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.callbackMaps = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)dirPath
|
||||
{
|
||||
if (!_dirPath) {
|
||||
_dirPath = [self createRNBundleDirectory];
|
||||
}
|
||||
return _dirPath;
|
||||
}
|
||||
|
||||
- (NSString *)createRNBundleDirectory
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *dirPath = [paths.firstObject stringByAppendingPathComponent:@"RNView"];
|
||||
BOOL isDir = NO;
|
||||
BOOL isCreated = [[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&isDir];
|
||||
if (!isCreated || !isDir) {
|
||||
NSError *error = nil;
|
||||
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
if (!success)
|
||||
{
|
||||
NSAssert(NO, @"create dir error: %@", error.localizedDescription);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return dirPath;
|
||||
}
|
||||
- (NSString *)bundlePathWithURLString:(NSString *)urlString
|
||||
{
|
||||
if (!urlString.length) {
|
||||
return nil;
|
||||
}
|
||||
NSString *lastPathComponent = [urlString componentsSeparatedByString:@"?"].firstObject.lastPathComponent.stringByDeletingPathExtension;
|
||||
NSString* zipPath = [urlString componentsSeparatedByString:@"?"].firstObject;
|
||||
NSString *fileName = [NSString stringWithFormat:@"%ld_%@",zipPath.hash,lastPathComponent];
|
||||
NSString *bundlePath = [self.dirPath stringByAppendingPathComponent:fileName];
|
||||
|
||||
return bundlePath;
|
||||
}
|
||||
- (BOOL)containBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
NSString *bundlePath = [self bundlePathWithURLString:bundleURL.absoluteString];
|
||||
if (bundlePath) {
|
||||
BOOL isDir = NO;
|
||||
BOOL hasExists = [[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:&isDir];
|
||||
return (hasExists && isDir);
|
||||
}
|
||||
else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)downloadWithBundleURL:(NSURL *)bundleURL completedBlock:(nullable void (^)(NSString *, NSString *, NSError * _Nullable))completedBlock
|
||||
{
|
||||
NSString *urlString = bundleURL.absoluteString;
|
||||
if (!urlString.length) {
|
||||
NSError* error = [NSError errorWithDomain:@"bundle包地址不能为空" code:0 userInfo:nil];
|
||||
if (completedBlock) {
|
||||
completedBlock(nil, nil, error);
|
||||
}
|
||||
// NSAssert(NO, @"bundleURL is nil!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
//文件夹地址
|
||||
NSString *bundlePath = [self bundlePathWithURLString:urlString];
|
||||
//index文件地址
|
||||
NSString *indexPath = [bundlePath stringByAppendingPathComponent:@"index.ios.bundle"];
|
||||
|
||||
|
||||
BOOL isDir = NO;
|
||||
BOOL hasExists = [[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:&isDir];
|
||||
BOOL hasIndexFile = [[NSFileManager defaultManager] fileExistsAtPath:indexPath];
|
||||
|
||||
if (hasExists && isDir && hasIndexFile) {
|
||||
if (completedBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completedBlock(indexPath, bundlePath, nil);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
@synchronized (self) {
|
||||
NSMutableArray *callbackBlocks = self.callbackMaps[urlString];
|
||||
if (!callbackBlocks) {
|
||||
callbackBlocks = [NSMutableArray array];
|
||||
self.callbackMaps[urlString] = callbackBlocks;
|
||||
}
|
||||
if (completedBlock) {
|
||||
[callbackBlocks addObject:[completedBlock copy]];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *zipPath = [bundlePath stringByAppendingPathExtension:@"zip"];
|
||||
|
||||
[ASHDownFileUtil downLoadWithUrl:urlString filePath:zipPath completedBlock:^(NSString * _Nonnull url, NSString * _Nonnull filePath) {
|
||||
NSError *error = nil;
|
||||
do{
|
||||
BOOL isUnzip = [self unzipFilePath:zipPath];
|
||||
if (!isUnzip) {
|
||||
error = [NSError errorWithDomain:[NSString stringWithFormat:@"文件解压 %@ 失败!", zipPath] code:0 userInfo:nil];
|
||||
break;
|
||||
}
|
||||
BOOL hasIndexFile = [[NSFileManager defaultManager] fileExistsAtPath:indexPath];
|
||||
if (!hasIndexFile) {
|
||||
error = [NSError errorWithDomain:[NSString stringWithFormat:@"未找到 index.ios.bundle 文件,%@ !", indexPath] code:0 userInfo:nil];
|
||||
break;
|
||||
}
|
||||
}while (0);
|
||||
|
||||
[self callbackWithBundleURLString:urlString indexPath:indexPath bundlePath:bundlePath error:error];
|
||||
|
||||
} failBlock:^(NSString * _Nonnull url, NSError * _Nonnull error) {
|
||||
[self callbackWithBundleURLString:urlString indexPath:indexPath bundlePath:bundlePath error:error];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)callbackWithBundleURLString:(NSString *)urlString indexPath:(NSString *)indexPath bundlePath:(NSString *)bundlePath error:(NSError *)error
|
||||
{
|
||||
NSMutableArray *callbackBlocks = nil;
|
||||
@synchronized (self) {
|
||||
callbackBlocks = self.callbackMaps[urlString];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
void (^completedBlock)(NSString *indexPath, NSString *bundlePath, NSError *error) = nil;
|
||||
for (completedBlock in callbackBlocks) {
|
||||
completedBlock(indexPath, bundlePath, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)unzipFilePath:(NSString *)zipPath
|
||||
{
|
||||
NSString *dirPath = [zipPath stringByDeletingPathExtension];
|
||||
ZipArchive* unzipArchive = [[ZipArchive alloc] initWithFileManager:[NSFileManager defaultManager]];
|
||||
[unzipArchive UnzipOpenFile:zipPath];
|
||||
BOOL isOK = [unzipArchive UnzipFileTo:dirPath overWrite:YES];
|
||||
[unzipArchive UnzipCloseFile];
|
||||
if (isOK) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:zipPath error:nil];
|
||||
}
|
||||
return isOK;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ASHRNViewFactoryImpl.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASHRNViewFactoryProtocol.h"
|
||||
@interface ASHRNViewFactoryImpl : NSObject<ASHRNViewFactoryProtocol>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// ASHRNViewFactoryImpl.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHRNViewFactoryImpl.h"
|
||||
#import "ASHRNViewController.h"
|
||||
@implementation ASHRNViewFactoryImpl
|
||||
- (UIViewController*)rnViewControllerWithBundleURL:(NSString *)bundleURL modelName:(NSString *)modelName
|
||||
{
|
||||
return [[ASHRNViewController alloc] initWithBundleURL:bundleURL modelName:modelName];
|
||||
}
|
||||
- (UIViewController*)rnViewControllerWithBundleName:(NSString *)bundleName modelName:(NSString *)modelName
|
||||
{
|
||||
return [[ASHRNViewController alloc] initWithBundleName:bundleName modelName:modelName];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// NSString+Util.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSString (Util)
|
||||
|
||||
- (NSDictionary*)ash_queryDictionary;
|
||||
@end
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// NSString+Util.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSString+Util.h"
|
||||
|
||||
@implementation NSString (Util)
|
||||
|
||||
- (NSDictionary*)ash_queryDictionary
|
||||
{
|
||||
NSArray *array = [self componentsSeparatedByString:@"?"];
|
||||
NSString *encodedString = array.lastObject;
|
||||
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
||||
NSArray *pairs = [encodedString componentsSeparatedByString:@"&"];
|
||||
|
||||
for (NSString *kvp in pairs) {
|
||||
if ([kvp length] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSRange pos = [kvp rangeOfString:@"="];
|
||||
NSString *key;
|
||||
NSString *val;
|
||||
|
||||
if (pos.location == NSNotFound) {
|
||||
key = [self ash_stringByUnescapingFromURLQuery:kvp];
|
||||
val = @"";
|
||||
} else {
|
||||
key = [self ash_stringByUnescapingFromURLQuery:[kvp substringToIndex:pos.location]];
|
||||
val = [self ash_stringByUnescapingFromURLQuery:[kvp substringFromIndex:pos.location + pos.length]];
|
||||
}
|
||||
|
||||
if (!key || !val) {
|
||||
continue; // I'm sure this will bite my arse one day
|
||||
}
|
||||
|
||||
[result setObject:val forKey:key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
- (NSString *)ash_stringByUnescapingFromURLQuery:(NSString *)string
|
||||
{
|
||||
NSString *deplussed = [string stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
return [deplussed stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// ASHRNBridgeManagerProtocol.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class RCTBridge;
|
||||
@protocol ASHRNBridgeManagerProtocol <NSObject>
|
||||
|
||||
|
||||
/**
|
||||
获取RCTBridge
|
||||
@param bundleUrl 包地址
|
||||
@param initialProperties 初始化参数
|
||||
@return RCTBridge
|
||||
*/
|
||||
- (RCTBridge* _Nullable)bridgeWithBundleUrl:(NSURL* _Nonnull)bundleUrl launchOptions:(NSDictionary* _Nullable)initialProperties;
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// ASHRNBundleManagerProtocol.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ASHRNBundleManagerProtocol <NSObject>
|
||||
- (BOOL)containBundleURL:(NSURL *)bundleURL;
|
||||
- (void)downloadWithBundleURL:(NSURL *)bundleURL completedBlock:(nullable void(^)(NSString *indexPath, NSString *bundlePath,NSError * _Nullable error))completedBlock;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// ASHRNViewFactoryProtocol.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ASHRNViewFactoryProtocol <NSObject>
|
||||
|
||||
/**
|
||||
加载网络地址的包
|
||||
|
||||
@param bundleURL 包地址
|
||||
@param modelName 页面名称
|
||||
@return 返回页面VC
|
||||
*/
|
||||
- (UIViewController*)rnViewControllerWithBundleURL:(NSString *)bundleURL modelName:(NSString *)modelName;
|
||||
|
||||
|
||||
/**
|
||||
加载本地包
|
||||
|
||||
@param bundleName 包名称
|
||||
@param modelName 页面名称
|
||||
@return 返回页面VC
|
||||
*/
|
||||
- (UIViewController *)rnViewControllerWithBundleName:(NSString *)bundleName modelName:(nullable NSString *)modelName;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// RCTImageCache+ASHCache.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RCTImageCache.h"
|
||||
|
||||
|
||||
/**
|
||||
设置图片缓存,自定义
|
||||
*/
|
||||
@interface RCTImageCache (ASHCache)
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// RCTImageCache+ASHCache.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/10.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RCTImageCache+ASHCache.h"
|
||||
#import <SDImageCache.h>
|
||||
@implementation RCTImageCache (ASHCache)
|
||||
|
||||
- (void)addImageToCache:(UIImage *)image
|
||||
URL:(NSString *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
responseDate:(NSString *)responseDate
|
||||
{
|
||||
[[SDImageCache sharedImageCache] storeImage:image forKey:url completion:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)imageForUrl:(NSString *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
responseDate:(NSString *)responseDate;
|
||||
{
|
||||
UIImage* image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:url];
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// ASHDownFileUtil.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASHDownFileUtil : NSObject
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
下载文件
|
||||
|
||||
@param url 文件地址
|
||||
@param filePath 保存地址
|
||||
@param completedBlock 成功回调
|
||||
@param failBlock 失败回调
|
||||
*/
|
||||
+(void)downLoadWithUrl:( NSString* )url filePath:(NSString*)filePath completedBlock:(void(^)(NSString* url, NSString*filePath))completedBlock failBlock:(void(^)(NSString* url, NSError* error))failBlock;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// ASHDownFileUtil.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHDownFileUtil.h"
|
||||
#import <AFNetworking.h>
|
||||
@implementation ASHDownFileUtil
|
||||
|
||||
+(void)downLoadWithUrl:(NSString *)url filePath:(NSString *)filePath completedBlock:(void (^)(NSString * _Nonnull, NSString * _Nonnull))completedBlock failBlock:(void (^)(NSString * _Nonnull, NSError * _Nonnull))failBlock
|
||||
{
|
||||
if (!url || !url.length) {
|
||||
if (failBlock) {
|
||||
failBlock(@"", [NSError errorWithDomain:@"下载地址为空" code:0 userInfo:nil]);
|
||||
}
|
||||
}
|
||||
AFHTTPSessionManager* sessionManager = [AFHTTPSessionManager manager];
|
||||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
|
||||
NSURLSessionDownloadTask* task = [sessionManager downloadTaskWithRequest:request progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
|
||||
// return [NSURL fileURLWithPath:filePath];
|
||||
return [NSURL URLWithString:filePath];
|
||||
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
if (completedBlock) {
|
||||
completedBlock(url, [filePath absoluteString]);
|
||||
}
|
||||
}else{
|
||||
if (failBlock) {
|
||||
failBlock(url, error);
|
||||
}
|
||||
}
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// ASHRNView.h
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class ASHRNView, RCTRootView;
|
||||
@protocol ASHRNViewDelegate <NSObject>
|
||||
@optional
|
||||
- (void)rnViewDidCreated:(ASHRNView *)rnView;
|
||||
|
||||
- (void)rnView:(ASHRNView *)rnView didFailed:(NSError *)error;
|
||||
|
||||
- (void)rnViewDidRenderFinish:(ASHRNView *)rnView;
|
||||
|
||||
@end
|
||||
@interface ASHRNView : UIView
|
||||
|
||||
#pragma mark method
|
||||
/**
|
||||
网络路径,默认加载 model: index.
|
||||
|
||||
@param bundleUrl 包地址
|
||||
*/
|
||||
- (void)loadBundle:(NSString*)bundleUrl;
|
||||
|
||||
/**
|
||||
网络路径
|
||||
|
||||
@param bundleUrl bundleUrl 包地址
|
||||
@param modelName 页面名称
|
||||
*/
|
||||
- (void)loadBundle:(NSString*)bundleUrl withModelName:(NSString*)modelName;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
加载本地包
|
||||
|
||||
@param bundleName 本地包名称
|
||||
*/
|
||||
- (void)loadBundleName:(NSString*)bundleName;
|
||||
|
||||
|
||||
/**
|
||||
加载本地包
|
||||
|
||||
@param bundleName 本地包名称
|
||||
@param modelName 页面名称
|
||||
*/
|
||||
- (void)loadBundleName:(NSString*)bundleName modelName:(NSString*)modelName;
|
||||
|
||||
|
||||
///刷新
|
||||
- (void)reloadPage;
|
||||
- (void)reloadPageWithProperties:(NSDictionary *)params;
|
||||
|
||||
#pragma mark property
|
||||
|
||||
/**
|
||||
通过url上获取的真实宽度
|
||||
*/
|
||||
@property (nonatomic, assign, readonly)CGFloat realWidth;
|
||||
/**
|
||||
通过url上获取的真实高度
|
||||
*/
|
||||
@property (nonatomic, assign ,readonly)CGFloat realHeigh;
|
||||
/**
|
||||
包的网络地址
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleURL;
|
||||
/**
|
||||
页面名称
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *modelName;
|
||||
|
||||
|
||||
/**
|
||||
RN RootView
|
||||
*/
|
||||
@property (nonatomic, strong, readonly)RCTRootView *rootView;
|
||||
|
||||
/**
|
||||
index.ios.bundle地址
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *indexPath;
|
||||
|
||||
/**
|
||||
包解压之后文件夹地址
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *bundlePath;
|
||||
|
||||
/**
|
||||
初始化参数
|
||||
*/
|
||||
@property (nonatomic, copy) NSDictionary* initialProperties;
|
||||
|
||||
@property(nonatomic,weak) id<ASHRNViewDelegate> viewDelegate;
|
||||
@end
|
||||
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// ASHRNView.m
|
||||
// ASHReactNative
|
||||
//
|
||||
// Created by xmfish on 17/4/7.
|
||||
// Copyright © 2017年 ash. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASHRNView.h"
|
||||
#import "NSString+Util.h"
|
||||
#import "ASHReactNative.h"
|
||||
#import <MBProgressHUD.h>
|
||||
#import <RCTBridge.h>
|
||||
#import <RCTRootView.h>
|
||||
@interface ASHRNView()
|
||||
@property (nonatomic, copy) NSString *bundlePath;
|
||||
@property (nonatomic, copy) NSString *indexPath;
|
||||
@property (nonatomic, copy) NSString *modelName;
|
||||
@property (nonatomic, strong)RCTRootView* rootView;
|
||||
|
||||
@property (nonatomic, assign)CGFloat realWidth;
|
||||
@property (nonatomic, assign)CGFloat realHeigh;
|
||||
@end
|
||||
@implementation ASHRNView
|
||||
@synthesize bundleURL = _bundleURL;
|
||||
@synthesize indexPath = _indexPath;
|
||||
@synthesize modelName = _modelName;
|
||||
RCT_EXTERN void RCTRegisterModule(Class);
|
||||
|
||||
+ (NSString *)moduleName {
|
||||
return @"ASHRNView";
|
||||
}
|
||||
|
||||
- (void)loadBundle:(NSString *)bundleUrl
|
||||
{
|
||||
NSDictionary* dic = [bundleUrl ash_queryDictionary];
|
||||
NSString* model = dic[@"model"];
|
||||
if (!model.length) {
|
||||
model = @"index";
|
||||
}
|
||||
[self loadBundle:bundleUrl withModelName:model];
|
||||
}
|
||||
- (void)loadBundle:(NSString *)bundleUrl withModelName:(NSString *)modelName
|
||||
{
|
||||
if (![_bundleURL isEqualToString:bundleUrl] || ![_modelName isEqualToString:modelName]) {
|
||||
|
||||
NSMutableDictionary* dic = [NSMutableDictionary dictionaryWithDictionary:[bundleUrl ash_queryDictionary]];
|
||||
if([dic objectForKey:@"width"]){
|
||||
self.realWidth = [[dic objectForKey:@"width"] floatValue];
|
||||
}
|
||||
if([dic objectForKey:@"heigh"]){
|
||||
self.realHeigh = [[dic objectForKey:@"heigh"] floatValue];
|
||||
}
|
||||
if (self.initialProperties) {
|
||||
[dic addEntriesFromDictionary:self.initialProperties];
|
||||
}
|
||||
self.initialProperties = dic;
|
||||
if (!modelName.length) {
|
||||
modelName = dic[@"model"];
|
||||
if (!modelName.length) {
|
||||
modelName = @"index";
|
||||
}
|
||||
}
|
||||
_bundleURL = bundleUrl;
|
||||
_modelName = modelName;
|
||||
[self refreshRNView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadBundleName:(NSString *)bundleName
|
||||
{
|
||||
[self loadBundleName:bundleName modelName:@"index"];
|
||||
}
|
||||
- (void)loadBundleName:(NSString *)bundleName modelName:(NSString *)modelName
|
||||
{
|
||||
if (![self.indexPath isEqualToString:bundleName] || ![_modelName isEqualToString:modelName]) {
|
||||
self.indexPath = bundleName;
|
||||
self.modelName = modelName;
|
||||
[self refreshRNView];
|
||||
}
|
||||
}
|
||||
- (void)refreshRNView
|
||||
{
|
||||
[self loadRNView];
|
||||
}
|
||||
- (void)loadRNView
|
||||
{
|
||||
if([self.viewDelegate respondsToSelector:@selector(rnViewDidCreated:)]){
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.viewDelegate rnViewDidCreated:self];
|
||||
});
|
||||
|
||||
}
|
||||
if (self.bundleURL) {
|
||||
__weak typeof(self) wself = self;
|
||||
NSURL *URL = [NSURL URLWithString:self.bundleURL];
|
||||
[[ASHReactNative shareInstance].bundleManager downloadWithBundleURL:URL completedBlock:^(NSString *indexPath, NSString *bundlePath, NSError *error) {
|
||||
__strong typeof(self) sself = wself;
|
||||
if (error) {
|
||||
[self delegateError:error];
|
||||
} else {
|
||||
sself.bundlePath = bundlePath;
|
||||
sself.indexPath = indexPath;
|
||||
[sself loadRNWithIndexPath:self.indexPath];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self loadRNWithIndexPath:self.indexPath];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadRNWithIndexPath:(NSString*)indexPath
|
||||
{
|
||||
|
||||
if (!indexPath.length) {
|
||||
[self delegateError:[NSError errorWithDomain:@"nil path" code:-9999 userInfo:@{@"msg":@"nil path"}]];
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL* bundleUrl = [NSURL URLWithString:indexPath];
|
||||
RCTBridge *bridge = [[ASHReactNative shareInstance].rnbridgeFactory bridgeWithBundleUrl:bundleUrl launchOptions:nil];
|
||||
|
||||
if(!bridge.isValid){
|
||||
[self delegateError:[NSError errorWithDomain:@"bridge invalid" code:-9999 userInfo:@{@"msg":@"bridge invalid"}]];
|
||||
return;
|
||||
}
|
||||
if (!self.modelName) {
|
||||
self.modelName = @"index";
|
||||
}
|
||||
RCTRootView* rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:self.modelName initialProperties:self.initialProperties];
|
||||
rootView.frame = self.bounds;
|
||||
self.rootView = rootView;
|
||||
[self addSubview:rootView];
|
||||
if ([self.viewDelegate respondsToSelector:@selector(rnViewDidRenderFinish:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.viewDelegate rnViewDidRenderFinish:self];
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)delegateError:(NSError*)error
|
||||
{
|
||||
if ([self.viewDelegate respondsToSelector:@selector(rnView:didFailed:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.viewDelegate rnView:self didFailed:error];
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
#pragma mark - 刷新RN页面
|
||||
- (void)reloadPage {
|
||||
[self reloadPageWithProperties:nil];
|
||||
}
|
||||
- (void)reloadPageWithProperties:(NSDictionary *)params {
|
||||
self.initialProperties = params;
|
||||
[self refreshRNView];
|
||||
}
|
||||
@end
|
||||
Reference in New Issue
Block a user