Apple TV support 1: existing Objective C code should compile for tvOS

Summary:
First commit for Apple TV support: changes to existing Objective-C code so that it will compile correctly for tvOS.
Closes https://github.com/facebook/react-native/pull/9649

Differential Revision: D3916021

Pulled By: javache

fbshipit-source-id: 34acc9daf3efff835ffe38c43ba5d4098a02c830
This commit is contained in:
Douglas Lowder
2016-09-27 06:19:45 -07:00
committed by Facebook Github Bot 5
parent 339531065f
commit d368ebfab2
29 changed files with 197 additions and 142 deletions

View File

@@ -12,6 +12,14 @@
#import "RCTBridgeModule.h"
#import "RCTInvalidating.h"
typedef NS_ENUM(NSInteger, RCTAlertViewStyle) {
RCTAlertViewStyleDefault = 0,
RCTAlertViewStyleSecureTextInput,
RCTAlertViewStylePlainTextInput,
RCTAlertViewStyleLoginAndPasswordInput
};
@interface RCTAlertManager : NSObject <RCTBridgeModule, RCTInvalidating>
@end

View File

@@ -16,22 +16,21 @@
@implementation RCTConvert (UIAlertViewStyle)
RCT_ENUM_CONVERTER(UIAlertViewStyle, (@{
@"default": @(UIAlertViewStyleDefault),
@"secure-text": @(UIAlertViewStyleSecureTextInput),
@"plain-text": @(UIAlertViewStylePlainTextInput),
@"login-password": @(UIAlertViewStyleLoginAndPasswordInput),
}), UIAlertViewStyleDefault, integerValue)
RCT_ENUM_CONVERTER(RCTAlertViewStyle, (@{
@"default": @(RCTAlertViewStyleDefault),
@"secure-text": @(RCTAlertViewStyleSecureTextInput),
@"plain-text": @(RCTAlertViewStylePlainTextInput),
@"login-password": @(RCTAlertViewStyleLoginAndPasswordInput),
}), RCTAlertViewStyleDefault, integerValue)
@end
@interface RCTAlertManager() <UIAlertViewDelegate>
@interface RCTAlertManager()
@end
@implementation RCTAlertManager
{
NSMutableArray<UIAlertView *> *_alerts;
NSMutableArray<UIAlertController *> *_alertControllers;
NSMutableArray<RCTResponseSenderBlock> *_alertCallbacks;
NSMutableArray<NSArray<NSString *> *> *_alertButtonKeys;
@@ -46,9 +45,6 @@ RCT_EXPORT_MODULE()
- (void)invalidate
{
for (UIAlertView *alert in _alerts) {
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
for (UIAlertController *alertController in _alertControllers) {
[alertController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
@@ -73,7 +69,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
{
NSString *title = [RCTConvert NSString:args[@"title"]];
NSString *message = [RCTConvert NSString:args[@"message"]];
UIAlertViewStyle type = [RCTConvert UIAlertViewStyle:args[@"type"]];
RCTAlertViewStyle type = [RCTConvert RCTAlertViewStyle:args[@"type"]];
NSArray<NSDictionary *> *buttons = [RCTConvert NSDictionaryArray:args[@"buttons"]];
NSString *defaultValue = [RCTConvert NSString:args[@"defaultValue"]];
NSString *cancelButtonKey = [RCTConvert NSString:args[@"cancelButtonKey"]];
@@ -85,7 +81,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}
if (buttons.count == 0) {
if (type == UIAlertViewStyleDefault) {
if (type == RCTAlertViewStyleDefault) {
buttons = @[@{@"0": RCTUIKitLocalizedString(@"OK")}];
cancelButtonKey = @"0";
} else {
@@ -108,14 +104,14 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
message:nil
preferredStyle:UIAlertControllerStyleAlert];
switch (type) {
case UIAlertViewStylePlainTextInput: {
case RCTAlertViewStylePlainTextInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.text = defaultValue;
}];
break;
}
case UIAlertViewStyleSecureTextInput: {
case RCTAlertViewStyleSecureTextInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = RCTUIKitLocalizedString(@"Password");
textField.secureTextEntry = YES;
@@ -123,7 +119,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}];
break;
}
case UIAlertViewStyleLoginAndPasswordInput: {
case RCTAlertViewStyleLoginAndPasswordInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = RCTUIKitLocalizedString(@"Login");
textField.text = defaultValue;
@@ -134,7 +130,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}];
break;
}
case UIAlertViewStyleDefault:
case RCTAlertViewStyleDefault:
break;
}
@@ -156,11 +152,11 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
style:buttonStyle
handler:^(__unused UIAlertAction *action) {
switch (type) {
case UIAlertViewStylePlainTextInput:
case UIAlertViewStyleSecureTextInput:
case RCTAlertViewStylePlainTextInput:
case RCTAlertViewStyleSecureTextInput:
callback(@[buttonKey, [alertController.textFields.firstObject text]]);
break;
case UIAlertViewStyleLoginAndPasswordInput: {
case RCTAlertViewStyleLoginAndPasswordInput: {
NSDictionary<NSString *, NSString *> *loginCredentials = @{
@"login": [alertController.textFields.firstObject text],
@"password": [alertController.textFields.lastObject text]
@@ -168,7 +164,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
callback(@[buttonKey, loginCredentials]);
break;
}
case UIAlertViewStyleDefault:
case RCTAlertViewStyleDefault:
callback(@[buttonKey]);
break;
}
@@ -183,37 +179,4 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
[presentingController presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSUInteger index = [_alerts indexOfObject:alertView];
RCTAssert(index != NSNotFound, @"Dismissed alert was not recognised");
RCTResponseSenderBlock callback = _alertCallbacks[index];
NSArray<NSString *> *buttonKeys = _alertButtonKeys[index];
switch (alertView.alertViewStyle) {
case UIAlertViewStylePlainTextInput:
case UIAlertViewStyleSecureTextInput:
callback(@[buttonKeys[buttonIndex], [alertView textFieldAtIndex:0].text]);
break;
case UIAlertViewStyleLoginAndPasswordInput: {
NSDictionary<NSString *, NSString *> *loginCredentials = @{
@"login": [alertView textFieldAtIndex:0].text,
@"password": [alertView textFieldAtIndex:1].text,
};
callback(@[buttonKeys[buttonIndex], loginCredentials]);
break;
}
case UIAlertViewStyleDefault:
callback(@[buttonKeys[buttonIndex]]);
break;
}
[_alerts removeObjectAtIndex:index];
[_alertCallbacks removeObjectAtIndex:index];
[_alertButtonKeys removeObjectAtIndex:index];
}
@end

View File

@@ -67,7 +67,11 @@ static NSString *RCTGetStorageDirectory()
static NSString *storageDirectory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_OS_TV
storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
#else
storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
#endif
storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory];
});
return storageDirectory;
@@ -214,6 +218,10 @@ RCT_EXPORT_MODULE()
{
RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread");
#if TARGET_OS_TV
RCTLogWarn(@"Persistent storage is not supported on tvOS, your data may be removed at any point.")
#endif
NSError *error = nil;
if (!RCTHasCreatedStorageDirectory) {
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory()

View File

@@ -76,8 +76,11 @@ RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(UIColor *)color backgro
if (!self->_window && !RCTRunningInTestEnvironment()) {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 22)];
#if TARGET_OS_TV
self->_window.windowLevel = UIWindowLevelNormal + 1;
#else
self->_window.windowLevel = UIWindowLevelStatusBar + 1;
#endif
// set a root VC so rotation is supported
self->_window.rootViewController = [UIViewController new];

View File

@@ -120,7 +120,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
@end
@interface RCTDevMenu () <RCTBridgeModule, UIActionSheetDelegate, RCTInvalidating, RCTWebSocketProxyDelegate>
@interface RCTDevMenu () <RCTBridgeModule, RCTInvalidating, RCTWebSocketProxyDelegate>
@property (nonatomic, strong) Class executorClass;
@@ -128,7 +128,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
@implementation RCTDevMenu
{
UIActionSheet *_actionSheet;
UIAlertController *_actionSheet;
NSUserDefaults *_defaults;
NSMutableDictionary *_settings;
NSURLSessionDataTask *_updateTask;
@@ -409,7 +409,7 @@ RCT_EXPORT_MODULE()
{
_presentedItems = nil;
[_updateTask cancel];
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@@ -423,7 +423,7 @@ RCT_EXPORT_MODULE()
- (void)toggle
{
if (_actionSheet) {
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
_actionSheet = nil;
} else {
[self show];
@@ -458,13 +458,11 @@ RCT_EXPORT_MODULE()
Class jsDebuggingExecutorClass = objc_lookUpClass("RCTWebSocketExecutor");
if (!jsDebuggingExecutorClass) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:[NSString stringWithFormat:@"%@ Debugger Unavailable", _webSocketExecutorName] handler:^{
UIAlertView *alert = RCTAlertView(
[NSString stringWithFormat:@"%@ Debugger Unavailable", self->_webSocketExecutorName],
[NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", self->_webSocketExecutorName],
nil,
@"OK",
nil);
[alert show];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@ Debugger Unavailable", self->_webSocketExecutorName]
message:[NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", self->_webSocketExecutorName]
preferredStyle:UIAlertControllerStyleAlert];
[RCTPresentedViewController() presentViewController:alertController animated:YES completion:NULL];
}]];
} else {
BOOL isDebuggingJS = _executorClass && _executorClass == jsDebuggingExecutorClass;
@@ -514,55 +512,45 @@ RCT_EXPORT_METHOD(show)
return;
}
UIActionSheet *actionSheet = [UIActionSheet new];
actionSheet.title = @"React Native: Development";
actionSheet.delegate = self;
_actionSheet = [UIAlertController alertControllerWithTitle:@"React Native: Development"
message:@""
preferredStyle:UIAlertControllerStyleActionSheet];
NSArray<RCTDevMenuItem *> *items = [self menuItems];
for (RCTDevMenuItem *item in items) {
switch (item.type) {
case RCTDevMenuTypeButton: {
[actionSheet addButtonWithTitle:item.title];
[_actionSheet addAction:[UIAlertAction actionWithTitle:item.title
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// Cancel button tappped.
[item callHandler];
}]];
break;
}
case RCTDevMenuTypeToggle: {
BOOL selected = [item.value boolValue];
[actionSheet addButtonWithTitle:selected? item.selectedTitle : item.title];
[_actionSheet addAction:[UIAlertAction actionWithTitle:(selected? item.selectedTitle : item.title)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
BOOL value = [self->_settings[item.key] boolValue];
[self updateSetting:item.key value:@(!value)]; // will call handler
}]];
break;
}
}
}
[actionSheet addButtonWithTitle:@"Cancel"];
actionSheet.cancelButtonIndex = actionSheet.numberOfButtons - 1;
[_actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
}]];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:RCTKeyWindow().rootViewController.view];
_actionSheet = actionSheet;
_presentedItems = items;
[RCTPresentedViewController() presentViewController:_actionSheet animated:YES completion:^(void){}];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
_actionSheet = nil;
if (buttonIndex == actionSheet.cancelButtonIndex) {
return;
}
RCTDevMenuItem *item = _presentedItems[buttonIndex];
switch (item.type) {
case RCTDevMenuTypeButton: {
[item callHandler];
break;
}
case RCTDevMenuTypeToggle: {
BOOL value = [_settings[item.key] boolValue];
[self updateSetting:item.key value:@(!value)]; // will call handler
break;
}
}
return;
}
RCT_EXPORT_METHOD(reload)
{

View File

@@ -19,6 +19,8 @@ RCT_EXPORT_MODULE()
- (void)startObserving
{
#if !TARGET_OS_TV
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
#define ADD_KEYBOARD_HANDLER(NAME, SELECTOR) \
@@ -33,6 +35,8 @@ RCT_EXPORT_MODULE()
#undef ADD_KEYBOARD_HANDLER
#endif
}
- (NSArray<NSString *> *)supportedEvents
@@ -94,6 +98,9 @@ static NSString *RCTAnimationNameForCurve(UIViewAnimationCurve curve)
static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification)
{
#if TARGET_OS_TV
return @{};
#else
NSDictionary *userInfo = notification.userInfo;
CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
@@ -106,4 +113,5 @@ static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification)
@"duration": @(duration * 1000.0), // ms
@"easing": RCTAnimationNameForCurve(curve),
};
#endif
}

