Skip to content

Commit b719ae6

Browse files
committed
Fix a 'NotFound' ViewController when using Storyboards with NavigationController
1 parent 9a0c744 commit b719ae6

File tree

1 file changed

+60
-256
lines changed

1 file changed

+60
-256
lines changed

src/ios/CDVNativeView.m

Lines changed: 60 additions & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -46,65 +46,20 @@ - (instancetype)init
4646
}
4747
- (void)show:(CDVInvokedUrlCommand*)command {
4848

49-
CDVPluginResult *pluginResult;
49+
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
5050

51-
@try {
52-
53-
NSString *viewControllerName;
54-
NSString *storyboardName;
55-
NSString *uri;
56-
NSString *message;
57-
NSString *firstParam;
51+
NSString* className = [command argumentAtIndex: 0];
52+
NSString* storyboardName = @"";
53+
54+
UIViewController *viewController = nil;
55+
56+
if ([command.arguments count] > 1) {
5857

59-
NSMutableDictionary* config = [command.arguments objectAtIndex:0];
58+
NSString* secondParam = [command argumentAtIndex: 1];
6059

61-
if ([config isKindOfClass:[NSMutableDictionary class]]) {
62-
63-
viewControllerName = [config objectForKey:@"viewControllerName"];
64-
storyboardName = [config objectForKey:@"storyboardName"];
65-
uri = [config objectForKey:@"uri"];
66-
67-
} else if ([config isKindOfClass:[NSString class]]) {
68-
69-
if ([command.arguments count] == 1) {
70-
71-
firstParam = [command argumentAtIndex: 0];
72-
73-
if ([self isValidURI: firstParam]) {
74-
// Open app with valid uri name
75-
[self openAPP:firstParam withCommand: command];
76-
77-
} else if ([firstParam containsString:@"Storyboard"]) {
78-
// Init viewController from Storyboard with initial view Controller or user defined viewControllerName
79-
[self instantiateViewController:nil fromStoryboard:firstParam];
80-
81-
} else if ([firstParam containsString:@"Controller"]) {
82-
// Init viewController with or without xib
83-
[self instantiateViewController:firstParam];
84-
85-
} else {
86-
message = [[NSString alloc] initWithFormat:@"%@ invalid. Must contain a Storyboard / Controller / URI valid in name", firstParam];
87-
@throw [[NSException alloc] initWithName:@"IoException" reason:message userInfo:nil];
88-
}
89-
90-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
91-
return;
92-
93-
}else if ([command.arguments count] == 2) {
94-
95-
// first param is Storyboard
96-
storyboardName = [command argumentAtIndex: 0];
97-
98-
// second param is ViewController and/or storyboardId
99-
viewControllerName = [command argumentAtIndex: 1];
100-
101-
}else{
102-
message = [[NSString alloc] initWithFormat:@"An UIViewController name or Storyboard name or URI valid name is required at least. Please, pass in the first param in JS, like this: 'NativeView.show('MyViewController') or NativeView.show('MyStoryboard') or NativeView.show('MyStoryboard', 'MyViewController') or NativeView.show('instagram://')"];
103-
@throw [[NSException alloc] initWithName:@"NotFoundException" reason:message userInfo:nil];
104-
}
105-
106-
}else{
107-
@throw [[NSException alloc] initWithName:@"ParamsTypeException" reason:@"The params of show() method needs be a string or a json" userInfo:nil];
60+
if (secondParam != nil) {
61+
storyboardName = className;
62+
className = secondParam != nil ? secondParam : @"";
10863
}
10964

11065
if ([self isValidURI: uri]) {
@@ -136,236 +91,85 @@ - (void)show:(CDVInvokedUrlCommand*)command {
13691
pluginResult = [CDVPluginResult resultWithStatus:exceptionType messageAsDictionary:error];
13792
}
13893

139-
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
140-
}
141-
142-
- (void)showMarket:(CDVInvokedUrlCommand*)command {
143-
144-
NSMutableDictionary* config = [command.arguments objectAtIndex:0];
145-
146-
dispatch_async(dispatch_get_main_queue(), ^{
94+
if ([self.viewController navigationController] != nil) {
14795

148-
CDVPluginResult *pluginResult;
149-
NSString *appId;
150-
151-
if ([config isKindOfClass:[NSMutableDictionary class]]) {
152-
appId = [config objectForKey:@"marketId"];
153-
}else if([config isKindOfClass:[NSString class]]) {
154-
appId = (NSString *) config;
155-
}
156-
157-
if (appId && [appId isKindOfClass:[NSString class]] && [appId length] > 0) {
158-
NSString *url = [NSString stringWithFormat:@"itms://itunes.apple.com/app/%@", appId];
159-
160-
[self openAPP:url withCommand: command];
96+
if ([self.viewController.navigationController.viewControllers count] > 1) {
97+
[self.viewController.navigationController popViewControllerAnimated: YES];
98+
}else{
16199

162-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
163-
} else {
164-
NSDictionary* error = @{
165-
@"success": @NO,
166-
@"name": @"ParamInvalidException",
167-
@"message": @"Invalid application id: the parameter 'marketId' is invalid"
168-
};
100+
viewController = [self instantiateViewControllerWithName: className];
169101

170-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_INVALID_ACTION messageAsDictionary:error];
102+
[self.viewController.navigationController pushViewController:viewController animated:YES];
171103
}
172104

173-
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
174-
});
175-
}
176-
177-
- (void)checkIfAppInstalled:(CDVInvokedUrlCommand*)command {
178-
CDVPluginResult *pluginResult;
179-
NSString *uri;
180-
181-
@try {
182-
NSMutableDictionary* config = [command.arguments objectAtIndex:0];
105+
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
183106

184-
if ([config isKindOfClass:[NSMutableDictionary class]]) {
185-
uri = [config objectForKey:@"uri"];
186-
187-
if (uri == nil) {
188-
@throw [[NSException alloc] initWithName:@"ParamsTypeException" reason:@"The 'uri' key is required" userInfo:nil];
189-
}
190-
}else if ([config isKindOfClass:[NSString class]]) {
191-
uri = (NSString *) config;
192-
}else{
193-
@throw [[NSException alloc] initWithName:@"ParamsTypeException" reason:@"The params of checkIfAppInstalled() method needs be a string or a json" userInfo:nil];
194-
}
107+
}else if (className.length > 0 ){
195108

196-
if (![self isValidURI: uri]) {
197-
NSString *message = [[NSString alloc] initWithFormat:@"uri param invalid: %@", uri];
198-
NSDictionary* error = @{
199-
@"success": @NO,
200-
@"name": @"ParamInvalidException",
201-
@"message": message
202-
};
109+
if ([[NSBundle mainBundle] pathForResource:storyboardName ofType: @"storyboardc"] != nil
110+
&& storyboardName.length > 0) {
111+
112+
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle mainBundle]];
203113

204-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
205-
} else {
206-
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:uri]]) {
207-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:(true)];
208-
}
209-
else {
210-
NSString *message = [[NSString alloc] initWithFormat:@"The app that responds to URI: %@ was not found", uri];
211-
NSDictionary* error = @{
212-
@"success": @NO,
213-
@"name": @"NotFoundException",
214-
@"message": message
215-
};
114+
viewController = [storyboard instantiateViewControllerWithIdentifier:className];
115+
116+
}else{
216117

217-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION messageAsDictionary:error];
118+
viewController = [self tryInstantiateViewWithName: className];
218119
}
219120
}
220121
} @catch (NSException *e) {
221122
NSLog(@"[%@]: %@", e.name, e.reason);
222123

223-
typedef CDVCommandStatus (^CaseBlock)(void);
224-
225-
CaseBlock c = self.resultExceptions[e.name];
226-
227-
CDVCommandStatus exceptionType = c ? c() : CDVCommandStatus_ERROR;
228-
NSDictionary* error = @{
229-
@"success": @NO,
230-
@"name": e.name,
231-
@"message": e.reason
232-
};
233-
pluginResult = [CDVPluginResult resultWithStatus:exceptionType messageAsDictionary:error];
234-
} @finally {
235-
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
236-
}
237-
}
238-
239-
- (void) instantiateViewController:(NSString *)viewControllerName {
240-
241-
NSString *message;
242-
243-
if (viewControllerName && viewControllerName.length > 0) {
244-
245-
UIViewController *destinyViewController = nil;
124+
CDVAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
125+
appDelegate.window.rootViewController = viewController;
246126

247-
// Call preInitializeViewControllerWithName if exists in self.viewController
248-
SEL selector = NSSelectorFromString(@"preInitializeViewControllerWithName:");
249-
250-
if ([self.viewController respondsToSelector:selector]) {
251-
252-
SuppressPerformSelectorLeakWarning(
253-
destinyViewController = [self.viewController performSelector:selector withObject:viewControllerName];
254-
);
127+
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
128+
}else{
129+
@try {
130+
[self raiseClassNameError];
131+
} @catch(InstantiateViewControllerError* e) {
132+
NSLog(@"%@", e.reason);
255133
}
256-
257-
// if not performSelector, call automatically the viewController
258-
if (!destinyViewController) {
259-
@try {
260-
if ([[NSBundle mainBundle] pathForResource:viewControllerName ofType:@"nib"]) {
261-
// Initialize with nib/xib
262-
destinyViewController = [[UIViewController alloc] initWithNibName:viewControllerName bundle:nil];
263-
} else {
264-
// Initialize without nib/xib
265-
Class viewController = NSClassFromString(viewControllerName);
266-
id anInstance = [[viewController alloc] init];
267-
destinyViewController = anInstance;
268-
}
269-
} @catch(NSException *e) {
270-
message = [[NSString alloc] initWithFormat:@"%@ and/or its own xib does not exist. \nDetail: %@", viewControllerName, e.reason];
271-
@throw [[NSException alloc] initWithName:@"NotFoundException" reason:message userInfo:nil];
272-
}
273-
}
274-
275-
// Call destinyViewController from current viewController
276-
[self.viewController.navigationController pushViewController:destinyViewController animated:YES];
277-
278-
} else {
279-
message = [[NSString alloc] initWithFormat:@"UIViewController with name %@ was not found", viewControllerName];
280-
@throw [[NSException alloc] initWithName:@"ParamInvalidException" reason:message userInfo:nil];
281134
}
135+
136+
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId ];
282137
}
283138

