Added support for URLs pointing to files inside application home

This commit is contained in:
Nick Lockwood 2015-07-13 10:33:39 -07:00
Родитель d1b14ef062
Коммит 90dd7a13f0
4 изменённых файлов: 83 добавлений и 46 удалений

Просмотреть файл

@ -15,6 +15,7 @@
#import <XCTest/XCTest.h>
#import "RCTConvert.h"
#import "RCTUtils.h"
@interface RCTConvert_NSURLTests : XCTestCase
@ -42,7 +43,7 @@ TEST_PATH(name, _input, [[[NSBundle mainBundle] bundlePath] stringByAppendingPat
TEST_URL(basic, @"http://example.com", @"http://example.com")
TEST_URL(null, (id)kCFNull, nil)
// Local files
// Resource files
TEST_PATH(fileURL, @"file:///blah/hello.jsbundle", @"/blah/hello.jsbundle")
TEST_BUNDLE_PATH(filePath, @"blah/hello.jsbundle", @"blah/hello.jsbundle")
TEST_BUNDLE_PATH(filePathWithSpaces, @"blah blah/hello.jsbundle", @"blah blah/hello.jsbundle")
@ -50,6 +51,9 @@ TEST_BUNDLE_PATH(filePathWithEncodedSpaces, @"blah%20blah/hello.jsbundle", @"bla
TEST_BUNDLE_PATH(imageAt2XPath, @"images/foo@2x.jpg", @"images/foo@2x.jpg")
TEST_BUNDLE_PATH(imageFile, @"foo.jpg", @"foo.jpg")
// User documents
TEST_PATH(documentsFolder, @"~/Documents", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])
// Remote files
TEST_URL(fullURL, @"http://example.com/blah/hello.jsbundle", @"http://example.com/blah/hello.jsbundle")
TEST_URL(urlWithSpaces, @"http://example.com/blah blah/foo", @"http://example.com/blah%20blah/foo")
@ -57,4 +61,12 @@ TEST_URL(urlWithEncodedSpaces, @"http://example.com/blah%20blah/foo", @"http://e
TEST_URL(imageURL, @"http://example.com/foo@2x.jpg", @"http://example.com/foo@2x.jpg")
TEST_URL(imageURLWithSpaces, @"http://example.com/blah foo@2x.jpg", @"http://example.com/blah%20foo@2x.jpg")
// Data URLs
- (void)testDataURL
{
NSURL *expectedURL = RCTDataURL(@"text/plain", [@"abcde" dataUsingEncoding:NSUTF8StringEncoding]);
NSURL *testURL = [NSURL URLWithString:@"data:text/plain;base64,YWJjZGU="];
XCTAssertEqualObjects([testURL absoluteString], [expectedURL absoluteString]);
}
@end

Просмотреть файл

@ -107,7 +107,11 @@ RCT_CONVERTER(NSString *, NSString, description)
// Assume that it's a local path
path = [path stringByRemovingPercentEncoding];
if (![path isAbsolutePath]) {
if ([path hasPrefix:@"~"]) {
// Path is inside user directory
path = [path stringByExpandingTildeInPath];
} else if (![path isAbsolutePath]) {
// Assume it's a resource path
path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:path];
}
return [NSURL fileURLWithPath:path];
@ -652,43 +656,54 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
return nil;
}
if (RCT_DEBUG && ![json isKindOfClass:[NSString class]] && ![json isKindOfClass:[NSDictionary class]]) {
RCTLogConvertError(json, "an image");
return nil;
}
UIImage *image;
NSString *path;
CGFloat scale = 0.0;
if ([json isKindOfClass:[NSString class]]) {
if ([json length] == 0) {
return nil;
}
path = json;
} else {
} else if ([json isKindOfClass:[NSDictionary class]]) {
path = [self NSString:json[@"uri"]];
scale = [self CGFloat:json[@"scale"]];
} else {
RCTLogConvertError(json, "an image");
}
if ([path hasPrefix:@"data:"]) {
NSURL *url = [NSURL URLWithString:path];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image = [UIImage imageWithData:imageData];
} else if ([path isAbsolutePath] || [path hasPrefix:@"~"]) {
image = [UIImage imageWithContentsOfFile:path.stringByExpandingTildeInPath];
} else {
image = [UIImage imageNamed:path];
if (!image) {
image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:path ofType:nil]];
NSURL *URL = [self NSURL:path];
NSString *scheme = [URL.scheme lowercaseString];
if ([scheme isEqualToString:@"file"]) {
if ([NSThread currentThread] == [NSThread mainThread]) {
// Image may reside inside a .car file, in which case we have no choice
// but to use +[UIImage imageNamed] - but this method isn't thread safe
image = [UIImage imageNamed:path];
}
if (!image) {
// Attempt to load from the file system
if ([path pathExtension].length == 0) {
path = [path stringByAppendingPathExtension:@"png"];
}
image = [UIImage imageWithContentsOfFile:path];
}
// We won't warn about nil images because there are legitimate cases
// where we find out if a string is an image by using this method, but
// we do enforce thread-safe API usage with the following check
if (RCT_DEBUG && !image && [UIImage imageNamed:path]) {
RCTAssertMainThread();
}
} else if ([scheme isEqualToString:@"data"]) {
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
} else {
RCTLogConvertError(json, "an image. Only local files or data URIs are supported");
}
if (scale > 0) {
image = [UIImage imageWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
image = [UIImage imageWithCGImage:image.CGImage
scale:scale
orientation:image.imageOrientation];
}
// NOTE: we don't warn about nil images because there are legitimate
// case where we find out if a string is an image by using this method
return image;
}

Просмотреть файл

@ -47,6 +47,7 @@ RCT_EXTERN BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);
// Creates a standardized error object
RCT_EXTERN NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData);
RCT_EXTERN NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData);
RCT_EXTERN NSDictionary *RCTJSErrorFromNSError(NSError *error);
// Returns YES if React is running in a test environment
RCT_EXTERN BOOL RCTRunningInTestEnvironment(void);
@ -58,7 +59,8 @@ RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image);
RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);
// Convert nil values to NSNull, and vice-versa
RCT_EXTERN id RCTNullIfNil(id value);
RCT_EXTERN id RCTNilIfNull(id value);
RCT_EXTERN id RCTNullIfNil(id value);
RCT_EXTERN NSDictionary *RCTJSErrorFromNSError(NSError *error);
// Convert data to a Base64-encoded data URL
RCT_EXTERN NSURL *RCTDataURL(NSString *mimeType, NSData *data);