View File

@@ -60,8 +60,10 @@
_stackTraceTableView.delegate = self;
_stackTraceTableView.dataSource = self;
_stackTraceTableView.backgroundColor = [UIColor clearColor];
#if !TARGET_OS_TV
_stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3];
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
#endif
_stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
[rootView addSubview:_stackTraceTableView];
@@ -175,9 +177,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
[fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]];
}
}
#if !TARGET_OS_TV
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:fullStackTrace];
#endif
}
- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame

View File

@@ -99,6 +99,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
// `UIKeyboardWillChangeFrameNotification`s.
+ (void)initializeStatics
{
#if !TARGET_OS_TV
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -106,12 +107,15 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
name:UIKeyboardWillChangeFrameNotification
object:nil];
});
#endif
}
+ (void)keyboardWillChangeFrame:(NSNotification *)notification
{
#if !TARGET_OS_TV
NSDictionary *userInfo = notification.userInfo;
_currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
#endif
}
- (instancetype)initWithDuration:(NSTimeInterval)duration dictionary:(NSDictionary *)config
@@ -225,8 +229,9 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
NSDictionary *_componentDataByName;
NSMutableSet<id<RCTComponent>> *_bridgeTransactionListeners;
#if !TARGET_OS_TV
UIInterfaceOrientation _currentInterfaceOrientation;
#endif
}
@synthesize bridge = _bridge;
@@ -244,6 +249,7 @@ RCT_EXPORT_MODULE()
- (void)interfaceOrientationWillChange:(NSNotification *)notification
{
#if !TARGET_OS_TV
UIInterfaceOrientation nextOrientation =
[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
@@ -260,6 +266,7 @@ RCT_EXPORT_MODULE()
}
_currentInterfaceOrientation = nextOrientation;
#endif
}
- (void)invalidate
@@ -339,11 +346,13 @@ RCT_EXPORT_MODULE()
selector:@selector(didReceiveNewContentSizeMultiplier)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:_bridge.accessibilityManager];
#if !TARGET_OS_TV
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceOrientationWillChange:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
#endif
[RCTAnimation initializeStatics];
}
@@ -1510,7 +1519,9 @@ RCT_EXPORT_METHOD(clearJSResponder)
allJSConstants[name] = constantsNamespace;
}];
#if !TARGET_OS_TV
_currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation];
#endif
[allJSConstants addEntriesFromDictionary:@{
@"customBubblingEventTypes": bubblingEvents,
@"customDirectEventTypes": directEvents,