284-
- (void) instantiateViewController:(NSString *)viewControllerName fromStoryboard:(NSString *)storyboardName {
139+
- (UIViewController*) instantiateViewControllerWithName: (NSString*) name {
285140

286-
NSString *message;
287-
288-
if (storyboardName && storyboardName.length > 0) {
289-
290-
UIViewController *destinyViewController = nil;
141+
if ([[NSBundle mainBundle] pathForResource:name ofType: @"nib"]) {
142+
return [[UIViewController alloc] initWithNibName: name bundle: nil];
143+
}else{
291144

292-
// Call preInitializeViewControllerWithName:fromStoryBoardName if exists in self.viewController
293-
SEL selector = NSSelectorFromString(@"preInitializeViewControllerWithName:fromStoryBoardName:");
145+
NSBundle* mainBundle = [NSBundle mainBundle];
146+
NSString *storyboardName = [mainBundle objectForInfoDictionaryKey:@"UIMainStoryboardFile"];
294147

295-
if ([self.viewController respondsToSelector:selector]) {
296-
297-
SuppressPerformSelectorLeakWarning(
298-
destinyViewController = [self.viewController performSelector:selector withObject:viewControllerName withObject:storyboardName];
299-
);
148+
if (storyboardName != nil) {
149+
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
150+
return [storyboard instantiateViewControllerWithIdentifier:name];
300151
}
301-
302-
// if not performSelector, call automatically the viewController from storyboard
303-
if (!destinyViewController) {
304-
// initialize a storyboard automatically from viewControllerName or default initialViewController property
305-
@try {
306-
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle mainBundle]];
307-
308-
if (viewControllerName && viewControllerName.length > 0) {
309-
message = [[NSString alloc] initWithFormat:@"Identity -> Storyboard ID: %@ not found in storyboard %@", viewControllerName, storyboardName];
310-
// if pass a viewControllerName, initializate the storyboard with viewControllerName initial
311-
destinyViewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerName];
312-
} else {
313-
message = [[NSString alloc] initWithFormat:@"Storyboard -> ViewController -> 'is Initial View Controller' not check in storyboard %@", storyboardName];
314-
// if not pass a viewControllerName, initializate the storyboard with default inicialViewController property
315-
destinyViewController = [storyboard instantiateInitialViewController];
316-
}
317-
} @catch (NSException *e) {
318-
NSString *detailMessage = [[NSString alloc] initWithFormat:@"%@ \nDetail: %@", message, e.reason];
319-
@throw [[NSException alloc] initWithName:@"NotFoundException" reason:detailMessage userInfo:nil];
320-
}
321-
}
322-
323-
// Call destinyViewController from current viewController
324-
[self.viewController.navigationController pushViewController:destinyViewController animated:YES];
325-
326-
} else {
327-
message = [[NSString alloc] initWithFormat:@"Storyboard %@ was not found", storyboardName];
328-
@throw [[NSException alloc] initWithName:@"ParamInvalidException" reason:message userInfo:nil];
329152
}
153+
154+
NSString* message = [[NSString alloc] initWithFormat:@"The ViewController: %@ was not found", name];
155+
@throw [[InstantiateViewControllerError alloc] initWithName: @"notFound" reason: message userInfo: nil];
330156
}
331157

