From 1959ea1e4b945e3a30a71759a2c677617b89463d Mon Sep 17 00:00:00 2001 From: Aevit Date: Thu, 30 Nov 2017 15:32:18 +0800 Subject: [PATCH] add missing image fix tool for iOS --- ios/RNKitCodePush.xcodeproj/project.pbxproj | 20 ++++ .../RCTConvert+rnkit_missingImage.h | 24 +++++ .../RCTConvert+rnkit_missingImage.m | 99 +++++++++++++++++++ .../RNKitMissingImageFixToolBridge.h | 18 ++++ .../RNKitMissingImageFixToolBridge.m | 24 +++++ ios/RNKitCodePush/RNKitCodePush.h | 4 + ios/RNKitCodePush/RNKitCodePush.m | 4 + lib/index.js | 27 +++++ 8 files changed, 220 insertions(+) create mode 100644 ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.h create mode 100644 ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.m create mode 100644 ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.h create mode 100644 ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.m diff --git a/ios/RNKitCodePush.xcodeproj/project.pbxproj b/ios/RNKitCodePush.xcodeproj/project.pbxproj index d32c3fb..ec3ad18 100644 --- a/ios/RNKitCodePush.xcodeproj/project.pbxproj +++ b/ios/RNKitCodePush.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 9F394D881C7C25DC00C794C0 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D761C7C25DC00C794C0 /* unzip.c */; }; 9F394D891C7C25DC00C794C0 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D781C7C25DC00C794C0 /* zip.c */; }; 9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */; }; + E1162A861FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = E1162A841FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.m */; }; + E1162A871FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.m in Sources */ = {isa = PBXBuildFile; fileRef = E1162A851FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -90,6 +92,10 @@ 9F394D7A1C7C25DC00C794C0 /* SSZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSZipArchive.h; sourceTree = ""; }; 9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSZipArchive.m; sourceTree = ""; }; 9F394D7C1C7C25DC00C794C0 /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipArchive.h; sourceTree = ""; }; + E1162A821FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+rnkit_missingImage.h"; sourceTree = ""; }; + E1162A831FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKitMissingImageFixToolBridge.h; sourceTree = ""; }; + E1162A841FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNKitMissingImageFixToolBridge.m; sourceTree = ""; }; + E1162A851FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+rnkit_missingImage.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -122,6 +128,7 @@ 91C5F0011C76ECA90037E727 /* RNKitCodePush */ = { isa = PBXGroup; children = ( + E1162A811FCFE99D005B2A29 /* MissingImageFixTool */, 9F1BCB381CAF6B3E000EF2CB /* BSDiff */, 9F394D571C7C25DC00C794C0 /* SSZipArchive */, 3F50BE3A1E812F8E003A6F64 /* RNKitCodePush.h */, @@ -213,6 +220,17 @@ path = minizip; sourceTree = ""; }; + E1162A811FCFE99D005B2A29 /* MissingImageFixTool */ = { + isa = PBXGroup; + children = ( + E1162A821FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.h */, + E1162A851FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.m */, + E1162A831FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.h */, + E1162A841FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.m */, + ); + path = MissingImageFixTool; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -273,6 +291,7 @@ 9F394D811C7C25DC00C794C0 /* fileenc.c in Sources */, 9F394D871C7C25DC00C794C0 /* mztools.c in Sources */, 9F394D821C7C25DC00C794C0 /* hmac.c in Sources */, + E1162A861FCFE99D005B2A29 /* RNKitMissingImageFixToolBridge.m in Sources */, 9F394D881C7C25DC00C794C0 /* unzip.c in Sources */, 9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */, 3F50BE401E812F8E003A6F64 /* RNKitCodePush.m in Sources */, @@ -283,6 +302,7 @@ 9F394D801C7C25DC00C794C0 /* entropy.c in Sources */, 9F394D831C7C25DC00C794C0 /* prng.c in Sources */, 9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */, + E1162A871FCFE99D005B2A29 /* RCTConvert+rnkit_missingImage.m in Sources */, 9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */, 9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */, 9F394D891C7C25DC00C794C0 /* zip.c in Sources */, diff --git a/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.h b/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.h new file mode 100644 index 0000000..03f56e6 --- /dev/null +++ b/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.h @@ -0,0 +1,24 @@ +// +// RCTConvert+rnkit_missingImage.h +// RNKitCodePush +// +// Created by aevit on 2017/11/30. +// Copyright © 2017年 erica. All rights reserved. +// + +#import + +/** + sometimes when patch completed, some images are missing, until 2017.11.30, still don't know why. + so, use swizzle to solve this problem in the other way: if the image doesn't exist in the sanbox, search the bundle. + + @param _assetsPath the ReactNative Framework will put the images to directory "assets" in the main bundle by default, but when you change to the different directory, remember that the parameter "assetPath" should change too. + @return + */ +void installMissingImageFixTool(NSString *_assetsPath); + +/** + swizzle origin methods + */ +void uninstallMissingImageFixTool(); + diff --git a/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.m b/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.m new file mode 100644 index 0000000..11c3fbc --- /dev/null +++ b/ios/RNKitCodePush/MissingImageFixTool/RCTConvert+rnkit_missingImage.m @@ -0,0 +1,99 @@ +// +// RCTConvert+rnkit_missingImage.m +// RNKitCodePush +// +// Created by aevit on 2017/11/30. +// Copyright © 2017年 erica. All rights reserved. +// + +#import "RCTConvert+rnkit_missingImage.h" +#import +#import +#import "RNKitCodePush.h" + +@implementation RCTConvert (rnkit_missingImage) +static BOOL sc_installed = NO; +static NSString *sc_assetsPath = nil; + +#pragma mark ---------- public methods +void installMissingImageFixTool(NSString *_assetsPath) { + dispatch_semaphore_t lock = dispatch_semaphore_create(1); + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + if (!sc_installed) { + sc_installed = YES; + sc_assetsPath = _assetsPath ?: @"assets/"; + swizzleRCTImageSourceMethod(); + } + dispatch_semaphore_signal(lock); +} + +void uninstallMissingImageFixTool() { + dispatch_semaphore_t lock = dispatch_semaphore_create(1); + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + if (sc_installed) { + sc_installed = NO; + sc_assetsPath = nil; + swizzleRCTImageSourceMethod(); + } + dispatch_semaphore_signal(lock); +} + +#pragma mark - private methods +void swizzleRCTImageSourceMethod() { + + Class class = object_getClass((id)[RCTConvert class]); + + SEL originalSel = sel_registerName("RCTImageSource:"); + SEL swizzleSel = sel_registerName("swizzleRCTImageSource:"); + + Method originalMethod = class_getClassMethod(class, originalSel); + Method swizzleMethod = class_getClassMethod(class, swizzleSel); + + BOOL success = class_addMethod(class, originalSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + if (success) { + class_replaceMethod(class, swizzleSel, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzleMethod); + } +} + ++ (RCTImageSource *)swizzleRCTImageSource:(id)json { + if ([RNKitCodePush hasUpdateInfo] && json && [json isKindOfClass:[NSDictionary class]] && json[@"uri"] && [json[@"uri"] hasPrefix:@"file://"]) { + NSString *path = json[@"uri"]; + if (![[self class] isExistInRNKitCodePush:path]) { + path = [[self class] convertToBundlePath:path]; + if (path) { + NSMutableDictionary *aDict = [NSMutableDictionary dictionaryWithDictionary:json]; + aDict[@"uri"] = [NSString stringWithFormat:@"file://%@", path]; + return [RCTConvert swizzleRCTImageSource:aDict]; + } + } + } + return [RCTConvert swizzleRCTImageSource:json]; +} + ++ (BOOL)isExistInRNKitCodePush:(NSString*)uri { + NSString *path = [uri stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + NSRange range = [path rangeOfString:@"rnkitcodepush/"]; + if (range.location != NSNotFound) { + path = [NSString stringWithFormat:@"%@/%@", [RNKitCodePush downloadDir], [path substringFromIndex:(range.location + range.length)]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + return YES; + } + } + return NO; +} + ++ (NSString*)convertToBundlePath:(NSString*)uri { + NSString *path = [uri stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + NSRange range = [path rangeOfString:sc_assetsPath]; + if (range.location != NSNotFound) { + path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], [path substringFromIndex:range.location]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + return path; + } + } + return nil; +} + +@end diff --git a/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.h b/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.h new file mode 100644 index 0000000..72976ed --- /dev/null +++ b/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.h @@ -0,0 +1,18 @@ +// +// RNKitMissingImageFixToolBridge.h +// RNKitCodePush +// +// Created by aevit on 2017/11/30. +// Copyright © 2017年 erica. All rights reserved. +// + +#import +#if __has_include() +#import +#else +#import "RCTBridgeModule.h" +#endif + +@interface RNKitMissingImageFixToolBridge : NSObject + +@end diff --git a/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.m b/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.m new file mode 100644 index 0000000..3e1c3a8 --- /dev/null +++ b/ios/RNKitCodePush/MissingImageFixTool/RNKitMissingImageFixToolBridge.m @@ -0,0 +1,24 @@ +// +// RNKitMissingImageFixToolBridge.m +// RNKitCodePush +// +// Created by aevit on 2017/11/30. +// Copyright © 2017年 erica. All rights reserved. +// + +#import "RNKitMissingImageFixToolBridge.h" +#import "RCTConvert+rnkit_missingImage.h" + +@implementation RNKitMissingImageFixToolBridge + +RCT_EXPORT_MODULE(RNKitMissingImageFixToolBridge); + +RCT_EXPORT_METHOD(installMissingImageFixTool:(NSString*)assetsPath) { + installMissingImageFixTool(assetsPath); +} + +RCT_EXPORT_METHOD(uninstallMissingImageFixTool) { + uninstallMissingImageFixTool(); +} + +@end diff --git a/ios/RNKitCodePush/RNKitCodePush.h b/ios/RNKitCodePush/RNKitCodePush.h index 77c3258..6b96b9d 100644 --- a/ios/RNKitCodePush/RNKitCodePush.h +++ b/ios/RNKitCodePush/RNKitCodePush.h @@ -16,4 +16,8 @@ + (NSURL *)bundleURL; ++ (BOOL)hasUpdateInfo; + ++ (NSString *)downloadDir; + @end diff --git a/ios/RNKitCodePush/RNKitCodePush.m b/ios/RNKitCodePush/RNKitCodePush.m index e298e6e..0469015 100644 --- a/ios/RNKitCodePush/RNKitCodePush.m +++ b/ios/RNKitCodePush/RNKitCodePush.m @@ -136,6 +136,10 @@ + (NSURL *)bundleURL return [RNKitCodePush binaryBundleURL]; } ++ (BOOL)hasUpdateInfo { + return [[NSUserDefaults standardUserDefaults] dictionaryForKey:keyUpdateInfo] ? YES : NO; +} + - (NSDictionary *)constantsToExport { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; diff --git a/lib/index.js b/lib/index.js index 4c7ec7c..6117916 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,6 +5,11 @@ * @author SimMan * @copyright 2011-2017 RNKit.io, Inc. */ +import { + NativeModules, + Platform +} from 'react-native' + const RNKitCodePushModule = require('react-native').NativeModules.RNKitCodePush let apiHost = 'https://update.rnkit.io/api/v1' @@ -45,6 +50,28 @@ export default class RNKitCodePush { return RNKitCodePushModule.deviceInfo } + /** + * sometimes when patch completed, some images are missing, until 2017.11.30, still don't know why. + * so in iOS, use swizzle to solve this problem in the other way: if the image doesn't exist in the sanbox, search the bundle. + * + * @param {any} assetPath the ReactNative Framework will put the images to directory "assets" in the main bundle by default, + * but when you change to the different directory, remember that the parameter "assetPath" should change too. + * @memberof RNKitCodePush + */ + installMissingImageFixTool(assetPath) { + const { RNKitMissingImageFixToolBridge } = NativeModules + if (Platform.OS === 'ios' && RNKitMissingImageFixToolBridge && RNKitMissingImageFixToolBridge.installMissingImageFixTool) { + RNKitMissingImageFixToolBridge.installMissingImageFixTool(assetPath ? assetPath : 'assets/') + } + } + + uninstallMissingImageFixTool() { + const { RNKitMissingImageFixToolBridge } = NativeModules + if (Platform.OS === 'ios' && RNKitMissingImageFixToolBridge && RNKitMissingImageFixToolBridge.uninstallMissingImageFixTool) { + RNKitMissingImageFixToolBridge.uninstallMissingImageFixTool() + } + } + // check update from service async checkUpdate () { const resp = await fetch(`${this.host}/update/check`, {