Просмотреть файл

@ -238,6 +238,27 @@ NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary
return error;
}
// TODO: Can we just replace RCTMakeError with this function instead?
NSDictionary *RCTJSErrorFromNSError(NSError *error)
{
NSString *errorMessage;
NSArray *stackTrace = [NSThread callStackSymbols];
NSMutableDictionary *errorInfo =
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
if (error) {
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
errorInfo[@"code"] = @(error.code);
} else {
errorMessage = @"Unknown error from a native module";
errorInfo[@"domain"] = RCTErrorDomain;
errorInfo[@"code"] = @-1;
}
return RCTMakeError(errorMessage, nil, errorInfo);
}
BOOL RCTRunningInTestEnvironment(void)
{
static BOOL isTestEnvironment = NO;
@ -277,23 +298,10 @@ id RCTNilIfNull(id value)
return value == (id)kCFNull ? nil : value;
}
// TODO: Can we just replace RCTMakeError with this function instead?
NSDictionary *RCTJSErrorFromNSError(NSError *error)
NSURL *RCTDataURL(NSString *mimeType, NSData *data)
{
NSString *errorMessage;
NSArray *stackTrace = [NSThread callStackSymbols];
NSMutableDictionary *errorInfo =
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
if (error) {
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
errorInfo[@"code"] = @(error.code);
} else {
errorMessage = @"Unknown error from a native module";
errorInfo[@"domain"] = RCTErrorDomain;
errorInfo[@"code"] = @-1;
}
return RCTMakeError(errorMessage, nil, errorInfo);
return [NSURL URLWithString:
[NSString stringWithFormat:@"data:%@;base64,%@", mimeType,
[data base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]]];
}