332-
- (bool) isValidURI:(NSString *)uri {
333-
if (uri != nil && [uri containsString:@"://"]) { // TODO: Replace for regular expression
334-
return true;
158+
- (UIViewController*) tryInstantiateViewWithName:(NSString *)name {
159+
160+
@try {
161+
return [self instantiateViewControllerWithName: name];
162+
} @catch (InstantiateViewControllerError* e) {
163+
NSLog(@"%@", e.reason);
335164
}
336-
return false;
165+
166+
return nil;
337167
}
338168

339-
- (void) openAPP:(NSString *)uriValue withCommand:(CDVInvokedUrlCommand*) command {
169+
- (void) raiseClassNameError {
340170

341-
if ([[UIApplication sharedApplication] respondsToSelector:@selector(openURL:options:completionHandler:)]) { // ios >= 10
342-
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:uriValue] options:@{} completionHandler:^(BOOL opened) {
343-
344-
CDVPluginResult *pluginResult;
345-
346-
if (!opened) {
347-
NSString* message = @"APP with uri %@ not found.";
348-
NSLog(message, uriValue);
349-
350-
NSDictionary* error = @{
351-
@"success": @NO,
352-
@"name": @"InstantiationException",
353-
@"message": [[NSString alloc] initWithFormat:message, uriValue]
354-
};
355-
356-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_INSTANTIATION_EXCEPTION messageAsDictionary:error];
357-
} else {
358-
NSString* message = @"APP with uri %@ opened.";
359-
NSLog(message, uriValue);
360-
361-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSString alloc] initWithFormat:message, uriValue]];
362-
}
363-
364-
[self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
365-
}];
366-
} else { // ios < 10 (Will be depreciated)
367-
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:uriValue]];
368-
}
171+
NSString* message = [[NSString alloc] initWithFormat:@"The UIViewController name is required when the project don't have a navigatioController. Please, pass a className by param in JS, like this: 'NativeView.show('MyUIViewController')"];
172+
@throw [[InstantiateViewControllerError alloc] initWithName: @"nameNotDefined" reason: message userInfo: nil];
369173
}
370174

371175
@end

0 commit comments

Comments
 (0)