This commit is contained in:
tiago.kayaya
2021-08-18 18:58:21 +01:00
parent 24e2a8f518
commit d7efe502f8
679 changed files with 123414 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
*.mode1v3
*.perspectivev3
*.pbxuser
.DS_Store
build/
@@ -0,0 +1,25 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
@@ -0,0 +1,31 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
@interface NSArray (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end
@interface NSDictionary (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end
@interface NSString (CDVJSONSerializingPrivate)
- (id)cdv_JSONObject;
- (id)cdv_JSONFragment;
@end
@@ -0,0 +1,99 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVJSON_private.h"
#import <Foundation/NSJSONSerialization.h>
@implementation NSArray (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString
{
@autoreleasepool {
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:0
error:&error];
if (error != nil) {
NSLog(@"NSArray JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
}
@end
@implementation NSDictionary (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString
{
@autoreleasepool {
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
}
@end
@implementation NSString (CDVJSONSerializingPrivate)
- (id)cdv_JSONObject
{
@autoreleasepool {
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingMutableContainers
error:&error];
if (error != nil) {
NSLog(@"NSString JSONObject error: %@, Malformed Data: %@", [error localizedDescription], self);
}
return object;
}
}
- (id)cdv_JSONFragment
{
@autoreleasepool {
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:&error];
if (error != nil) {
NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
}
return object;
}
}
@end
@@ -0,0 +1,24 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
@interface CDVPlugin (Private)
- (instancetype)initWithWebViewEngine:(id <CDVWebViewEngineProtocol>)theWebViewEngine;
@end
@@ -0,0 +1,26 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDVPlugin.h>
@interface CDVGestureHandler : CDVPlugin
@property (nonatomic, strong) UILongPressGestureRecognizer* lpgr;
@end
@@ -0,0 +1,70 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVGestureHandler.h"
@implementation CDVGestureHandler
- (void)pluginInitialize
{
[self applyLongPressFix];
}
- (void)applyLongPressFix
{
// You can't suppress 3D Touch and still have regular longpress,
// so if this is false, let's not consider the 3D Touch setting at all.
if (![self.commandDelegate.settings objectForKey:@"suppresseslongpressgesture"] ||
![[self.commandDelegate.settings objectForKey:@"suppresseslongpressgesture"] boolValue]) {
return;
}
self.lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGestures:)];
self.lpgr.minimumPressDuration = 0.45f;
self.lpgr.allowableMovement = 200.0f;
// 0.45 is ok for 'regular longpress', 0.05-0.08 is required for '3D Touch longpress',
// but since this will also kill onclick handlers (not ontouchend) it's optional.
if ([self.commandDelegate.settings objectForKey:@"suppresses3dtouchgesture"] &&
[[self.commandDelegate.settings objectForKey:@"suppresses3dtouchgesture"] boolValue]) {
self.lpgr.minimumPressDuration = 0.15f;
}
NSArray *views = self.webView.subviews;
if (views.count == 0) {
NSLog(@"No webview subviews found, not applying the longpress fix.");
return;
}
for (int i=0; i<views.count; i++) {
UIView *webViewScrollView = views[i];
if ([webViewScrollView isKindOfClass:[UIScrollView class]]) {
NSArray *webViewScrollViewSubViews = webViewScrollView.subviews;
UIView *browser = webViewScrollViewSubViews[0];
[browser addGestureRecognizer:self.lpgr];
break;
}
}
}
- (void)handleLongPressGestures:(UILongPressGestureRecognizer*)sender
{
}
@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDVPlugin.h>
@interface CDVHandleOpenURL : CDVPlugin
@property (nonatomic, strong) NSURL* url;
@property (nonatomic, assign) BOOL pageLoaded;
@end
@@ -0,0 +1,86 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVHandleOpenURL.h"
#import <Cordova/CDV.h>
@implementation CDVHandleOpenURL
- (void)pluginInitialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationLaunchedWithUrl:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationPageDidLoad:) name:CDVPageDidLoadNotification object:nil];
}
- (void)applicationLaunchedWithUrl:(NSNotification*)notification
{
NSURL* url = [notification object];
self.url = url;
// warm-start handler
if (self.pageLoaded) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}
- (void)applicationPageDidLoad:(NSNotification*)notification
{
// cold-start handler
self.pageLoaded = YES;
if (self.url) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}
- (void)processOpenUrl:(NSURL*)url pageLoaded:(BOOL)pageLoaded
{
__weak __typeof(self) weakSelf = self;
dispatch_block_t handleOpenUrl = ^(void) {
// calls into javascript global function 'handleOpenURL'
NSString* jsString = [NSString stringWithFormat:@"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%@\");}});", url.absoluteString];
[weakSelf.webViewEngine evaluateJavaScript:jsString completionHandler:nil];
};
if (!pageLoaded) {
NSString* jsString = @"document.readystate";
[self.webViewEngine evaluateJavaScript:jsString
completionHandler:^(id object, NSError* error) {
if ((error == nil) && [object isKindOfClass:[NSString class]]) {
NSString* readyState = (NSString*)object;
BOOL ready = [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
if (ready) {
handleOpenUrl();
} else {
self.url = url;
}
}
}];
} else {
handleOpenUrl();
}
}
@end
@@ -0,0 +1,36 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDVPlugin.h>
#import <Cordova/CDVWhitelist.h>
#define CDVWebViewNavigationType int
typedef NS_ENUM(NSInteger, CDVIntentAndNavigationFilterValue) {
CDVIntentAndNavigationFilterValueIntentAllowed,
CDVIntentAndNavigationFilterValueNavigationAllowed,
CDVIntentAndNavigationFilterValueNoneAllowed
};
@interface CDVIntentAndNavigationFilter : CDVPlugin <NSXMLParserDelegate>
+ (CDVIntentAndNavigationFilterValue) filterUrl:(NSURL*)url intentsWhitelist:(CDVWhitelist*)intentsWhitelist navigationsWhitelist:(CDVWhitelist*)navigationsWhitelist;
+ (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType filterValue:(CDVIntentAndNavigationFilterValue)filterValue;
+ (BOOL)shouldOpenURLRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType;
@end
@@ -0,0 +1,153 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVIntentAndNavigationFilter.h"
#import <Cordova/CDV.h>
@interface CDVIntentAndNavigationFilter ()
@property (nonatomic, readwrite) NSMutableArray* allowIntents;
@property (nonatomic, readwrite) NSMutableArray* allowNavigations;
@property (nonatomic, readwrite) CDVWhitelist* allowIntentsWhitelist;
@property (nonatomic, readwrite) CDVWhitelist* allowNavigationsWhitelist;
@end
@implementation CDVIntentAndNavigationFilter
#pragma mark NSXMLParserDelegate
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict
{
if ([elementName isEqualToString:@"allow-navigation"]) {
[self.allowNavigations addObject:attributeDict[@"href"]];
}
if ([elementName isEqualToString:@"allow-intent"]) {
[self.allowIntents addObject:attributeDict[@"href"]];
}
}
- (void)parserDidStartDocument:(NSXMLParser*)parser
{
// file: url <allow-navigations> are added by default
// navigation to the scheme used by the app is also allowed
self.allowNavigations = [[NSMutableArray alloc] initWithArray:@[ @"file://"]];
// If the custom app scheme is defined, append it to the allow navigation as default
NSString* scheme = ((CDVViewController*)self.viewController).appScheme;
if (scheme) {
[self.allowNavigations addObject: [NSString stringWithFormat:@"%@://", scheme]];
}
// no intents are added by default
self.allowIntents = [[NSMutableArray alloc] init];
}
- (void)parserDidEndDocument:(NSXMLParser*)parser
{
self.allowIntentsWhitelist = [[CDVWhitelist alloc] initWithArray:self.allowIntents];
self.allowNavigationsWhitelist = [[CDVWhitelist alloc] initWithArray:self.allowNavigations];
}
- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError
{
NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]);
}
#pragma mark CDVPlugin
- (void)pluginInitialize
{
if ([self.viewController isKindOfClass:[CDVViewController class]]) {
[(CDVViewController*)self.viewController parseSettingsWithParser:self];
}
}
+ (CDVIntentAndNavigationFilterValue) filterUrl:(NSURL*)url intentsWhitelist:(CDVWhitelist*)intentsWhitelist navigationsWhitelist:(CDVWhitelist*)navigationsWhitelist
{
// a URL can only allow-intent OR allow-navigation, if both are specified,
// only allow-navigation is allowed
BOOL allowNavigationsPass = [navigationsWhitelist URLIsAllowed:url logFailure:NO];
BOOL allowIntentPass = [intentsWhitelist URLIsAllowed:url logFailure:NO];
if (allowNavigationsPass && allowIntentPass) {
return CDVIntentAndNavigationFilterValueNavigationAllowed;
} else if (allowNavigationsPass) {
return CDVIntentAndNavigationFilterValueNavigationAllowed;
} else if (allowIntentPass) {
return CDVIntentAndNavigationFilterValueIntentAllowed;
}
return CDVIntentAndNavigationFilterValueNoneAllowed;
}
- (CDVIntentAndNavigationFilterValue) filterUrl:(NSURL*)url
{
return [[self class] filterUrl:url intentsWhitelist:self.allowIntentsWhitelist navigationsWhitelist:self.allowNavigationsWhitelist];
}
#define CDVWebViewNavigationTypeLinkClicked 0
#define CDVWebViewNavigationTypeLinkOther -1
+ (BOOL)shouldOpenURLRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType
{
BOOL isMainNavigation = [[request.mainDocumentURL absoluteString] isEqualToString:[request.URL absoluteString]];
return (
navigationType == CDVWebViewNavigationTypeLinkClicked ||
(navigationType == CDVWebViewNavigationTypeLinkOther && isMainNavigation)
);
}
+ (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType filterValue:(CDVIntentAndNavigationFilterValue)filterValue
{
NSString* allowIntents_whitelistRejectionFormatString = @"ERROR External navigation rejected - <allow-intent> not set for url='%@'";
NSString* allowNavigations_whitelistRejectionFormatString = @"ERROR Internal navigation rejected - <allow-navigation> not set for url='%@'";
NSURL* url = [request URL];
switch (filterValue) {
case CDVIntentAndNavigationFilterValueNavigationAllowed:
return YES;
case CDVIntentAndNavigationFilterValueIntentAllowed:
// only allow-intent if it's a CDVWebViewNavigationTypeLinkClicked (anchor tag) or CDVWebViewNavigationTypeOther and it's an internal link
if ([[self class] shouldOpenURLRequest:request navigationType:navigationType]){
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
// consume the request (i.e. no error) if it wasn't handled above
return NO;
case CDVIntentAndNavigationFilterValueNoneAllowed:
// allow-navigation attempt failed for sure
NSLog(@"%@", [NSString stringWithFormat:allowNavigations_whitelistRejectionFormatString, [url absoluteString]]);
// anchor tag link means it was an allow-intent attempt that failed as well
if (CDVWebViewNavigationTypeLinkClicked == navigationType) {
NSLog(@"%@", [NSString stringWithFormat:allowIntents_whitelistRejectionFormatString, [url absoluteString]]);
}
return NO;
}
}
- (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType
{
return [[self class] shouldOverrideLoadWithRequest:request navigationType:navigationType filterValue:[self filterUrl:request.URL]];
}
@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDVPlugin.h>
@interface CDVLaunchScreen : CDVPlugin
- (void)show:(CDVInvokedUrlCommand*)command;
- (void)hide:(CDVInvokedUrlCommand*)command;
@end
@@ -0,0 +1,39 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVLaunchScreen.h"
#import <Cordova/CDVViewController.h>
@implementation CDVLaunchScreen
- (void)show:(CDVInvokedUrlCommand*)command
{
if ([self.viewController isKindOfClass:[CDVViewController class]]) {
[(CDVViewController*)self.viewController showLaunchScreen:YES];
}
}
- (void)hide:(CDVInvokedUrlCommand*)command
{
if ([self.viewController isKindOfClass:[CDVViewController class]]) {
[(CDVViewController*)self.viewController showLaunchScreen:NO];
}
}
@end
@@ -0,0 +1,26 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDVPlugin.h>
@interface CDVLogger : CDVPlugin
- (void)logLevel:(CDVInvokedUrlCommand*)command;
@end
@@ -0,0 +1,37 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVLogger.h"
@implementation CDVLogger
/* log a message */
- (void)logLevel:(CDVInvokedUrlCommand*)command
{
id level = [command argumentAtIndex:0];
id message = [command argumentAtIndex:1];
if ([level isEqualToString:@"LOG"]) {
NSLog(@"%@", message);
} else {
NSLog(@"%@: %@", level, message);
}
}
@end
@@ -0,0 +1,29 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <WebKit/WebKit.h>
#import <Cordova/CDV.h>
@interface CDVWebViewEngine : CDVPlugin <CDVWebViewEngineProtocol, WKScriptMessageHandler, WKNavigationDelegate>
@property (nonatomic, strong, readonly) id <WKUIDelegate> uiDelegate;
- (void)allowsBackForwardNavigationGestures:(CDVInvokedUrlCommand*)command;
@end
@@ -0,0 +1,607 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVWebViewEngine.h"
#import "CDVWebViewUIDelegate.h"
#import "CDVWebViewProcessPoolFactory.h"
#import <Cordova/NSDictionary+CordovaPreferences.h>
#import "CDVURLSchemeHandler.h"
#import <objc/message.h>
#define CDV_BRIDGE_NAME @"cordova"
#define CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR @"loadFileURL:allowingReadAccessToURL:"
@interface CDVWebViewWeakScriptMessageHandler : NSObject <WKScriptMessageHandler>
@property (nonatomic, weak, readonly) id<WKScriptMessageHandler>scriptMessageHandler;
- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler;
@end
@interface CDVWebViewEngine ()
@property (nonatomic, strong, readwrite) UIView* engineWebView;
@property (nonatomic, strong, readwrite) id <WKUIDelegate> uiDelegate;
@property (nonatomic, weak) id <WKScriptMessageHandler> weakScriptMessageHandler;
@property (nonatomic, strong) CDVURLSchemeHandler * schemeHandler;
@property (nonatomic, readwrite) NSString *CDV_ASSETS_URL;
@property (nonatomic, readwrite) Boolean cdvIsFileScheme;
@end
// see forwardingTargetForSelector: selector comment for the reason for this pragma
#pragma clang diagnostic ignored "-Wprotocol"
@implementation CDVWebViewEngine
@synthesize engineWebView = _engineWebView;
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super init];
if (self) {
if (NSClassFromString(@"WKWebView") == nil) {
return nil;
}
self.engineWebView = [[WKWebView alloc] initWithFrame:frame];
}
return self;
}
- (WKWebViewConfiguration*) createConfigurationFromSettings:(NSDictionary*)settings
{
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
configuration.processPool = [[CDVWebViewProcessPoolFactory sharedFactory] sharedProcessPool];
if (settings == nil) {
return configuration;
}
configuration.allowsInlineMediaPlayback = [settings cordovaBoolSettingForKey:@"AllowInlineMediaPlayback" defaultValue:NO];
// Set the media types that are required for user action for playback
WKAudiovisualMediaTypes mediaType = WKAudiovisualMediaTypeAll; // default
// targetMediaType will always exist, either from user's "config.xml" or default ("defaults.xml").
id targetMediaType = [settings cordovaSettingForKey:@"MediaTypesRequiringUserActionForPlayback"];
if ([targetMediaType isEqualToString:@"none"]) {
mediaType = WKAudiovisualMediaTypeNone;
} else if ([targetMediaType isEqualToString:@"audio"]) {
mediaType = WKAudiovisualMediaTypeAudio;
} else if ([targetMediaType isEqualToString:@"video"]) {
mediaType = WKAudiovisualMediaTypeVideo;
} else if ([targetMediaType isEqualToString:@"all"]) {
mediaType = WKAudiovisualMediaTypeAll;
} else {
NSLog(@"Invalid \"MediaTypesRequiringUserActionForPlayback\" was detected. Fallback to default value of \"all\" types.");
}
configuration.mediaTypesRequiringUserActionForPlayback = mediaType;
configuration.suppressesIncrementalRendering = [settings cordovaBoolSettingForKey:@"SuppressesIncrementalRendering" defaultValue:NO];
/*
* If the old preference key "MediaPlaybackAllowsAirPlay" exists, use it or default to "YES".
* Check if the new preference key "AllowsAirPlayForMediaPlayback" exists and overwrite the "MediaPlaybackAllowsAirPlay" value.
*/
BOOL allowsAirPlayForMediaPlayback = [settings cordovaBoolSettingForKey:@"MediaPlaybackAllowsAirPlay" defaultValue:YES];
if([settings cordovaSettingForKey:@"AllowsAirPlayForMediaPlayback"] != nil) {
allowsAirPlayForMediaPlayback = [settings cordovaBoolSettingForKey:@"AllowsAirPlayForMediaPlayback" defaultValue:YES];
}
configuration.allowsAirPlayForMediaPlayback = allowsAirPlayForMediaPlayback;
/*
* Sets Custom User Agents
* - (Default) "userAgent" is set the the clean user agent.
* E.g.
* UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
*
* - If "OverrideUserAgent" is set, it will overwrite the entire "userAgent" value. The "AppendUserAgent" will be iggnored if set.
* Notice: The override logic is handled in the "pluginInitialize" method.
* E.g.
* OverrideUserAgent = "foobar"
* UserAgent = "foobar"
*
* - If "AppendUserAgent" is set and "OverrideUserAgent" is not set, the user defined "AppendUserAgent" will be appended to the "userAgent"
* E.g.
* AppendUserAgent = "foobar"
* UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 foobar"
*/
NSString *userAgent = configuration.applicationNameForUserAgent;
if (
[settings cordovaSettingForKey:@"OverrideUserAgent"] == nil &&
[settings cordovaSettingForKey:@"AppendUserAgent"] != nil
) {
userAgent = [NSString stringWithFormat:@"%@ %@", userAgent, [settings cordovaSettingForKey:@"AppendUserAgent"]];
}
configuration.applicationNameForUserAgent = userAgent;
if (@available(iOS 13.0, *)) {
NSString *contentMode = [settings cordovaSettingForKey:@"PreferredContentMode"];
if ([contentMode isEqual: @"mobile"]) {
configuration.defaultWebpagePreferences.preferredContentMode = WKContentModeMobile;
} else if ([contentMode isEqual: @"desktop"]) {
configuration.defaultWebpagePreferences.preferredContentMode = WKContentModeDesktop;
}
}
return configuration;
}
- (void)pluginInitialize
{
// viewController would be available now. we attempt to set all possible delegates to it, by default
CDVViewController* vc = (CDVViewController*)self.viewController;
NSDictionary* settings = self.commandDelegate.settings;
NSString *scheme = [settings cordovaSettingForKey:@"scheme"];
// If scheme is file or nil, then default to file scheme
self.cdvIsFileScheme = [scheme isEqualToString: @"file"] || scheme == nil;
NSString *hostname = @"";
if(!self.cdvIsFileScheme) {
if(scheme == nil || [WKWebView handlesURLScheme:scheme]){
scheme = @"app";
}
vc.appScheme = scheme;
hostname = [settings cordovaSettingForKey:@"hostname"];
if(hostname == nil){
hostname = @"localhost";
}
self.CDV_ASSETS_URL = [NSString stringWithFormat:@"%@://%@", scheme, hostname];
}
CDVWebViewUIDelegate* uiDelegate = [[CDVWebViewUIDelegate alloc] initWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]];
uiDelegate.allowNewWindows = [settings cordovaBoolSettingForKey:@"AllowNewWindows" defaultValue:NO];
self.uiDelegate = uiDelegate;
CDVWebViewWeakScriptMessageHandler *weakScriptMessageHandler = [[CDVWebViewWeakScriptMessageHandler alloc] initWithScriptMessageHandler:self];
WKUserContentController* userContentController = [[WKUserContentController alloc] init];
[userContentController addScriptMessageHandler:weakScriptMessageHandler name:CDV_BRIDGE_NAME];
if(self.CDV_ASSETS_URL) {
NSString *scriptCode = [NSString stringWithFormat:@"window.CDV_ASSETS_URL = '%@';", self.CDV_ASSETS_URL];
WKUserScript *wkScript = [[WKUserScript alloc] initWithSource:scriptCode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
if (wkScript) {
[userContentController addUserScript:wkScript];
}
}
WKWebViewConfiguration* configuration = [self createConfigurationFromSettings:settings];
configuration.userContentController = userContentController;
// Do not configure the scheme handler if the scheme is default (file)
if(!self.cdvIsFileScheme) {
self.schemeHandler = [[CDVURLSchemeHandler alloc] initWithVC:vc];
[configuration setURLSchemeHandler:self.schemeHandler forURLScheme:scheme];
}
// re-create WKWebView, since we need to update configuration
WKWebView* wkWebView = [[WKWebView alloc] initWithFrame:self.engineWebView.frame configuration:configuration];
wkWebView.UIDelegate = self.uiDelegate;
/*
* This is where the "OverrideUserAgent" is handled. This will replace the entire UserAgent
* with the user defined custom UserAgent.
*/
if ([settings cordovaSettingForKey:@"OverrideUserAgent"] != nil) {
wkWebView.customUserAgent = [settings cordovaSettingForKey:@"OverrideUserAgent"];
}
self.engineWebView = wkWebView;
if ([self.viewController conformsToProtocol:@protocol(WKUIDelegate)]) {
wkWebView.UIDelegate = (id <WKUIDelegate>)self.viewController;
}
if ([self.viewController conformsToProtocol:@protocol(WKNavigationDelegate)]) {
wkWebView.navigationDelegate = (id <WKNavigationDelegate>)self.viewController;
} else {
wkWebView.navigationDelegate = (id <WKNavigationDelegate>)self;
}
if ([self.viewController conformsToProtocol:@protocol(WKScriptMessageHandler)]) {
[wkWebView.configuration.userContentController addScriptMessageHandler:(id < WKScriptMessageHandler >)self.viewController name:CDV_BRIDGE_NAME];
}
[self updateSettings:settings];
// check if content thread has died on resume
NSLog(@"%@", @"CDVWebViewEngine will reload WKWebView if required on resume");
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onAppWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification object:nil];
NSLog(@"Using WKWebView");
[self addURLObserver];
}
- (void)onReset {
[self addURLObserver];
}
static void * KVOContext = &KVOContext;
- (void)addURLObserver {
if(!IsAtLeastiOSVersion(@"9.0")){
[self.webView addObserver:self forKeyPath:@"URL" options:0 context:KVOContext];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (context == KVOContext) {
if (object == [self webView] && [keyPath isEqualToString: @"URL"] && [object valueForKeyPath:keyPath] == nil){
NSLog(@"URL is nil. Reloading WKWebView");
[(WKWebView*)_engineWebView reload];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) onAppWillEnterForeground:(NSNotification*)notification {
if ([self shouldReloadWebView]) {
NSLog(@"%@", @"CDVWebViewEngine reloading!");
[(WKWebView*)_engineWebView reload];
}
}
- (BOOL)shouldReloadWebView
{
WKWebView* wkWebView = (WKWebView*)_engineWebView;
return [self shouldReloadWebView:wkWebView.URL title:wkWebView.title];
}
- (BOOL)shouldReloadWebView:(NSURL*)location title:(NSString*)title
{
BOOL title_is_nil = (title == nil);
BOOL location_is_blank = [[location absoluteString] isEqualToString:@"about:blank"];
BOOL reload = (title_is_nil || location_is_blank);
#ifdef DEBUG
NSLog(@"%@", @"CDVWebViewEngine shouldReloadWebView::");
NSLog(@"CDVWebViewEngine shouldReloadWebView title: %@", title);
NSLog(@"CDVWebViewEngine shouldReloadWebView location: %@", [location absoluteString]);
NSLog(@"CDVWebViewEngine shouldReloadWebView reload: %u", reload);
#endif
return reload;
}
- (id)loadRequest:(NSURLRequest*)request
{
if ([self canLoadRequest:request]) { // can load, differentiate between file urls and other schemes
if(request.URL.fileURL && self.cdvIsFileScheme) {
NSURL* readAccessUrl = [request.URL URLByDeletingLastPathComponent];
return [(WKWebView*)_engineWebView loadFileURL:request.URL allowingReadAccessToURL:readAccessUrl];
} else if (request.URL.fileURL) {
NSURL* startURL = [NSURL URLWithString:((CDVViewController *)self.viewController).startPage];
NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
NSURL *url = [[NSURL URLWithString:self.CDV_ASSETS_URL] URLByAppendingPathComponent:request.URL.path];
if ([request.URL.path isEqualToString:startFilePath]) {
url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.CDV_ASSETS_URL, startURL]];
}
if(request.URL.query) {
url = [NSURL URLWithString:[@"?" stringByAppendingString:request.URL.query] relativeToURL:url];
}
if(request.URL.fragment) {
url = [NSURL URLWithString:[@"#" stringByAppendingString:request.URL.fragment] relativeToURL:url];
}
request = [NSURLRequest requestWithURL:url];
}
return [(WKWebView*)_engineWebView loadRequest:request];
} else { // can't load, print out error
NSString* errorHtml = [NSString stringWithFormat:
@"<!doctype html>"
@"<title>Error</title>"
@"<div style='font-size:2em'>"
@" <p>The WebView engine '%@' is unable to load the request: %@</p>"
@" <p>Most likely the cause of the error is that the loading of file urls is not supported in iOS %@.</p>"
@"</div>",
NSStringFromClass([self class]),
[request.URL description],
[[UIDevice currentDevice] systemVersion]
];
return [self loadHTMLString:errorHtml baseURL:nil];
}
}
- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL
{
return [(WKWebView*)_engineWebView loadHTMLString:string baseURL:baseURL];
}
- (NSURL*) URL
{
return [(WKWebView*)_engineWebView URL];
}
- (BOOL) canLoadRequest:(NSURLRequest*)request
{
// See: https://issues.apache.org/jira/browse/CB-9636
SEL wk_sel = NSSelectorFromString(CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR);
// if it's a file URL, check whether WKWebView has the selector (which is in iOS 9 and up only)
if (request.URL.fileURL) {
return [_engineWebView respondsToSelector:wk_sel];
} else {
return YES;
}
}
- (void)updateSettings:(NSDictionary*)settings
{
WKWebView* wkWebView = (WKWebView*)_engineWebView;
wkWebView.configuration.preferences.minimumFontSize = [settings cordovaFloatSettingForKey:@"MinimumFontSize" defaultValue:0.0];
/*
wkWebView.configuration.preferences.javaScriptEnabled = [settings cordovaBoolSettingForKey:@"JavaScriptEnabled" default:YES];
wkWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = [settings cordovaBoolSettingForKey:@"JavaScriptCanOpenWindowsAutomatically" default:NO];
*/
// By default, DisallowOverscroll is false (thus bounce is allowed)
BOOL bounceAllowed = !([settings cordovaBoolSettingForKey:@"DisallowOverscroll" defaultValue:NO]);
// prevent webView from bouncing
if (!bounceAllowed) {
if ([wkWebView respondsToSelector:@selector(scrollView)]) {
((UIScrollView*)[wkWebView scrollView]).bounces = NO;
} else {
for (id subview in wkWebView.subviews) {
if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
((UIScrollView*)subview).bounces = NO;
}
}
}
}
NSString* decelerationSetting = [settings cordovaSettingForKey:@"WKWebViewDecelerationSpeed"];
if (![@"fast" isEqualToString:decelerationSetting]) {
[wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal];
} else {
[wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast];
}
wkWebView.allowsBackForwardNavigationGestures = [settings cordovaBoolSettingForKey:@"AllowBackForwardNavigationGestures" defaultValue:NO];
wkWebView.allowsLinkPreview = [settings cordovaBoolSettingForKey:@"Allow3DTouchLinkPreview" defaultValue:YES];
}
- (void)updateWithInfo:(NSDictionary*)info
{
NSDictionary* scriptMessageHandlers = [info objectForKey:kCDVWebViewEngineScriptMessageHandlers];
NSDictionary* settings = [info objectForKey:kCDVWebViewEngineWebViewPreferences];
id navigationDelegate = [info objectForKey:kCDVWebViewEngineWKNavigationDelegate];
id uiDelegate = [info objectForKey:kCDVWebViewEngineWKUIDelegate];
WKWebView* wkWebView = (WKWebView*)_engineWebView;
if (scriptMessageHandlers && [scriptMessageHandlers isKindOfClass:[NSDictionary class]]) {
NSArray* allKeys = [scriptMessageHandlers allKeys];
for (NSString* key in allKeys) {
id object = [scriptMessageHandlers objectForKey:key];
if ([object conformsToProtocol:@protocol(WKScriptMessageHandler)]) {
[wkWebView.configuration.userContentController addScriptMessageHandler:object name:key];
}
}
}
if (navigationDelegate && [navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]) {
wkWebView.navigationDelegate = navigationDelegate;
}
if (uiDelegate && [uiDelegate conformsToProtocol:@protocol(WKUIDelegate)]) {
wkWebView.UIDelegate = uiDelegate;
}
if (settings && [settings isKindOfClass:[NSDictionary class]]) {
[self updateSettings:settings];
}
}
// This forwards the methods that are in the header that are not implemented here.
// Both WKWebView implement the below:
// loadHTMLString:baseURL:
// loadRequest:
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return _engineWebView;
}
- (UIView*)webView
{
return self.engineWebView;
}
#pragma mark WKScriptMessageHandler implementation
- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message
{
if (![message.name isEqualToString:CDV_BRIDGE_NAME]) {
return;
}
CDVViewController* vc = (CDVViewController*)self.viewController;
NSArray* jsonEntry = message.body; // NSString:callbackId, NSString:service, NSString:action, NSArray:args
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
if (![vc.commandQueue execute:command]) {
#ifdef DEBUG
NSError* error = nil;
NSString* commandJson = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonEntry
options:0
error:&error];
if (error == nil) {
commandJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
NSLog(@"FAILED pluginJSON = %@", commandString);
#endif
}
}
#pragma mark WKNavigationDelegate implementation
- (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation
{
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:webView]];
}
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
{
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:webView]];
}
- (void)webView:(WKWebView*)theWebView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error
{
[self webView:theWebView didFailNavigation:navigation withError:error];
}
- (void)webView:(WKWebView*)theWebView didFailNavigation:(WKNavigation*)navigation withError:(NSError*)error
{
CDVViewController* vc = (CDVViewController*)self.viewController;
NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]];
NSLog(@"%@", message);
NSURL* errorUrl = vc.errorURL;
if (errorUrl) {
NSCharacterSet *charSet = [NSCharacterSet URLFragmentAllowedCharacterSet];
errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEncodingWithAllowedCharacters:charSet]] relativeToURL:errorUrl];
NSLog(@"%@", [errorUrl absoluteString]);
[theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]];
}
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
{
[webView reload];
}
- (BOOL)defaultResourcePolicyForURL:(NSURL*)url
{
// all file:// urls are allowed
if ([url isFileURL]) {
return YES;
}
return NO;
}
- (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction*) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler
{
NSURL* url = [navigationAction.request URL];
CDVViewController* vc = (CDVViewController*)self.viewController;
/*
* Give plugins the chance to handle the url
*/
BOOL anyPluginsResponded = NO;
BOOL shouldAllowRequest = NO;
for (NSString* pluginName in vc.pluginObjects) {
CDVPlugin* plugin = [vc.pluginObjects objectForKey:pluginName];
SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
if ([plugin respondsToSelector:selector]) {
anyPluginsResponded = YES;
// https://issues.apache.org/jira/browse/CB-12497
int navType = (int)navigationAction.navigationType;
shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType));
if (!shouldAllowRequest) {
break;
}
}
}
if (anyPluginsResponded) {
return decisionHandler(shouldAllowRequest);
}
/*
* Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
*/
BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url];
if (shouldAllowNavigation) {
return decisionHandler(YES);
} else {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
}
return decisionHandler(NO);
}
#pragma mark - Plugin interface
- (void)allowsBackForwardNavigationGestures:(CDVInvokedUrlCommand*)command;
{
id value = [command argumentAtIndex:0];
if (!([value isKindOfClass:[NSNumber class]])) {
value = [NSNumber numberWithBool:NO];
}
WKWebView* wkWebView = (WKWebView*)_engineWebView;
wkWebView.allowsBackForwardNavigationGestures = [value boolValue];
}
@end
#pragma mark - CDVWebViewWeakScriptMessageHandler
@implementation CDVWebViewWeakScriptMessageHandler
- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler
{
self = [super init];
if (self) {
_scriptMessageHandler = scriptMessageHandler;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
[self.scriptMessageHandler userContentController:userContentController didReceiveScriptMessage:message];
}
@end
@@ -0,0 +1,32 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <WebKit/WebKit.h>
@interface CDVWebViewUIDelegate : NSObject <WKUIDelegate>
{
NSMutableArray<UIViewController*>* windows;
}
@property (nonatomic, copy) NSString* title;
@property (nonatomic, assign) BOOL allowNewWindows;
- (instancetype)initWithTitle:(NSString*)title;
@end
@@ -0,0 +1,163 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVWebViewUIDelegate.h"
@implementation CDVWebViewUIDelegate
- (instancetype)initWithTitle:(NSString*)title
{
self = [super init];
if (self) {
self.title = title;
windows = [[NSMutableArray alloc] init];
}
return self;
}
- (void) webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message
initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action)
{
completionHandler();
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:ok];
UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootController presentViewController:alert animated:YES completion:nil];
}
- (void) webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message
initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action)
{
completionHandler(YES);
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:ok];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action)
{
completionHandler(NO);
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:cancel];
UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootController presentViewController:alert animated:YES completion:nil];
}
- (void) webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame
completionHandler:(void (^)(NSString* result))completionHandler
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title
message:prompt
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action)
{
completionHandler(((UITextField*)alert.textFields[0]).text);
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:ok];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action)
{
completionHandler(nil);
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:cancel];
[alert addTextFieldWithConfigurationHandler:^(UITextField* textField) {
textField.text = defaultText;
}];
UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootController presentViewController:alert animated:YES completion:nil];
}
- (WKWebView*) webView:(WKWebView*)webView createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration forNavigationAction:(WKNavigationAction*)navigationAction windowFeatures:(WKWindowFeatures*)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
if (self.allowNewWindows) {
WKWebView* v = [[WKWebView alloc] initWithFrame:webView.frame configuration:configuration];
v.UIDelegate = webView.UIDelegate;
v.navigationDelegate = webView.navigationDelegate;
UIViewController* vc = [[UIViewController alloc] init];
vc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
vc.view = v;
[windows addObject:vc];
UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootController presentViewController:vc animated:YES completion:nil];
return v;
} else {
[webView loadRequest:navigationAction.request];
}
}
return nil;
}
- (void)webViewDidClose:(WKWebView*)webView
{
for (UIViewController* vc in windows) {
if (vc.view == webView) {
[vc dismissViewControllerAnimated:YES completion:nil];
[windows removeObject:vc];
break;
}
}
// We do not allow closing the primary WebView
}
@end
@@ -0,0 +1,30 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailability.h"
#import "CDVAvailabilityDeprecated.h"
#import "CDVAppDelegate.h"
#import "CDVPlugin.h"
#import "CDVPluginResult.h"
#import "CDVViewController.h"
#import "CDVCommandDelegate.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVWhitelist.h"
#import "CDVScreenOrientationDelegate.h"
#import "CDVTimer.h"
@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVViewController.h"
@interface CDVAppDelegate : NSObject <UIApplicationDelegate>{}
@property (nonatomic, strong) IBOutlet UIWindow* window;
@property (nonatomic, strong) IBOutlet CDVViewController* viewController;
@end
@@ -0,0 +1,97 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAppDelegate.h"
@implementation CDVAppDelegate
@synthesize window, viewController;
- (id)init
{
self = [super init];
return self;
}
#pragma mark UIApplicationDelegate implementation
/**
* This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up)
*/
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
CGRect screenBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:screenBounds];
self.window.autoresizesSubviews = YES;
// only set if not already set in subclass
if (self.viewController == nil) {
self.viewController = [[CDVViewController alloc] init];
}
// Set your app's start page by setting the <content src='foo.html' /> tag in config.xml.
// If necessary, uncomment the line below to override it.
// self.viewController.startPage = @"index.html";
// NOTE: To customize the view's frame size (which defaults to full screen), override
// [self.viewController viewWillAppear:] in your view controller.
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
// this happens while we are running ( in the background, or from within our own app )
// only valid if 40x-Info.plist specifies a protocol to handle
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
if (!url) {
return NO;
}
NSMutableDictionary * openURLData = [[NSMutableDictionary alloc] init];
[openURLData setValue:url forKey:@"url"];
if (options[UIApplicationOpenURLOptionsSourceApplicationKey]) {
[openURLData setValue:options[UIApplicationOpenURLOptionsSourceApplicationKey] forKey:@"sourceApplication"];
}
if (options[UIApplicationOpenURLOptionsAnnotationKey]) {
[openURLData setValue:options[UIApplicationOpenURLOptionsAnnotationKey] forKey:@"annotation"];
}
// all plugins will get the notification, and their handlers will be called
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:openURLData]];
return YES;
}
- (UIInterfaceOrientationMask)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
// iPhone doesn't support upside down by default, while the iPad does. Override to allow all orientations always, and let the root view controller decide what's allowed (the supported orientations mask gets intersected).
NSUInteger supportedInterfaceOrientations = (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationPortraitUpsideDown);
return supportedInterfaceOrientations;
}
@end
@@ -0,0 +1,118 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailabilityDeprecated.h"
#define __CORDOVA_IOS__
#define __CORDOVA_0_9_6 906
#define __CORDOVA_1_0_0 10000
#define __CORDOVA_1_1_0 10100
#define __CORDOVA_1_2_0 10200
#define __CORDOVA_1_3_0 10300
#define __CORDOVA_1_4_0 10400
#define __CORDOVA_1_4_1 10401
#define __CORDOVA_1_5_0 10500
#define __CORDOVA_1_6_0 10600
#define __CORDOVA_1_6_1 10601
#define __CORDOVA_1_7_0 10700
#define __CORDOVA_1_8_0 10800
#define __CORDOVA_1_8_1 10801
#define __CORDOVA_1_9_0 10900
#define __CORDOVA_2_0_0 20000
#define __CORDOVA_2_1_0 20100
#define __CORDOVA_2_2_0 20200
#define __CORDOVA_2_3_0 20300
#define __CORDOVA_2_4_0 20400
#define __CORDOVA_2_5_0 20500
#define __CORDOVA_2_6_0 20600
#define __CORDOVA_2_7_0 20700
#define __CORDOVA_2_8_0 20800
#define __CORDOVA_2_9_0 20900
#define __CORDOVA_3_0_0 30000
#define __CORDOVA_3_1_0 30100
#define __CORDOVA_3_2_0 30200
#define __CORDOVA_3_3_0 30300
#define __CORDOVA_3_4_0 30400
#define __CORDOVA_3_4_1 30401
#define __CORDOVA_3_5_0 30500
#define __CORDOVA_3_6_0 30600
#define __CORDOVA_3_7_0 30700
#define __CORDOVA_3_8_0 30800
#define __CORDOVA_3_9_0 30900
#define __CORDOVA_3_9_1 30901
#define __CORDOVA_3_9_2 30902
#define __CORDOVA_4_0_0 40000
#define __CORDOVA_4_0_1 40001
#define __CORDOVA_4_1_0 40100
#define __CORDOVA_4_1_1 40101
#define __CORDOVA_4_2_0 40200
#define __CORDOVA_4_2_1 40201
#define __CORDOVA_4_3_0 40300
#define __CORDOVA_4_3_1 40301
#define __CORDOVA_4_4_0 40400
#define __CORDOVA_4_5_0 40500
#define __CORDOVA_4_5_1 40501
#define __CORDOVA_4_5_2 40502
#define __CORDOVA_4_5_4 40504
#define __CORDOVA_5_0_0 50000
#define __CORDOVA_5_0_1 50001
#define __CORDOVA_5_1_0 50100
#define __CORDOVA_5_1_1 50101
#define __CORDOVA_6_0_0 60000
#define __CORDOVA_6_1_0 60100
#define __CORDOVA_6_2_0 60200
/* coho:next-version,insert-before */
#define __CORDOVA_NA 99999 /* not available */
/*
#if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_4_0_0
// do something when its at least 4.0.0
#else
// do something else (non 4.0.0)
#endif
*/
#ifndef CORDOVA_VERSION_MIN_REQUIRED
/* coho:next-version-min-required,replace-after */
#define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_6_2_0
#endif
/*
Returns YES if it is at least version specified as NSString(X)
Usage:
if (IsAtLeastiOSVersion(@"5.1")) {
// do something for iOS 5.1 or greater
}
*/
#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending)
/* Return the string version of the decimal version */
#define CDV_VERSION [NSString stringWithFormat:@"%d.%d.%d", \
(CORDOVA_VERSION_MIN_REQUIRED / 10000), \
(CORDOVA_VERSION_MIN_REQUIRED % 10000) / 100, \
(CORDOVA_VERSION_MIN_REQUIRED % 10000) % 100]
// Enable this to log all exec() calls.
#define CDV_ENABLE_EXEC_LOGGING 0
#if CDV_ENABLE_EXEC_LOGGING
#define CDV_EXEC_LOG NSLog
#else
#define CDV_EXEC_LOG(...) do { \
} while (NO)
#endif
@@ -0,0 +1,26 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#ifdef __clang__
#define CDV_DEPRECATED(version, msg) __attribute__((deprecated("Deprecated in Cordova " #version ". " msg)))
#else
#define CDV_DEPRECATED(version, msg) __attribute__((deprecated()))
#endif
@@ -0,0 +1,49 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
@class CDVPlugin;
@class CDVPluginResult;
@class CDVWhitelist;
typedef NSURL* (^ UrlTransformerBlock)(NSURL*);
@protocol CDVCommandDelegate <NSObject>
@property (nonatomic, readonly) NSDictionary* settings;
@property (nonatomic, copy) UrlTransformerBlock urlTransformer;
- (NSString*)pathForResource:(NSString*)resourcepath;
- (id)getCommandInstance:(NSString*)pluginName;
// Sends a plugin result to the JS. This is thread-safe.
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId;
// Evaluates the given JS. This is thread-safe.
- (void)evalJs:(NSString*)js;
// Can be used to evaluate JS right away instead of scheduling it on the run-loop.
// This is required for dispatch resign and pause events, but should not be used
// without reason. Without the run-loop delay, alerts used in JS callbacks may result
// in dead-lock. This method must be called from the UI thread.
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop;
// Runs the given block on a background thread using a shared thread-pool.
- (void)runInBackground:(void (^)(void))block;
@end
@@ -0,0 +1,36 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import "CDVCommandDelegate.h"
@class CDVViewController;
@class CDVCommandQueue;
@interface CDVCommandDelegateImpl : NSObject <CDVCommandDelegate>{
@private
__weak CDVViewController* _viewController;
NSRegularExpression* _callbackIdPattern;
@protected
__weak CDVCommandQueue* _commandQueue;
BOOL _delayResponses;
}
- (id)initWithViewController:(CDVViewController*)viewController;
- (void)flushCommandQueueWithDelayedJs;
@end
@@ -0,0 +1,181 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"
#import "CDVCommandQueue.h"
#import "CDVPluginResult.h"
#import "CDVViewController.h"
@implementation CDVCommandDelegateImpl
@synthesize urlTransformer;
- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_commandQueue = _viewController.commandQueue;
NSError* err = nil;
_callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9._-]" options:0 error:&err];
if (err != nil) {
// Couldn't initialize Regex
NSLog(@"Error: Couldn't initialize regex");
_callbackIdPattern = nil;
}
}
return self;
}
- (NSString*)pathForResource:(NSString*)resourcepath
{
NSBundle* mainBundle = [NSBundle mainBundle];
NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
NSString* filename = [directoryParts lastObject];
[directoryParts removeLastObject];
NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@"/"];
NSString* directoryStr = _viewController.wwwFolderName;
if ([directoryPartsJoined length] > 0) {
directoryStr = [NSString stringWithFormat:@"%@/%@", _viewController.wwwFolderName, [directoryParts componentsJoinedByString:@"/"]];
}
return [mainBundle pathForResource:filename ofType:@"" inDirectory:directoryStr];
}
- (void)flushCommandQueueWithDelayedJs
{
_delayResponses = YES;
[_commandQueue executePending];
_delayResponses = NO;
}
- (void)evalJsHelper2:(NSString*)js
{
CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]);
[_viewController.webViewEngine evaluateJavaScript:js completionHandler:^(id obj, NSError* error) {
// TODO: obj can be something other than string
if ([obj isKindOfClass:[NSString class]]) {
NSString* commandsJSON = (NSString*)obj;
if ([commandsJSON length] > 0) {
CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining.");
}
[self->_commandQueue enqueueCommandBatch:commandsJSON];
[self->_commandQueue executePending];
}
}];
}
- (void)evalJsHelper:(NSString*)js
{
// Cycle the run-loop before executing the JS.
// For _delayResponses -
// This ensures that we don't eval JS during the middle of an existing JS
// function (possible since WKWebViewDelegate callbacks can be synchronous).
// For !isMainThread -
// It's a hard error to eval on the non-UI thread.
// For !_commandQueue.currentlyExecuting -
// This works around a bug where sometimes alerts() within callbacks can cause
// dead-lock.
// If the commandQueue is currently executing, then we know that it is safe to
// execute the callback immediately.
// Using (dispatch_get_main_queue()) does *not* fix deadlocks for some reason,
// but performSelectorOnMainThread: does.
if (_delayResponses || ![NSThread isMainThread] || !_commandQueue.currentlyExecuting) {
[self performSelectorOnMainThread:@selector(evalJsHelper2:) withObject:js waitUntilDone:NO];
} else {
[self evalJsHelper2:js];
}
}
- (BOOL)isValidCallbackId:(NSString*)callbackId
{
if ((callbackId == nil) || (_callbackIdPattern == nil)) {
return NO;
}
// Disallow if too long or if any invalid characters were found.
if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
return NO;
}
return YES;
}
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId
{
CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status);
// This occurs when there is are no win/fail callbacks for the call.
if ([@"INVALID" isEqualToString:callbackId]) {
return;
}
// This occurs when the callback id is malformed.
if (![self isValidCallbackId:callbackId]) {
NSLog(@"Invalid callback id received by sendPluginResult");
return;
}
int status = [result.status intValue];
BOOL keepCallback = [result.keepCallback boolValue];
NSString* argumentsAsJSON = [result argumentsAsJSON];
BOOL debug = NO;
#ifdef DEBUG
debug = YES;
#endif
NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d, %d)", callbackId, status, argumentsAsJSON, keepCallback, debug];
[self evalJsHelper:js];
}
- (void)evalJs:(NSString*)js
{
[self evalJs:js scheduledOnRunLoop:YES];
}
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop
{
js = [NSString stringWithFormat:@"try{cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})}catch(e){console.log('exception nativeEvalAndFetch : '+e);};", js];
if (scheduledOnRunLoop) {
[self evalJsHelper:js];
} else {
[self evalJsHelper2:js];
}
}
- (id)getCommandInstance:(NSString*)pluginName
{
return [_viewController getCommandInstance:pluginName];
}
- (void)runInBackground:(void (^)(void))block
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
- (NSDictionary*)settings
{
return _viewController.settings;
}
@end
@@ -0,0 +1,39 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@class CDVInvokedUrlCommand;
@class CDVViewController;
@interface CDVCommandQueue : NSObject
@property (nonatomic, readonly) BOOL currentlyExecuting;
- (id)initWithViewController:(CDVViewController*)viewController;
- (void)dispose;
- (void)resetRequestId;
- (void)enqueueCommandBatch:(NSString*)batchJSON;
- (void)fetchCommandsFromJs;
- (void)executePending;
- (BOOL)execute:(CDVInvokedUrlCommand*)command;
@end
@@ -0,0 +1,194 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#include <objc/message.h>
#import "CDVCommandQueue.h"
#import "CDVViewController.h"
#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"
#import "CDVDebug.h"
// Parse JS on the main thread if it's shorter than this.
static const NSInteger JSON_SIZE_FOR_MAIN_THREAD = 4 * 1024; // Chosen arbitrarily.
// Execute multiple commands in one go until this many seconds have passed.
static const double MAX_EXECUTION_TIME = .008; // Half of a 60fps frame.
@interface CDVCommandQueue () {
NSInteger _lastCommandQueueFlushRequestId;
__weak CDVViewController* _viewController;
NSMutableArray* _queue;
NSTimeInterval _startExecutionTime;
}
@end
@implementation CDVCommandQueue
- (BOOL)currentlyExecuting
{
return _startExecutionTime > 0;
}
- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_queue = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dispose
{
// TODO(agrieve): Make this a zeroing weak ref once we drop support for 4.3.
_viewController = nil;
}
- (void)resetRequestId
{
_lastCommandQueueFlushRequestId = 0;
}
- (void)enqueueCommandBatch:(NSString*)batchJSON
{
if ([batchJSON length] > 0) {
NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init];
[_queue addObject:commandBatchHolder];
if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) {
[commandBatchHolder addObject:[batchJSON cdv_JSONObject]];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSMutableArray* result = [batchJSON cdv_JSONObject];
@synchronized(commandBatchHolder) {
[commandBatchHolder addObject:result];
}
[self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
});
}
}
}
- (void)fetchCommandsFromJs
{
__weak CDVCommandQueue* weakSelf = self;
NSString* js = @"cordova.require('cordova/exec').nativeFetchMessages()";
[_viewController.webViewEngine evaluateJavaScript:js
completionHandler:^(id obj, NSError* error) {
if ((error == nil) && [obj isKindOfClass:[NSString class]]) {
NSString* queuedCommandsJSON = (NSString*)obj;
CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0);
[weakSelf enqueueCommandBatch:queuedCommandsJSON];
// this has to be called here now, because fetchCommandsFromJs is now async (previously: synchronous)
[self executePending];
}
}];
}
- (void)executePending
{
// Make us re-entrant-safe.
if (_startExecutionTime > 0) {
return;
}
@try {
_startExecutionTime = [NSDate timeIntervalSinceReferenceDate];
while ([_queue count] > 0) {
NSMutableArray* commandBatchHolder = _queue[0];
NSMutableArray* commandBatch = nil;
@synchronized(commandBatchHolder) {
// If the next-up command is still being decoded, wait for it.
if ([commandBatchHolder count] == 0) {
break;
}
commandBatch = commandBatchHolder[0];
}
while ([commandBatch count] > 0) {
@autoreleasepool {
// Execute the commands one-at-a-time.
NSArray* jsonEntry = [commandBatch cdv_dequeue];
if ([commandBatch count] == 0) {
[_queue removeObjectAtIndex:0];
}
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry cdv_JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
DLog(@"FAILED pluginJSON = %@", commandString);
#endif
}
}
// Yield if we're taking too long.
if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) {
[self performSelector:@selector(executePending) withObject:nil afterDelay:0];
return;
}
}
}
} @finally
{
_startExecutionTime = 0;
}
}
- (BOOL)execute:(CDVInvokedUrlCommand*)command
{
if ((command.className == nil) || (command.methodName == nil)) {
NSLog(@"ERROR: Classname and/or methodName not found for command.");
return NO;
}
// Fetch an instance of this class
CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className];
if (!([obj isKindOfClass:[CDVPlugin class]])) {
NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className);
return NO;
}
BOOL retVal = YES;
double started = [[NSDate date] timeIntervalSince1970] * 1000.0;
// Find the proper selector to call.
NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName];
SEL normalSelector = NSSelectorFromString(methodName);
if ([obj respondsToSelector:normalSelector]) {
// [obj performSelector:normalSelector withObject:command];
((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command);
} else {
// There's no method to call, so throw an error.
NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className);
retVal = NO;
}
double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started;
if (elapsed > 10) {
NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed);
}
return retVal;
}
@end
@@ -0,0 +1,30 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
@interface CDVConfigParser : NSObject <NSXMLParserDelegate>
{
NSString* featureName;
}
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readonly, strong) NSString* startPage;
@end
@@ -0,0 +1,81 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVConfigParser.h"
@interface CDVConfigParser ()
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSString* startPage;
@end
@implementation CDVConfigParser
@synthesize pluginsDict, settings, startPage, startupPluginNames;
- (id)init
{
self = [super init];
if (self != nil) {
self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30];
self.settings = [[NSMutableDictionary alloc] initWithCapacity:30];
self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8];
featureName = nil;
}
return self;
}
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict
{
if ([elementName isEqualToString:@"preference"]) {
settings[[attributeDict[@"name"] lowercaseString]] = attributeDict[@"value"];
} else if ([elementName isEqualToString:@"feature"]) { // store feature name to use with correct parameter set
featureName = [attributeDict[@"name"] lowercaseString];
} else if ((featureName != nil) && [elementName isEqualToString:@"param"]) {
NSString* paramName = [attributeDict[@"name"] lowercaseString];
id value = attributeDict[@"value"];
if ([paramName isEqualToString:@"ios-package"]) {
pluginsDict[featureName] = value;
}
BOOL paramIsOnload = ([paramName isEqualToString:@"onload"] && [@"true" isEqualToString : value]);
BOOL attribIsOnload = [@"true" isEqualToString :[attributeDict[@"onload"] lowercaseString]];
if (paramIsOnload || attribIsOnload) {
[self.startupPluginNames addObject:featureName];
}
} else if ([elementName isEqualToString:@"content"]) {
self.startPage = attributeDict[@"src"];
}
}
- (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName
{
if ([elementName isEqualToString:@"feature"]) { // no longer handling a feature so release
featureName = nil;
}
}
- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError
{
NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]);
}
@end
@@ -0,0 +1,52 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface CDVInvokedUrlCommand : NSObject {
NSString* _callbackId;
NSString* _className;
NSString* _methodName;
NSArray* _arguments;
}
@property (nonatomic, readonly) NSArray* arguments;
@property (nonatomic, readonly) NSString* callbackId;
@property (nonatomic, readonly) NSString* className;
@property (nonatomic, readonly) NSString* methodName;
+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry;
- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName;
- (id)initFromJson:(NSArray*)jsonEntry;
// Returns the argument at the given index.
// If index >= the number of arguments, returns nil.
// If the argument at the given index is NSNull, returns nil.
- (id)argumentAtIndex:(NSUInteger)index;
// Same as above, but returns defaultValue instead of nil.
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue;
// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass;
@end
@@ -0,0 +1,116 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVInvokedUrlCommand.h"
#import "CDVJSON_private.h"
@implementation CDVInvokedUrlCommand
@synthesize arguments = _arguments;
@synthesize callbackId = _callbackId;
@synthesize className = _className;
@synthesize methodName = _methodName;
+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry
{
return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry];
}
- (id)initFromJson:(NSArray*)jsonEntry
{
id tmp = [jsonEntry objectAtIndex:0];
NSString* callbackId = tmp == [NSNull null] ? nil : tmp;
NSString* className = [jsonEntry objectAtIndex:1];
NSString* methodName = [jsonEntry objectAtIndex:2];
NSMutableArray* arguments = [jsonEntry objectAtIndex:3];
return [self initWithArguments:arguments
callbackId:callbackId
className:className
methodName:methodName];
}
- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName
{
self = [super init];
if (self != nil) {
_arguments = arguments;
_callbackId = callbackId;
_className = className;
_methodName = methodName;
}
[self massageArguments];
return self;
}
- (void)massageArguments
{
NSMutableArray* newArgs = nil;
for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {
id arg = [_arguments objectAtIndex:i];
if (![arg isKindOfClass:[NSDictionary class]]) {
continue;
}
NSDictionary* dict = arg;
NSString* type = [dict objectForKey:@"CDVType"];
if (!type || ![type isEqualToString:@"ArrayBuffer"]) {
continue;
}
NSString* data = [dict objectForKey:@"data"];
if (!data) {
continue;
}
if (newArgs == nil) {
newArgs = [NSMutableArray arrayWithArray:_arguments];
_arguments = newArgs;
}
[newArgs replaceObjectAtIndex:i withObject:[[NSData alloc] initWithBase64EncodedString:data options:0]];
}
}
- (id)argumentAtIndex:(NSUInteger)index
{
return [self argumentAtIndex:index withDefault:nil];
}
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue
{
return [self argumentAtIndex:index withDefault:defaultValue andClass:nil];
}
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass
{
if (index >= [_arguments count]) {
return defaultValue;
}
id ret = [_arguments objectAtIndex:index];
if (ret == [NSNull null]) {
ret = defaultValue;
}
if ((aClass != nil) && ![ret isKindOfClass:aClass]) {
ret = defaultValue;
}
return ret;
}
@end
@@ -0,0 +1,39 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import "CDVPlugin.h"
@interface CDVPlugin (CDVPluginResources)
/*
This will return the localized string for a key in a .bundle that is named the same as your class
For example, if your plugin class was called Foo, and you have a Spanish localized strings file, it will
try to load the desired key from Foo.bundle/es.lproj/Localizable.strings
*/
- (NSString*)pluginLocalizedString:(NSString*)key;
/*
This will return the image for a name in a .bundle that is named the same as your class
For example, if your plugin class was called Foo, and you have an image called "bar",
it will try to load the image from Foo.bundle/bar.png (and appropriately named retina versions)
*/
- (UIImage*)pluginImageResource:(NSString*)name;
@end
@@ -0,0 +1,38 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPlugin+Resources.h"
@implementation CDVPlugin (CDVPluginResources)
- (NSString*)pluginLocalizedString:(NSString*)key
{
NSBundle* bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:NSStringFromClass([self class]) ofType:@"bundle"]];
return [bundle localizedStringForKey:(key) value:nil table:nil];
}
- (UIImage*)pluginImageResource:(NSString*)name
{
NSString* resourceIdentifier = [NSString stringWithFormat:@"%@.bundle/%@", NSStringFromClass([self class]), name];
return [UIImage imageNamed:resourceIdentifier];
}
@end
@@ -0,0 +1,74 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CDVPluginResult.h"
#import "NSMutableArray+QueueAdditions.h"
#import "CDVCommandDelegate.h"
#import "CDVWebViewEngineProtocol.h"
@interface UIView (org_apache_cordova_UIView_Extension)
@property (nonatomic, weak) UIScrollView* scrollView;
@end
extern NSString* const CDVPageDidLoadNotification;
extern NSString* const CDVPluginHandleOpenURLNotification;
extern NSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification;
extern NSString* const CDVPluginResetNotification;
extern NSString* const CDVViewWillAppearNotification;
extern NSString* const CDVViewDidAppearNotification;
extern NSString* const CDVViewWillDisappearNotification;
extern NSString* const CDVViewDidDisappearNotification;
extern NSString* const CDVViewWillLayoutSubviewsNotification;
extern NSString* const CDVViewDidLayoutSubviewsNotification;
extern NSString* const CDVViewWillTransitionToSizeNotification;
@interface CDVPlugin : NSObject {}
@property (nonatomic, readonly, weak) UIView* webView;
@property (nonatomic, readonly, weak) id <CDVWebViewEngineProtocol> webViewEngine;
@property (nonatomic, weak) UIViewController* viewController;
@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;
@property (readonly, assign) BOOL hasPendingOperation;
- (void)pluginInitialize;
- (void)handleOpenURL:(NSNotification*)notification;
- (void)handleOpenURLWithApplicationSourceAndAnnotation:(NSNotification*)notification;
- (void)onAppTerminate;
- (void)onMemoryWarning;
- (void)onReset;
- (void)dispose;
/*
// see initWithWebView implementation
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
*/
- (id)appDelegate;
@end
@@ -0,0 +1,199 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPlugin.h"
#import "CDVPlugin+Private.h"
#import "CDVPlugin+Resources.h"
#import "CDVViewController.h"
#include <objc/message.h>
@implementation UIView (org_apache_cordova_UIView_Extension)
@dynamic scrollView;
- (UIScrollView*)scrollView
{
SEL scrollViewSelector = NSSelectorFromString(@"scrollView");
if ([self respondsToSelector:scrollViewSelector]) {
return ((id (*)(id, SEL))objc_msgSend)(self, scrollViewSelector);
}
return nil;
}
@end
NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification";
NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification";
NSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification = @"CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification";
NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification";
NSString* const CDVViewWillAppearNotification = @"CDVViewWillAppearNotification";
NSString* const CDVViewDidAppearNotification = @"CDVViewDidAppearNotification";
NSString* const CDVViewWillDisappearNotification = @"CDVViewWillDisappearNotification";
NSString* const CDVViewDidDisappearNotification = @"CDVViewDidDisappearNotification";
NSString* const CDVViewWillLayoutSubviewsNotification = @"CDVViewWillLayoutSubviewsNotification";
NSString* const CDVViewDidLayoutSubviewsNotification = @"CDVViewDidLayoutSubviewsNotification";
NSString* const CDVViewWillTransitionToSizeNotification = @"CDVViewWillTransitionToSizeNotification";
@interface CDVPlugin ()
@property (readwrite, assign) BOOL hasPendingOperation;
@property (nonatomic, readwrite, weak) id <CDVWebViewEngineProtocol> webViewEngine;
@end
@implementation CDVPlugin
@synthesize webViewEngine, viewController, commandDelegate, hasPendingOperation;
@dynamic webView;
// Do not override these methods. Use pluginInitialize instead.
- (instancetype)initWithWebViewEngine:(id <CDVWebViewEngineProtocol>)theWebViewEngine
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURLWithApplicationSourceAndAnnotation:) name:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebViewEngine.engineWebView];
self.webViewEngine = theWebViewEngine;
}
return self;
}
- (void)pluginInitialize
{
// You can listen to more app notifications, see:
// http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
// NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
// Added in 2.5.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
//Added in 4.3.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil];
}
- (void)dispose
{
viewController = nil;
commandDelegate = nil;
}
- (UIView*)webView
{
if (self.webViewEngine != nil) {
return self.webViewEngine.engineWebView;
}
return nil;
}
/*
// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
*/
/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)handleOpenURL:(NSNotification*)notification
{
// override to handle urls sent to your app
// register your url schemes in your App-Info.plist
NSURL* url = [notification object];
if ([url isKindOfClass:[NSURL class]]) {
/* Do your thing! */
}
}
/*
NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts
*/
- (void)handleOpenURLWithApplicationSourceAndAnnotation: (NSNotification*)notification
{
// override to handle urls sent to your app
// register your url schemes in your App-Info.plist
// The notification object is an NSDictionary which contains
// - url which is a type of NSURL
// - sourceApplication which is a type of NSString and represents the package
// id of the app that calls our app
// - annotation which a type of Property list which can be several different types
// please see https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/PropertyList.html
NSDictionary* notificationData = [notification object];
if ([notificationData isKindOfClass: NSDictionary.class]){
NSURL* url = notificationData[@"url"];
NSString* sourceApplication = notificationData[@"sourceApplication"];
id annotation = notificationData[@"annotation"];
if ([url isKindOfClass:NSURL.class] && [sourceApplication isKindOfClass:NSString.class] && annotation) {
/* Do your thing! */
}
}
}
/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)onAppTerminate
{
// override this if you need to do any cleanup on app exit
}
- (void)onMemoryWarning
{
// override to remove caches, etc
}
- (void)onReset
{
// Override to cancel any long-running requests when the WebView navigates or refreshes.
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notifications unless added using addObserverForName:object:queue:usingBlock:
}
- (id)appDelegate
{
return [[UIApplication sharedApplication] delegate];
}
@end
@@ -0,0 +1,83 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailability.h"
typedef NS_ENUM(NSUInteger, CDVCommandStatus) {
CDVCommandStatus_NO_RESULT NS_SWIFT_NAME(noResult) = 0,
CDVCommandStatus_OK NS_SWIFT_NAME(ok),
CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION NS_SWIFT_NAME(classNotFoundException),
CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION NS_SWIFT_NAME(illegalAccessException),
CDVCommandStatus_INSTANTIATION_EXCEPTION NS_SWIFT_NAME(instantiationException),
CDVCommandStatus_MALFORMED_URL_EXCEPTION NS_SWIFT_NAME(malformedUrlException),
CDVCommandStatus_IO_EXCEPTION NS_SWIFT_NAME(ioException),
CDVCommandStatus_INVALID_ACTION NS_SWIFT_NAME(invalidAction),
CDVCommandStatus_JSON_EXCEPTION NS_SWIFT_NAME(jsonException),
CDVCommandStatus_ERROR NS_SWIFT_NAME(error)
};
// This exists to preserve compatibility with early Swift plugins, who are
// using CDVCommandStatus as ObjC-style constants rather than as Swift enum
// values.
// This declares extern'ed constants (implemented in CDVPluginResult.m)
#define SWIFT_ENUM_COMPAT_HACK(enumVal) extern const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal)
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR);
#undef SWIFT_ENUM_COMPAT_HACK
@interface CDVPluginResult : NSObject {}
@property (nonatomic, strong, readonly) NSNumber* status;
@property (nonatomic, strong, readonly) id message;
@property (nonatomic, strong) NSNumber* keepCallback;
// This property can be used to scope the lifetime of another object. For example,
// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy.
@property (nonatomic, strong) id associatedObject;
- (CDVPluginResult*)init;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;
+ (void)setVerbose:(BOOL)verbose;
+ (BOOL)isVerbose;
- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;
- (NSString*)argumentsAsJSON;
@end
@@ -0,0 +1,203 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPluginResult.h"
#import "CDVJSON_private.h"
#import "CDVDebug.h"
// This exists to preserve compatibility with early Swift plugins, who are
// using CDVCommandStatus as ObjC-style constants rather than as Swift enum
// values.
// These constants alias the enum values back to their previous names.
#define SWIFT_ENUM_COMPAT_HACK(enumVal) const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal) = enumVal
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION);
SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR);
#undef SWIFT_ENUM_COMPAT_HACK
@interface CDVPluginResult ()
- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage;
@end
@implementation CDVPluginResult
@synthesize status, message, keepCallback, associatedObject;
static NSArray* org_apache_cordova_CommandStatusMsgs;
id messageFromArrayBuffer(NSData* data)
{
return @{
@"CDVType" : @"ArrayBuffer",
@"data" :[data base64EncodedStringWithOptions:0]
};
}
id massageMessage(id message)
{
if ([message isKindOfClass:[NSData class]]) {
return messageFromArrayBuffer(message);
}
return message;
}
id messageFromMultipart(NSArray* theMessages)
{
NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages];
for (NSUInteger i = 0; i < messages.count; ++i) {
[messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])];
}
return @{
@"CDVType" : @"MultiPart",
@"messages" : messages
};
}
+ (void)initialize
{
org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result",
@"OK",
@"Class not found",
@"Illegal access",
@"Instantiation error",
@"Malformed url",
@"IO error",
@"Invalid action",
@"JSON error",
@"Error",
nil];
}
- (CDVPluginResult*)init
{
return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil];
}
- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage
{
self = [super init];
if (self) {
status = @(statusOrdinal);
message = theMessage;
keepCallback = [NSNumber numberWithBool:NO];
}
return self;
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal
{
return [[self alloc] initWithStatus:statusOrdinal message:nil];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInteger:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithUnsignedInteger:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
{
NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]};
return [[self alloc] initWithStatus:statusOrdinal message:errDict];
}
- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback
{
[self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];
}
- (NSString*)argumentsAsJSON
{
id arguments = (self.message == nil ? [NSNull null] : self.message);
NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments];
NSString* argumentsJSON = [argumentsWrappedInArray cdv_JSONString];
argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)];
return argumentsJSON;
}
static BOOL gIsVerbose = NO;
+ (void)setVerbose:(BOOL)verbose
{
gIsVerbose = verbose;
}
+ (BOOL)isVerbose
{
return gIsVerbose;
}
@end
@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@protocol CDVScreenOrientationDelegate <NSObject>
- (UIInterfaceOrientationMask)supportedInterfaceOrientations;
- (BOOL)shouldAutorotate;
@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface CDVTimer : NSObject
+ (void)start:(NSString*)name;
+ (void)stop:(NSString*)name;
@end
@@ -0,0 +1,123 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVTimer.h"
#pragma mark CDVTimerItem
@interface CDVTimerItem : NSObject
@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSDate* started;
@property (nonatomic, strong) NSDate* ended;
- (void)log;
@end
@implementation CDVTimerItem
- (void)log
{
NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0);
}
@end
#pragma mark CDVTimer
@interface CDVTimer ()
@property (nonatomic, strong) NSMutableDictionary* items;
@end
@implementation CDVTimer
#pragma mark object methods
- (id)init
{
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:6];
}
return self;
}
- (void)add:(NSString*)name
{
if ([self.items objectForKey:[name lowercaseString]] == nil) {
CDVTimerItem* item = [CDVTimerItem new];
item.name = name;
item.started = [NSDate new];
[self.items setObject:item forKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' already exists.", name);
}
}
- (void)remove:(NSString*)name
{
CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]];
if (item != nil) {
item.ended = [NSDate new];
[item log];
[self.items removeObjectForKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' does not exist.", name);
}
}
- (void)removeAll
{
[self.items removeAllObjects];
}
#pragma mark class methods
+ (void)start:(NSString*)name
{
[[CDVTimer sharedInstance] add:name];
}
+ (void)stop:(NSString*)name
{
[[CDVTimer sharedInstance] remove:name];
}
+ (void)clearAll
{
[[CDVTimer sharedInstance] removeAll];
}
+ (CDVTimer*)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static CDVTimer* _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
@end
@@ -0,0 +1,34 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
#import "CDVViewController.h"
@interface CDVURLSchemeHandler : NSObject <WKURLSchemeHandler>
@property (nonatomic, strong) CDVViewController* viewController;
@property (nonatomic) CDVPlugin* schemePlugin;
- (instancetype)initWithVC:(CDVViewController *)controller;
@end
@@ -0,0 +1,137 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVURLSchemeHandler.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <objc/message.h>
@implementation CDVURLSchemeHandler
- (instancetype)initWithVC:(CDVViewController *)controller
{
self = [super init];
if (self) {
_viewController = controller;
}
return self;
}
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
NSString * startPath = [[NSBundle mainBundle] pathForResource:self.viewController.wwwFolderName ofType: nil];
NSURL * url = urlSchemeTask.request.URL;
NSString * stringToLoad = url.path;
NSString * scheme = url.scheme;
CDVViewController* vc = (CDVViewController*)self.viewController;
/*
* Give plugins the chance to handle the url
*/
BOOL anyPluginsResponded = NO;
BOOL handledRequest = NO;
NSDictionary *pluginObjects = [[vc pluginObjects] copy];
for (NSString* pluginName in pluginObjects) {
self.schemePlugin = [vc.pluginObjects objectForKey:pluginName];
SEL selector = NSSelectorFromString(@"overrideSchemeTask:");
if ([self.schemePlugin respondsToSelector:selector]) {
handledRequest = (((BOOL (*)(id, SEL, id <WKURLSchemeTask>))objc_msgSend)(self.schemePlugin, selector, urlSchemeTask));
if (handledRequest) {
anyPluginsResponded = YES;
break;
}
}
}
if (!anyPluginsResponded) {
if ([scheme isEqualToString:self.viewController.appScheme]) {
if ([stringToLoad hasPrefix:@"/_app_file_"]) {
startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_app_file_" withString:@""];
} else {
if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) {
startPath = [startPath stringByAppendingPathComponent:self.viewController.startPage];
} else {
startPath = [startPath stringByAppendingPathComponent:stringToLoad];
}
}
}
NSError * fileError = nil;
NSData * data = nil;
if ([self isMediaExtension:url.pathExtension]) {
data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError];
}
if (!data || fileError) {
data = [[NSData alloc] initWithContentsOfFile:startPath];
}
NSInteger statusCode = 200;
if (!data) {
statusCode = 404;
}
NSURL * localUrl = [NSURL URLWithString:url.absoluteString];
NSString * mimeType = [self getMimeType:url.pathExtension];
id response = nil;
if (data && [self isMediaExtension:url.pathExtension]) {
response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil];
} else {
NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"};
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers];
}
[urlSchemeTask didReceiveResponse:response];
if (data) {
[urlSchemeTask didReceiveData:data];
}
[urlSchemeTask didFinish];
}
}
- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask
{
SEL selector = NSSelectorFromString(@"stopSchemeTask:");
if (self.schemePlugin != nil && [self.schemePlugin respondsToSelector:selector]) {
(((void (*)(id, SEL, id <WKURLSchemeTask>))objc_msgSend)(self.schemePlugin, selector, urlSchemeTask));
}
}
-(NSString *) getMimeType:(NSString *)fileExtension {
if (fileExtension && ![fileExtension isEqualToString:@""]) {
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
return contentType ? contentType : @"application/octet-stream";
} else {
return @"text/html";
}
}
-(BOOL) isMediaExtension:(NSString *) pathExtension {
NSArray * mediaExtensions = @[@"m4v", @"mov", @"mp4",
@"aac", @"ac3", @"aiff", @"au", @"flac", @"m4a", @"mp3", @"wav"];
if ([mediaExtensions containsObject:pathExtension.lowercaseString]) {
return YES;
}
return NO;
}
@end
@@ -0,0 +1,78 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import <Foundation/NSJSONSerialization.h>
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVCommandDelegate.h"
#import "CDVCommandQueue.h"
#import "CDVScreenOrientationDelegate.h"
#import "CDVPlugin.h"
#import "CDVWebViewEngineProtocol.h"
@interface CDVViewController : UIViewController <CDVScreenOrientationDelegate>{
@protected
id <CDVWebViewEngineProtocol> _webViewEngine;
@protected
id <CDVCommandDelegate> _commandDelegate;
@protected
CDVCommandQueue* _commandQueue;
}
@property (nonatomic, readonly, weak) IBOutlet UIView* webView;
@property (nonatomic, readonly, strong) UIView* launchView;
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readonly, strong) NSDictionary* pluginsMap;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSXMLParser* configParser;
@property (nonatomic, readwrite, copy) NSString* appScheme;
@property (nonatomic, readwrite, copy) NSString* configFile;
@property (nonatomic, readwrite, copy) NSString* wwwFolderName;
@property (nonatomic, readwrite, copy) NSString* startPage;
@property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue;
@property (nonatomic, readonly, strong) id <CDVWebViewEngineProtocol> webViewEngine;
@property (nonatomic, readonly, strong) id <CDVCommandDelegate> commandDelegate;
/**
Takes/Gives an array of UIInterfaceOrientation (int) objects
ex. UIInterfaceOrientationPortrait
*/
@property (nonatomic, readwrite, strong) NSArray* supportedOrientations;
- (UIView*)newCordovaViewWithFrame:(CGRect)bounds;
- (NSString*)appURLScheme;
- (NSURL*)errorURL;
- (UIColor*)colorFromColorString:(NSString*)colorString CDV_DEPRECATED(7.0.0, "Use BackgroundColor in xcassets");
- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations;
- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation;
- (id)getCommandInstance:(NSString*)pluginName;
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className;
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName;
- (void)parseSettingsWithParser:(NSObject <NSXMLParserDelegate>*)delegate;
- (void)showLaunchScreen:(BOOL)visible;
@end
@@ -0,0 +1,819 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <objc/message.h>
#import "CDV.h"
#import "CDVPlugin+Private.h"
#import "CDVWebViewUIDelegate.h"
#import "CDVConfigParser.h"
#import <AVFoundation/AVFoundation.h>
#import "NSDictionary+CordovaPreferences.h"
#import "CDVCommandDelegateImpl.h"
#import <Foundation/NSCharacterSet.h>
@interface CDVViewController () { }
@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
@property (nonatomic, readwrite, strong) id <CDVWebViewEngineProtocol> webViewEngine;
@property (nonatomic, readwrite, strong) UIView* launchView;
@property (readwrite, assign) BOOL initialized;
@property (atomic, strong) NSURL* openURL;
@end
@implementation CDVViewController
@synthesize supportedOrientations;
@synthesize pluginObjects, pluginsMap, startupPluginNames;
@synthesize configParser, settings;
@synthesize wwwFolderName, startPage, initialized, openURL;
@synthesize commandDelegate = _commandDelegate;
@synthesize commandQueue = _commandQueue;
@synthesize webViewEngine = _webViewEngine;
@dynamic webView;
- (void)__init
{
if ((self != nil) && !self.initialized) {
_commandQueue = [[CDVCommandQueue alloc] initWithViewController:self];
_commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:)
name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:)
name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onWebViewPageDidLoad:)
name:CDVPageDidLoadNotification object:nil];
// read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist
self.supportedOrientations = [self parseInterfaceOrientations:
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]];
[self printVersion];
[self printMultitaskingInfo];
[self printPlatformVersionWarning];
self.initialized = YES;
}
}
- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
[self __init];
return self;
}
- (id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
[self __init];
return self;
}
- (id)init
{
self = [super init];
[self __init];
return self;
}
- (void)printVersion
{
NSLog(@"Apache Cordova native platform version %@ is starting.", CDV_VERSION);
}
- (void)printPlatformVersionWarning
{
if (!IsAtLeastiOSVersion(@"8.0")) {
NSLog(@"CRITICAL: For Cordova 4.0.0 and above, you will need to upgrade to at least iOS 8.0 or greater. Your current version of iOS is %@.",
[[UIDevice currentDevice] systemVersion]
);
}
}
- (void)printMultitaskingInfo
{
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
backgroundSupported = device.multitaskingSupported;
}
NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
exitsOnSuspend = [NSNumber numberWithBool:NO];
}
NSLog(@"Multi-tasking -> Device: %@, App: %@", (backgroundSupported ? @"YES" : @"NO"), (![exitsOnSuspend intValue]) ? @"YES" : @"NO");
}
-(NSString*)configFilePath{
NSString* path = self.configFile ?: @"config.xml";
// if path is relative, resolve it against the main bundle
if(![path isAbsolutePath]){
NSString* absolutePath = [[NSBundle mainBundle] pathForResource:path ofType:nil];
if(!absolutePath){
NSAssert(NO, @"ERROR: %@ not found in the main bundle!", path);
}
path = absolutePath;
}
// Assert file exists
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSAssert(NO, @"ERROR: %@ does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project.", path);
return nil;
}
return path;
}
- (void)parseSettingsWithParser:(NSObject <NSXMLParserDelegate>*)delegate
{
// read from config.xml in the app bundle
NSString* path = [self configFilePath];
NSURL* url = [NSURL fileURLWithPath:path];
self.configParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
if (self.configParser == nil) {
NSLog(@"Failed to initialize XML parser.");
return;
}
[self.configParser setDelegate:((id < NSXMLParserDelegate >)delegate)];
[self.configParser parse];
}
- (void)loadSettings
{
CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
[self parseSettingsWithParser:delegate];
// Get the plugin dictionary, whitelist and settings from the delegate.
self.pluginsMap = delegate.pluginsDict;
self.startupPluginNames = delegate.startupPluginNames;
self.settings = delegate.settings;
// And the start folder/page.
if(self.wwwFolderName == nil){
self.wwwFolderName = @"www";
}
if(delegate.startPage && self.startPage == nil){
self.startPage = delegate.startPage;
}
if (self.startPage == nil) {
self.startPage = @"index.html";
}
// Initialize the plugin objects dict.
self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}
- (NSURL*)appUrl
{
NSURL* appURL = nil;
if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:self.startPage];
} else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]];
} else if([self.wwwFolderName rangeOfString:@".bundle"].location != NSNotFound){
// www folder is actually a bundle
NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName];
appURL = [bundle URLForResource:self.startPage withExtension:nil];
} else if([self.wwwFolderName rangeOfString:@".framework"].location != NSNotFound){
// www folder is actually a framework
NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName];
appURL = [bundle URLForResource:self.startPage withExtension:nil];
} else {
// CB-3005 strip parameters from start page to check if page exists in resources
NSURL* startURL = [NSURL URLWithString:self.startPage];
NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
if (startFilePath == nil) {
appURL = nil;
} else {
appURL = [NSURL fileURLWithPath:startFilePath];
// CB-3005 Add on the query params or fragment.
NSString* startPageNoParentDirs = self.startPage;
NSRange r = [startPageNoParentDirs rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"?#"] options:0];
if (r.location != NSNotFound) {
NSString* queryAndOrFragment = [self.startPage substringFromIndex:r.location];
appURL = [NSURL URLWithString:queryAndOrFragment relativeToURL:appURL];
}
}
}
return appURL;
}
- (NSURL*)errorURL
{
NSURL* errorUrl = nil;
id setting = [self.settings cordovaSettingForKey:@"ErrorUrl"];
if (setting) {
NSString* errorUrlString = (NSString*)setting;
if ([errorUrlString rangeOfString:@"://"].location != NSNotFound) {
errorUrl = [NSURL URLWithString:errorUrlString];
} else {
NSURL* url = [NSURL URLWithString:(NSString*)setting];
NSString* errorFilePath = [self.commandDelegate pathForResource:[url path]];
if (errorFilePath) {
errorUrl = [NSURL fileURLWithPath:errorFilePath];
}
}
}
return errorUrl;
}
- (UIView*)webView
{
if (self.webViewEngine != nil) {
return self.webViewEngine.engineWebView;
}
return nil;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
// Load settings
[self loadSettings];
NSString* backupWebStorageType = @"cloud"; // default value
id backupWebStorage = [self.settings cordovaSettingForKey:@"BackupWebStorage"];
if ([backupWebStorage isKindOfClass:[NSString class]]) {
backupWebStorageType = backupWebStorage;
}
[self.settings setCordovaSetting:backupWebStorageType forKey:@"BackupWebStorage"];
// // Instantiate the Launch screen /////////
if (!self.launchView) {
[self createLaunchView];
}
// // Instantiate the WebView ///////////////
if (!self.webView) {
[self createGapView];
}
// /////////////////
if ([self.startupPluginNames count] > 0) {
[CDVTimer start:@"TotalPluginStartup"];
for (NSString* pluginName in self.startupPluginNames) {
[CDVTimer start:pluginName];
[self getCommandInstance:pluginName];
[CDVTimer stop:pluginName];
}
[CDVTimer stop:@"TotalPluginStartup"];
}
// /////////////////
NSURL* appURL = [self appUrl];
if (appURL) {
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[self.webViewEngine loadRequest:appReq];
} else {
NSString* loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
NSLog(@"%@", loadErr);
NSURL* errorUrl = [self errorURL];
if (errorUrl) {
errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [loadErr stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]] relativeToURL:errorUrl];
NSLog(@"%@", [errorUrl absoluteString]);
[self.webViewEngine loadRequest:[NSURLRequest requestWithURL:errorUrl]];
} else {
NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
[self.webViewEngine loadHTMLString:html baseURL:nil];
}
}
// /////////////////
UIColor* bgColor = [UIColor colorNamed:@"BackgroundColor"] ?: UIColor.whiteColor;
[self.launchView setBackgroundColor:bgColor];
[self.webView setBackgroundColor:bgColor];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillAppearNotification object:nil]];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidAppearNotification object:nil]];
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillDisappearNotification object:nil]];
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidDisappearNotification object:nil]];
}
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillLayoutSubviewsNotification object:nil]];
}
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidLayoutSubviewsNotification object:nil]];
}
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillTransitionToSizeNotification object:[NSValue valueWithCGSize:size]]];
}
- (UIColor*)colorFromColorString:(NSString*)colorString
{
// No value, nothing to do
if (!colorString) {
return nil;
}
// Validate format
NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"^(#[0-9A-F]{3}|(0x|#)([0-9A-F]{2})?[0-9A-F]{6})$" options:NSRegularExpressionCaseInsensitive error:&error];
NSUInteger countMatches = [regex numberOfMatchesInString:colorString options:0 range:NSMakeRange(0, [colorString length])];
if (!countMatches) {
return nil;
}
// #FAB to #FFAABB
if ([colorString hasPrefix:@"#"] && [colorString length] == 4) {
NSString* r = [colorString substringWithRange:NSMakeRange(1, 1)];
NSString* g = [colorString substringWithRange:NSMakeRange(2, 1)];
NSString* b = [colorString substringWithRange:NSMakeRange(3, 1)];
colorString = [NSString stringWithFormat:@"#%@%@%@%@%@%@", r, r, g, g, b, b];
}
// #RRGGBB to 0xRRGGBB
colorString = [colorString stringByReplacingOccurrencesOfString:@"#" withString:@"0x"];
// 0xRRGGBB to 0xAARRGGBB
if ([colorString hasPrefix:@"0x"] && [colorString length] == 8) {
colorString = [@"0xFF" stringByAppendingString:[colorString substringFromIndex:2]];
}
// 0xAARRGGBB to int
unsigned colorValue = 0;
NSScanner *scanner = [NSScanner scannerWithString:colorString];
if (![scanner scanHexInt:&colorValue]) {
return nil;
}
// int to UIColor
return [UIColor colorWithRed:((float)((colorValue & 0x00FF0000) >> 16))/255.0
green:((float)((colorValue & 0x0000FF00) >> 8))/255.0
blue:((float)((colorValue & 0x000000FF) >> 0))/255.0
alpha:((float)((colorValue & 0xFF000000) >> 24))/255.0];
}
- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
{
NSMutableArray* result = [[NSMutableArray alloc] init];
if (orientations != nil) {
NSEnumerator* enumerator = [orientations objectEnumerator];
NSString* orientationString;
while (orientationString = [enumerator nextObject]) {
if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]];
}
}
}
// default
if ([result count] == 0) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
}
return result;
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
NSUInteger ret = 0;
if ([self supportsOrientation:UIInterfaceOrientationPortrait]) {
ret = ret | (1 << UIInterfaceOrientationPortrait);
}
if ([self supportsOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown);
}
if ([self supportsOrientation:UIInterfaceOrientationLandscapeRight]) {
ret = ret | (1 << UIInterfaceOrientationLandscapeRight);
}
if ([self supportsOrientation:UIInterfaceOrientationLandscapeLeft]) {
ret = ret | (1 << UIInterfaceOrientationLandscapeLeft);
}
return ret;
}
- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation
{
return [self.supportedOrientations containsObject:@(orientation)];
}
- (UIView*)newCordovaViewWithFrame:(CGRect)bounds
{
NSString* defaultWebViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaDefaultWebViewEngine"];
NSString* webViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"];
if (!defaultWebViewEngineClass) {
defaultWebViewEngineClass = @"CDVWebViewEngine";
}
if (!webViewEngineClass) {
webViewEngineClass = defaultWebViewEngineClass;
}
// Find webViewEngine
if (NSClassFromString(webViewEngineClass)) {
self.webViewEngine = [[NSClassFromString(webViewEngineClass) alloc] initWithFrame:bounds];
// if a webView engine returns nil (not supported by the current iOS version) or doesn't conform to the protocol, or can't load the request, we use WKWebView
if (!self.webViewEngine || ![self.webViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)] || ![self.webViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]) {
self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
}
} else {
self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
}
if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) {
[self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass];
}
return self.webViewEngine.engineWebView;
}
- (void)createLaunchView
{
CGRect webViewBounds = self.view.bounds;
webViewBounds.origin = self.view.bounds.origin;
UIView* view = [[UIView alloc] initWithFrame:webViewBounds];
[view setAlpha:0];
NSString* launchStoryboardName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
if (launchStoryboardName != nil) {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:launchStoryboardName bundle:[NSBundle mainBundle]];
UIViewController* vc = [storyboard instantiateInitialViewController];
[view addSubview:vc.view];
}
self.launchView = view;
[self.view addSubview:view];
}
- (void)createGapView
{
CGRect webViewBounds = self.view.bounds;
webViewBounds.origin = self.view.bounds.origin;
UIView* view = [self newCordovaViewWithFrame:webViewBounds];
view.hidden = YES;
view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[self.view addSubview:view];
[self.view sendSubviewToBack:view];
}
- (void)didReceiveMemoryWarning
{
// iterate through all the plugin objects, and call hasPendingOperation
// if at least one has a pending operation, we don't call [super didReceiveMemoryWarning]
NSEnumerator* enumerator = [self.pluginObjects objectEnumerator];
CDVPlugin* plugin;
BOOL doPurge = YES;
while ((plugin = [enumerator nextObject])) {
if (plugin.hasPendingOperation) {
NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class]));
doPurge = NO;
}
}
if (doPurge) {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
// Release any cached data, images, etc. that aren't in use.
}
#pragma mark CordovaCommands
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className
{
if ([plugin respondsToSelector:@selector(setViewController:)]) {
[plugin setViewController:self];
}
if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
[plugin setCommandDelegate:_commandDelegate];
}
[self.pluginObjects setObject:plugin forKey:className];
[plugin pluginInitialize];
}
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName
{
if ([plugin respondsToSelector:@selector(setViewController:)]) {
[plugin setViewController:self];
}
if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
[plugin setCommandDelegate:_commandDelegate];
}
NSString* className = NSStringFromClass([plugin class]);
[self.pluginObjects setObject:plugin forKey:className];
[self.pluginsMap setValue:className forKey:[pluginName lowercaseString]];
[plugin pluginInitialize];
}
/**
Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned.
*/
- (id)getCommandInstance:(NSString*)pluginName
{
// first, we try to find the pluginName in the pluginsMap
// (acts as a whitelist as well) if it does not exist, we return nil
// NOTE: plugin names are matched as lowercase to avoid problems - however, a
// possible issue is there can be duplicates possible if you had:
// "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match
NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]];
if (className == nil) {
return nil;
}
id obj = [self.pluginObjects objectForKey:className];
if (!obj) {
obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
if (!obj) {
NSString* fullClassName = [NSString stringWithFormat:@"%@.%@",
NSBundle.mainBundle.infoDictionary[@"CFBundleExecutable"],
className];
obj = [[NSClassFromString(fullClassName)alloc] initWithWebViewEngine:_webViewEngine];
}
if (obj != nil) {
[self registerPlugin:obj withClassName:className];
} else {
NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName);
}
}
return obj;
}
#pragma mark -
- (NSString*)appURLScheme
{
NSString* URLScheme = nil;
NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"];
if (URLTypes != nil) {
NSDictionary* dict = [URLTypes objectAtIndex:0];
if (dict != nil) {
NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"];
if (URLSchemes != nil) {
URLScheme = [URLSchemes objectAtIndex:0];
}
}
}
return URLScheme;
}
#pragma mark -
#pragma mark UIApplicationDelegate impl
/*
This method lets your application know that it is about to be terminated and purged from memory entirely
*/
- (void)onAppWillTerminate:(NSNotification*)notification
{
// empty the tmp directory
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError* __autoreleasing err = nil;
// clear contents of NSTemporaryDirectory
NSString* tempDirectoryPath = NSTemporaryDirectory();
NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
NSString* fileName = nil;
BOOL result;
while ((fileName = [directoryEnumerator nextObject])) {
NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
result = [fileMgr removeItemAtPath:filePath error:&err];
if (!result && err) {
NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
}
}
}
- (bool)isUrlEmpty:(NSURL *)url
{
if (!url || (url == (id) [NSNull null])) {
return true;
}
NSString *urlAsString = [url absoluteString];
return (urlAsString == (id) [NSNull null] || [urlAsString length]==0 || [urlAsString isEqualToString:@"about:blank"]);
}
- (bool)checkAndReinitViewUrl
{
NSURL* appURL = [self appUrl];
if ([self isUrlEmpty: [self.webViewEngine URL]] && ![self isUrlEmpty: appURL]) {
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[self.webViewEngine loadRequest:appReq];
return true;
}
return false;
}
/*
This method is called to let your application know that it is about to move from the active to inactive state.
You should use this method to pause ongoing tasks, disable timer, ...
*/
- (void)onAppWillResignActive:(NSNotification*)notification
{
[self checkAndReinitViewUrl];
// NSLog(@"%@",@"applicationWillResignActive");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO];
}
/*
In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state.
You can use this method to undo many of the changes you made to your application upon entering the background.
invariably followed by applicationDidBecomeActive
*/
- (void)onAppWillEnterForeground:(NSNotification*)notification
{
[self checkAndReinitViewUrl];
// NSLog(@"%@",@"applicationWillEnterForeground");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"];
if (!IsAtLeastiOSVersion(@"11.0")) {
/** Clipboard fix **/
UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
NSString* string = pasteboard.string;
if (string) {
[pasteboard setValue:string forPasteboardType:@"public.text"];
}
}
}
// This method is called to let your application know that it moved from the inactive to active state.
- (void)onAppDidBecomeActive:(NSNotification*)notification
{
[self checkAndReinitViewUrl];
// NSLog(@"%@",@"applicationDidBecomeActive");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"];
}
/*
In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method
when the user quits an application that supports background execution.
*/
- (void)onAppDidEnterBackground:(NSNotification*)notification
{
[self checkAndReinitViewUrl];
// NSLog(@"%@",@"applicationDidEnterBackground");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO];
}
/**
Show the webview and fade out the intermediary view
This is to prevent the flashing of the mainViewController
*/
- (void)onWebViewPageDidLoad:(NSNotification*)notification
{
self.webView.hidden = NO;
if ([self.settings cordovaBoolSettingForKey:@"AutoHideSplashScreen" defaultValue:YES]) {
CGFloat splashScreenDelaySetting = [self.settings cordovaFloatSettingForKey:@"SplashScreenDelay" defaultValue:0];
if (splashScreenDelaySetting == 0) {
[self showLaunchScreen:NO];
} else {
// Divide by 1000 because config returns milliseconds and NSTimer takes seconds
CGFloat splashScreenDelay = splashScreenDelaySetting / 1000;
[NSTimer scheduledTimerWithTimeInterval:splashScreenDelay repeats:NO block:^(NSTimer * _Nonnull timer) {
[self showLaunchScreen:NO];
}];
}
}
}
/**
Method to be called from the plugin JavaScript to show or hide the launch screen.
*/
- (void)showLaunchScreen:(BOOL)visible
{
CGFloat fadeSplashScreenDuration = [self.settings cordovaFloatSettingForKey:@"FadeSplashScreenDuration" defaultValue:250];
// Setting minimum value for fade to 0.25 seconds
fadeSplashScreenDuration = fadeSplashScreenDuration < 250 ? 250 : fadeSplashScreenDuration;
// AnimateWithDuration takes seconds but cordova documentation specifies milliseconds
CGFloat fadeDuration = fadeSplashScreenDuration/1000;
[UIView animateWithDuration:fadeDuration animations:^{
[self.launchView setAlpha:(visible ? 1 : 0)];
}];
}
// ///////////////////////
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_commandQueue dispose];
[[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)];
[self.webViewEngine loadHTMLString:@"about:blank" baseURL:nil];
[self.pluginObjects removeAllObjects];
[self.webView removeFromSuperview];
self.webViewEngine = nil;
}
@end
@@ -0,0 +1,41 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#define kCDVWebViewEngineScriptMessageHandlers @"kCDVWebViewEngineScriptMessageHandlers"
#define kCDVWebViewEngineWKNavigationDelegate @"kCDVWebViewEngineWKNavigationDelegate"
#define kCDVWebViewEngineWKUIDelegate @"kCDVWebViewEngineWKUIDelegate"
#define kCDVWebViewEngineWebViewPreferences @"kCDVWebViewEngineWebViewPreferences"
@protocol CDVWebViewEngineProtocol <NSObject>
@property (nonatomic, strong, readonly) UIView* engineWebView;
- (id)loadRequest:(NSURLRequest*)request;
- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL;
- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler;
- (NSURL*)URL;
- (BOOL)canLoadRequest:(NSURLRequest*)request;
- (instancetype)initWithFrame:(CGRect)frame;
- (void)updateWithInfo:(NSDictionary*)info;
@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <WebKit/WebKit.h>
@interface CDVWebViewProcessPoolFactory : NSObject
@property (nonatomic, retain) WKProcessPool* sharedPool;
+(instancetype) sharedFactory;
-(WKProcessPool*) sharedProcessPool;
@end
@@ -0,0 +1,49 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
#import "CDVWebViewProcessPoolFactory.h"
static CDVWebViewProcessPoolFactory *factory = nil;
@implementation CDVWebViewProcessPoolFactory
+ (instancetype)sharedFactory
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
factory = [[CDVWebViewProcessPoolFactory alloc] init];
});
return factory;
}
- (instancetype)init
{
if (self = [super init]) {
_sharedPool = [[WKProcessPool alloc] init];
}
return self;
}
- (WKProcessPool*) sharedProcessPool {
return _sharedPool;
}
@end
@@ -0,0 +1,34 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
extern NSString* const kCDVDefaultWhitelistRejectionString;
@interface CDVWhitelist : NSObject
@property (nonatomic, copy) NSString* whitelistRejectionFormatString;
- (id)initWithArray:(NSArray*)array;
- (BOOL)schemeIsAllowed:(NSString*)scheme;
- (BOOL)URLIsAllowed:(NSURL*)url;
- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure;
- (NSString*)errorStringForURL:(NSURL*)url;
@end
@@ -0,0 +1,285 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVWhitelist.h"
NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";
@interface CDVWhitelistPattern : NSObject {
@private
NSRegularExpression* _scheme;
NSRegularExpression* _host;
NSNumber* _port;
NSRegularExpression* _path;
}
+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards;
- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path;
- (bool)matches:(NSURL*)url;
@end
@implementation CDVWhitelistPattern
+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards
{
NSString* regex = [NSRegularExpression escapedPatternForString:pattern];
if (allowWildcards) {
regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"];
/* [NSURL path] has the peculiarity that a trailing slash at the end of a path
* will be omitted. This regex tweak compensates for that.
*/
if ([regex hasSuffix:@"\\/.*"]) {
regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]];
}
}
return [NSString stringWithFormat:@"%@$", regex];
}
- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path
{
self = [super init]; // Potentially change "self"
if (self) {
if ((scheme == nil) || [scheme isEqualToString:@"*"]) {
_scheme = nil;
} else {
_scheme = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ([host isEqualToString:@"*"] || host == nil) {
_host = nil;
} else if ([host hasPrefix:@"*."]) {
_host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVWhitelistPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil];
} else {
_host = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ((port == nil) || [port isEqualToString:@"*"]) {
_port = nil;
} else {
_port = [[NSNumber alloc] initWithInteger:[port integerValue]];
}
if ((path == nil) || [path isEqualToString:@"/*"]) {
_path = nil;
} else {
_path = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil];
}
}
return self;
}
- (bool)matches:(NSURL*)url
{
return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) &&
(_host == nil || ([url host] != nil && [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])])) &&
(_port == nil || [[url port] isEqualToNumber:_port]) &&
(_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])])
;
}
@end
@interface CDVWhitelist ()
@property (nonatomic, readwrite, strong) NSMutableArray* whitelist;
@property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes;
- (void)addWhiteListEntry:(NSString*)pattern;
@end
@implementation CDVWhitelist
@synthesize whitelist, permittedSchemes, whitelistRejectionFormatString;
- (id)initWithArray:(NSArray*)array
{
self = [super init];
if (self) {
self.whitelist = [[NSMutableArray alloc] init];
self.permittedSchemes = [[NSMutableSet alloc] init];
self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
for (NSString* pattern in array) {
[self addWhiteListEntry:pattern];
}
}
return self;
}
- (BOOL)isIPv4Address:(NSString*)externalHost
{
// an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255.
// for our purposes, b can also be the wildcard character '*'
// we could use a regex to solve this problem but then I would have two problems
// anyways, this is much clearer and maintainable
NSArray* octets = [externalHost componentsSeparatedByString:@"."];
NSUInteger num_octets = [octets count];
// quick check
if (num_octets != 4) {
return NO;
}
// restrict number parsing to 0-255
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]];
[numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]];
// iterate through each octet, and test for a number between 0-255 or if it equals '*'
for (NSUInteger i = 0; i < num_octets; ++i) {
NSString* octet = [octets objectAtIndex:i];
if ([octet isEqualToString:@"*"]) { // passes - check next octet
continue;
} else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return
return NO;
}
}
return YES;
}
- (void)addWhiteListEntry:(NSString*)origin
{
if (self.whitelist == nil) {
return;
}
if ([origin isEqualToString:@"*"]) {
NSLog(@"Unlimited access to network resources");
self.whitelist = nil;
self.permittedSchemes = nil;
} else { // specific access
NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+):/?/?)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil];
NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])];
if (m != nil) {
NSRange r;
NSString* scheme = nil;
r = [m rangeAtIndex:2];
if (r.location != NSNotFound) {
scheme = [origin substringWithRange:r];
}
NSString* host = nil;
r = [m rangeAtIndex:3];
if (r.location != NSNotFound) {
host = [origin substringWithRange:r];
}
// Special case for two urls which are allowed to have empty hosts
if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) {
host = @"*";
}
NSString* port = nil;
r = [m rangeAtIndex:7];
if (r.location != NSNotFound) {
port = [origin substringWithRange:r];
}
NSString* path = nil;
r = [m rangeAtIndex:8];
if (r.location != NSNotFound) {
path = [origin substringWithRange:r];
}
if (scheme == nil) {
// XXX making it stupid friendly for people who forget to include protocol/SSL
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"http" host:host port:port path:path]];
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"https" host:host port:port path:path]];
} else {
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:scheme host:host port:port path:path]];
}
if (self.permittedSchemes != nil) {
if ([scheme isEqualToString:@"*"]) {
self.permittedSchemes = nil;
} else if (scheme != nil) {
[self.permittedSchemes addObject:scheme];
}
}
}
}
}
- (BOOL)schemeIsAllowed:(NSString*)scheme
{
if ([scheme isEqualToString:@"http"] ||
[scheme isEqualToString:@"https"] ||
[scheme isEqualToString:@"ftp"] ||
[scheme isEqualToString:@"ftps"]) {
return YES;
}
return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme];
}
- (BOOL)URLIsAllowed:(NSURL*)url
{
return [self URLIsAllowed:url logFailure:YES];
}
- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure
{
// Shortcut acceptance: Are all urls whitelisted ("*" in whitelist)?
if (whitelist == nil) {
return YES;
}
// Shortcut rejection: Check that the scheme is supported
NSString* scheme = [[url scheme] lowercaseString];
if (![self schemeIsAllowed:scheme]) {
if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
return NO;
}
// http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]]];
// If it is allowed, we are done. If not, continue to check for the actual scheme-specific list
if ([self URLIsAllowed:newUrl logFailure:NO]) {
return YES;
}
}
// Check the url against patterns in the whitelist
for (CDVWhitelistPattern* p in self.whitelist) {
if ([p matches:url]) {
return YES;
}
}
if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
// if we got here, the url host is not in the white-list, do nothing
return NO;
}
- (NSString*)errorStringForURL:(NSURL*)url
{
return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]];
}
@end
@@ -0,0 +1,35 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface NSDictionary (CordovaPreferences)
- (id)cordovaSettingForKey:(NSString*)key;
- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue;
- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue;
@end
@interface NSMutableDictionary (CordovaPreferences)
- (void)setCordovaSetting:(id)value forKey:(NSString*)key;
@end
@@ -0,0 +1,92 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "NSDictionary+CordovaPreferences.h"
#import <Foundation/Foundation.h>
@implementation NSDictionary (CordovaPreferences)
- (id)cordovaSettingForKey:(NSString*)key
{
return [self objectForKey:[key lowercaseString]];
}
- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue
{
BOOL value = defaultValue;
id prefObj = [self cordovaSettingForKey:key];
if (prefObj == nil) {
NSLog(@"The preference key \"%@\" is not defined and will default to \"%@\"",
key,
(defaultValue ? @"TRUE" : @"FALSE"));
return value;
}
if ([prefObj isKindOfClass:NSString.class]) {
prefObj = [prefObj lowercaseString];
if (
// True Case
[prefObj isEqualToString:@"true"] ||
[prefObj isEqualToString:@"1"] ||
// False Case
[prefObj isEqualToString:@"false"] ||
[prefObj isEqualToString:@"0"]
)
{
value = [prefObj isEqualToString:@"true"] || [prefObj isEqualToString:@"1"];
}
} else if (
[prefObj isKindOfClass:NSNumber.class] &&
(
[prefObj isEqual: @YES] ||
[prefObj isEqual: @NO]
)
)
{
value = [prefObj isEqual: @YES];
}
return value;
}
- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue
{
CGFloat value = defaultValue;
id prefObj = [self cordovaSettingForKey:key];
if (prefObj != nil) {
value = [prefObj floatValue];
}
return value;
}
@end
@implementation NSMutableDictionary (CordovaPreferences)
- (void)setCordovaSetting:(id)value forKey:(NSString*)key
{
[self setObject:value forKey:[key lowercaseString]];
}
@end
@@ -0,0 +1,29 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface NSMutableArray (QueueAdditions)
- (id)cdv_pop;
- (id)cdv_queueHead;
- (id)cdv_dequeue;
- (void)cdv_enqueue:(id)obj;
@end
@@ -0,0 +1,58 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "NSMutableArray+QueueAdditions.h"
@implementation NSMutableArray (QueueAdditions)
- (id)cdv_queueHead
{
if ([self count] == 0) {
return nil;
}
return [self objectAtIndex:0];
}
- (__autoreleasing id)cdv_dequeue
{
if ([self count] == 0) {
return nil;
}
id head = [self objectAtIndex:0];
if (head != nil) {
// [[head retain] autorelease]; ARC - the __autoreleasing on the return value should so the same thing
[self removeObjectAtIndex:0];
}
return head;
}
- (id)cdv_pop
{
return [self cdv_dequeue];
}
- (void)cdv_enqueue:(id)object
{
[self addObject:object];
}
@end
@@ -0,0 +1,803 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
28BFF9141F355A4E00DDF01A /* CDVLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BFF9121F355A4E00DDF01A /* CDVLogger.h */; };
28BFF9151F355A4E00DDF01A /* CDVLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BFF9131F355A4E00DDF01A /* CDVLogger.m */; };
2F4D42BC23F218BA00501999 /* CDVURLSchemeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */; };
2F4D42BD23F218BA00501999 /* CDVURLSchemeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */; };
2FCCEA17247E7366007276A8 /* CDVLaunchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */; };
2FCCEA18247E7366007276A8 /* CDVLaunchScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */; };
3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */; };
3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */; };
4E23F8FB23E16E96006CD852 /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */; };
4E23F8FC23E16E96006CD852 /* CDVWebViewUIDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
4E23F8FD23E16E96006CD852 /* CDVWebViewUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */; };
4E23F8FE23E16E96006CD852 /* CDVWebViewEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */; };
4E23F8FF23E16E96006CD852 /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */; settings = {ATTRIBUTES = (Public, ); }; };
4E23F90023E16E96006CD852 /* CDVWebViewEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */; };
4E23F90323E17FFA006CD852 /* CDVWebViewUIDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
4E714D3623F535B500A321AF /* CDVLaunchScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */; };
4E714D3823F535B500A321AF /* CDVLaunchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */; };
4F56D82D254A2EB50063F1D6 /* CDVWebViewEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */; };
4F56D830254A2ED70063F1D6 /* CDVWebViewUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */; };
4F56D833254A2ED90063F1D6 /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */; };
4F56D836254A2EE10063F1D6 /* CDVWebViewEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */; };
4F56D839254A2EE40063F1D6 /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */; };
4F56D83C254A2F2F0063F1D6 /* CDVURLSchemeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */; };
7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */; };
7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF21AB9028C008C4574 /* CDVDebug.h */; };
7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */; };
7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */; };
7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */; };
7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */; };
7ED95D351AB9029B008C4574 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D0F1AB9029B008C4574 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */; };
7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D121AB9029B008C4574 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */; };
7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */; };
7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */; };
7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */; };
7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */; };
7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D201AB9029B008C4574 /* CDVPlugin.m */; };
7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */; };
7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D241AB9029B008C4574 /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D251AB9029B008C4574 /* CDVTimer.m */; };
7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */; };
7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D531AB9029B008C4574 /* CDVWhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D541AB9029B008C4574 /* CDVWhitelist.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */; };
7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; };
7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; };
9052DE712150D040008E83D4 /* CDVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */; };
9052DE722150D040008E83D4 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */; };
9052DE732150D040008E83D4 /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */; };
9052DE742150D040008E83D4 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */; };
9052DE752150D040008E83D4 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */; };
9052DE762150D040008E83D4 /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */; };
9052DE772150D040008E83D4 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D201AB9029B008C4574 /* CDVPlugin.m */; };
9052DE782150D040008E83D4 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */; };
9052DE792150D040008E83D4 /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D251AB9029B008C4574 /* CDVTimer.m */; };
9052DE7C2150D040008E83D4 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */; };
9052DE7D2150D040008E83D4 /* CDVWhitelist.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */; };
9052DE7E2150D040008E83D4 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; };
9052DE7F2150D040008E83D4 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; };
9052DE802150D040008E83D4 /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */; };
9052DE812150D040008E83D4 /* CDVLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BFF9131F355A4E00DDF01A /* CDVLogger.m */; };
9052DE822150D040008E83D4 /* CDVGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */; };
9052DE832150D040008E83D4 /* CDVIntentAndNavigationFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */; };
9052DE842150D040008E83D4 /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */; };
9052DE892150D06B008E83D4 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF21AB9028C008C4574 /* CDVDebug.h */; };
9052DE8A2150D06B008E83D4 /* CDVJSON_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */; };
9052DE8B2150D06B008E83D4 /* CDVPlugin+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */; };
9052DE8C2150D06B008E83D4 /* CDVLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BFF9121F355A4E00DDF01A /* CDVLogger.h */; };
9052DE8D2150D06B008E83D4 /* CDVGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */; };
9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */; };
9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */; };
A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */; };
A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */; };
C0C01EB61E3911D50056E6CB /* Cordova.h in Headers */ = {isa = PBXBuildFile; fileRef = C0C01EB41E3911D50056E6CB /* Cordova.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D0F1AB9029B008C4574 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EBD1E39131A0056E6CB /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D121AB9029B008C4574 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EBE1E39131A0056E6CB /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EBF1E39131A0056E6CB /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC01E39131A0056E6CB /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC11E39131A0056E6CB /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC21E39131A0056E6CB /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC31E39131A0056E6CB /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC41E39131A0056E6CB /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC51E39131A0056E6CB /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC61E39131A0056E6CB /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC71E39131A0056E6CB /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01EC81E39131A0056E6CB /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D241AB9029B008C4574 /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01ECB1E39131A0056E6CB /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01ECC1E39131A0056E6CB /* CDVWebViewEngineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01ECD1E39131A0056E6CB /* CDVWhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01ECE1E39131A0056E6CB /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0C01ECF1E39131A0056E6CB /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
28BFF9121F355A4E00DDF01A /* CDVLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVLogger.h; sourceTree = "<group>"; };
28BFF9131F355A4E00DDF01A /* CDVLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVLogger.m; sourceTree = "<group>"; };
2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVURLSchemeHandler.h; sourceTree = "<group>"; };
2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CDVURLSchemeHandler.m; sourceTree = "<group>"; };
3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVIntentAndNavigationFilter.h; sourceTree = "<group>"; };
3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVIntentAndNavigationFilter.m; sourceTree = "<group>"; };
4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewProcessPoolFactory.m; sourceTree = "<group>"; };
4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewUIDelegate.h; sourceTree = "<group>"; };
4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewUIDelegate.m; sourceTree = "<group>"; };
4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewEngine.m; sourceTree = "<group>"; };
4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewProcessPoolFactory.h; sourceTree = "<group>"; };
4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewEngine.h; sourceTree = "<group>"; };
4E714D3223F535B500A321AF /* CDVLaunchScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVLaunchScreen.h; sourceTree = "<group>"; };
4E714D3423F535B500A321AF /* CDVLaunchScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVLaunchScreen.m; sourceTree = "<group>"; };
68A32D7114102E1C006B237C /* libCordova.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCordova.a; sourceTree = BUILT_PRODUCTS_DIR; };
7ED95CF21AB9028C008C4574 /* CDVDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVDebug.h; sourceTree = "<group>"; };
7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVJSON_private.h; sourceTree = "<group>"; };
7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVJSON_private.m; sourceTree = "<group>"; };
7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Private.h"; sourceTree = "<group>"; };
7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVHandleOpenURL.h; sourceTree = "<group>"; };
7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVHandleOpenURL.m; sourceTree = "<group>"; };
7ED95D0F1AB9029B008C4574 /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDV.h; sourceTree = "<group>"; };
7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAppDelegate.h; sourceTree = "<group>"; };
7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVAppDelegate.m; sourceTree = "<group>"; };
7ED95D121AB9029B008C4574 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailability.h; sourceTree = "<group>"; };
7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailabilityDeprecated.h; sourceTree = "<group>"; };
7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegate.h; sourceTree = "<group>"; };
7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegateImpl.h; sourceTree = "<group>"; };
7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandDelegateImpl.m; sourceTree = "<group>"; };
7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandQueue.h; sourceTree = "<group>"; };
7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandQueue.m; sourceTree = "<group>"; };
7ED95D191AB9029B008C4574 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVConfigParser.h; sourceTree = "<group>"; };
7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVConfigParser.m; sourceTree = "<group>"; };
7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVInvokedUrlCommand.h; sourceTree = "<group>"; };
7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVInvokedUrlCommand.m; sourceTree = "<group>"; };
7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Resources.h"; sourceTree = "<group>"; };
7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CDVPlugin+Resources.m"; sourceTree = "<group>"; };
7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPlugin.h; sourceTree = "<group>"; };
7ED95D201AB9029B008C4574 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPlugin.m; sourceTree = "<group>"; };
7ED95D211AB9029B008C4574 /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginResult.h; sourceTree = "<group>"; };
7ED95D221AB9029B008C4574 /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginResult.m; sourceTree = "<group>"; };
7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVScreenOrientationDelegate.h; sourceTree = "<group>"; };
7ED95D241AB9029B008C4574 /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVTimer.h; sourceTree = "<group>"; };
7ED95D251AB9029B008C4574 /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVTimer.m; sourceTree = "<group>"; };
7ED95D2A1AB9029B008C4574 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVViewController.h; sourceTree = "<group>"; };
7ED95D2B1AB9029B008C4574 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVViewController.m; sourceTree = "<group>"; };
7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewEngineProtocol.h; sourceTree = "<group>"; };
7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWhitelist.h; sourceTree = "<group>"; };
7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWhitelist.m; sourceTree = "<group>"; };
7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+CordovaPreferences.h"; sourceTree = "<group>"; };
7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+CordovaPreferences.m"; sourceTree = "<group>"; };
7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+QueueAdditions.h"; sourceTree = "<group>"; };
7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+QueueAdditions.m"; sourceTree = "<group>"; };
A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVGestureHandler.h; sourceTree = "<group>"; };
A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVGestureHandler.m; sourceTree = "<group>"; };
AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CordovaLib_Prefix.pch; sourceTree = SOURCE_ROOT; };
C0C01EB21E3911D50056E6CB /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C0C01EB41E3911D50056E6CB /* Cordova.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Cordova.h; sourceTree = "<group>"; };
C0C01EB51E3911D50056E6CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
C0C01EAE1E3911D50056E6CB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D2AAC07C0554694100DB518D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
034768DFFF38A50411DB9C8B /* Products */ = {
isa = PBXGroup;
children = (
68A32D7114102E1C006B237C /* libCordova.a */,
C0C01EB21E3911D50056E6CB /* Cordova.framework */,
);
name = Products;
sourceTree = "<group>";
};
0867D691FE84028FC02AAC07 = {
isa = PBXGroup;
children = (
7ED95D0E1AB9029B008C4574 /* Public */,
7ED95CF11AB9028C008C4574 /* Private */,
C0C01EB31E3911D50056E6CB /* Cordova */,
034768DFFF38A50411DB9C8B /* Products */,
);
sourceTree = "<group>";
};
28BFF9111F355A1D00DDF01A /* CDVLogger */ = {
isa = PBXGroup;
children = (
28BFF9121F355A4E00DDF01A /* CDVLogger.h */,
28BFF9131F355A4E00DDF01A /* CDVLogger.m */,
);
path = CDVLogger;
sourceTree = "<group>";
};
3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */ = {
isa = PBXGroup;
children = (
3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */,
3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */,
);
path = CDVIntentAndNavigationFilter;
sourceTree = "<group>";
};
4E23F8F423E16D30006CD852 /* CDVWebViewEngine */ = {
isa = PBXGroup;
children = (
4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */,
4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */,
4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */,
4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */,
);
path = CDVWebViewEngine;
sourceTree = "<group>";
};
4E714D3123F5356700A321AF /* CDVLaunchScreen */ = {
isa = PBXGroup;
children = (
4E714D3223F535B500A321AF /* CDVLaunchScreen.h */,
4E714D3423F535B500A321AF /* CDVLaunchScreen.m */,
);
path = CDVLaunchScreen;
sourceTree = "<group>";
};
7ED95CF11AB9028C008C4574 /* Private */ = {
isa = PBXGroup;
children = (
AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */,
7ED95CF21AB9028C008C4574 /* CDVDebug.h */,
7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */,
7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */,
7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */,
7ED95CF61AB9028C008C4574 /* Plugins */,
);
name = Private;
path = Classes/Private;
sourceTree = "<group>";
};
7ED95CF61AB9028C008C4574 /* Plugins */ = {
isa = PBXGroup;
children = (
4E714D3123F5356700A321AF /* CDVLaunchScreen */,
4E23F8F423E16D30006CD852 /* CDVWebViewEngine */,
28BFF9111F355A1D00DDF01A /* CDVLogger */,
A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */,
3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */,
7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */,
);
path = Plugins;
sourceTree = "<group>";
};
7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */ = {
isa = PBXGroup;
children = (
7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */,
7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */,
);
path = CDVHandleOpenURL;
sourceTree = "<group>";
};
7ED95D0E1AB9029B008C4574 /* Public */ = {
isa = PBXGroup;
children = (
7ED95D0F1AB9029B008C4574 /* CDV.h */,
7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */,
7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */,
7ED95D121AB9029B008C4574 /* CDVAvailability.h */,
7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */,
7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */,
7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */,
7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */,
7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */,
7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */,
7ED95D191AB9029B008C4574 /* CDVConfigParser.h */,
7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */,
7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */,
7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */,
7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */,
7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */,
7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */,
7ED95D201AB9029B008C4574 /* CDVPlugin.m */,
7ED95D211AB9029B008C4574 /* CDVPluginResult.h */,
7ED95D221AB9029B008C4574 /* CDVPluginResult.m */,
7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */,
7ED95D241AB9029B008C4574 /* CDVTimer.h */,
7ED95D251AB9029B008C4574 /* CDVTimer.m */,
7ED95D2A1AB9029B008C4574 /* CDVViewController.h */,
4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */,
4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */,
7ED95D2B1AB9029B008C4574 /* CDVViewController.m */,
7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */,
7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */,
7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */,
7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */,
7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */,
7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */,
7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */,
2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */,
2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */,
);
name = Public;
path = Classes/Public;
sourceTree = "<group>";
};
A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */ = {
isa = PBXGroup;
children = (
A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */,
A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */,
);
path = CDVGestureHandler;
sourceTree = "<group>";
};
C0C01EB31E3911D50056E6CB /* Cordova */ = {
isa = PBXGroup;
children = (
C0C01EB41E3911D50056E6CB /* Cordova.h */,
C0C01EB51E3911D50056E6CB /* Info.plist */,
);
path = Cordova;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
C0C01EAF1E3911D50056E6CB /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
C0C01EB61E3911D50056E6CB /* Cordova.h in Headers */,
C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */,
C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in Headers */,
C0C01EBD1E39131A0056E6CB /* CDVAvailability.h in Headers */,
C0C01EBE1E39131A0056E6CB /* CDVAvailabilityDeprecated.h in Headers */,
C0C01EBF1E39131A0056E6CB /* CDVCommandDelegate.h in Headers */,
C0C01EC01E39131A0056E6CB /* CDVCommandDelegateImpl.h in Headers */,
4F56D839254A2EE40063F1D6 /* CDVWebViewProcessPoolFactory.h in Headers */,
C0C01EC11E39131A0056E6CB /* CDVCommandQueue.h in Headers */,
C0C01EC21E39131A0056E6CB /* CDVConfigParser.h in Headers */,
C0C01EC31E39131A0056E6CB /* CDVInvokedUrlCommand.h in Headers */,
C0C01EC41E39131A0056E6CB /* CDVPlugin+Resources.h in Headers */,
C0C01EC51E39131A0056E6CB /* CDVPlugin.h in Headers */,
C0C01EC61E39131A0056E6CB /* CDVPluginResult.h in Headers */,
C0C01EC71E39131A0056E6CB /* CDVScreenOrientationDelegate.h in Headers */,
4F56D836254A2EE10063F1D6 /* CDVWebViewEngine.h in Headers */,
C0C01EC81E39131A0056E6CB /* CDVTimer.h in Headers */,
C0C01ECB1E39131A0056E6CB /* CDVViewController.h in Headers */,
C0C01ECC1E39131A0056E6CB /* CDVWebViewEngineProtocol.h in Headers */,
C0C01ECD1E39131A0056E6CB /* CDVWhitelist.h in Headers */,
C0C01ECE1E39131A0056E6CB /* NSDictionary+CordovaPreferences.h in Headers */,
4E23F90323E17FFA006CD852 /* CDVWebViewUIDelegate.h in Headers */,
C0C01ECF1E39131A0056E6CB /* NSMutableArray+QueueAdditions.h in Headers */,
9052DE892150D06B008E83D4 /* CDVDebug.h in Headers */,
9052DE8A2150D06B008E83D4 /* CDVJSON_private.h in Headers */,
9052DE8B2150D06B008E83D4 /* CDVPlugin+Private.h in Headers */,
9052DE8C2150D06B008E83D4 /* CDVLogger.h in Headers */,
9052DE8D2150D06B008E83D4 /* CDVGestureHandler.h in Headers */,
9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in Headers */,
9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */,
2FCCEA18247E7366007276A8 /* CDVLaunchScreen.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2AAC07A0554694100DB518D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7ED95D351AB9029B008C4574 /* CDV.h in Headers */,
7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */,
7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */,
7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */,
7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */,
7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */,
7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */,
4E23F8FF23E16E96006CD852 /* CDVWebViewProcessPoolFactory.h in Headers */,
7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */,
2F4D42BC23F218BA00501999 /* CDVURLSchemeHandler.h in Headers */,
7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */,
7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */,
7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */,
7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */,
7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */,
4E23F8FC23E16E96006CD852 /* CDVWebViewUIDelegate.h in Headers */,
7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */,
7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */,
7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */,
4E23F90023E16E96006CD852 /* CDVWebViewEngine.h in Headers */,
7ED95D531AB9029B008C4574 /* CDVWhitelist.h in Headers */,
7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */,
7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */,
7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */,
7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */,
7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */,
28BFF9141F355A4E00DDF01A /* CDVLogger.h in Headers */,
A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */,
3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */,
7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */,
4E714D3623F535B500A321AF /* CDVLaunchScreen.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
C0C01EB11E3911D50056E6CB /* Cordova */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0C01EB91E3911D50056E6CB /* Build configuration list for PBXNativeTarget "Cordova" */;
buildPhases = (
C0C01EAD1E3911D50056E6CB /* Sources */,
C0C01EAE1E3911D50056E6CB /* Frameworks */,
C0C01EAF1E3911D50056E6CB /* Headers */,
CEDDBB5523948D4C00506451 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = Cordova;
productName = Cordova;
productReference = C0C01EB21E3911D50056E6CB /* Cordova.framework */;
productType = "com.apple.product-type.framework";
};
D2AAC07D0554694100DB518D /* CordovaLib */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "CordovaLib" */;
buildPhases = (
D2AAC07A0554694100DB518D /* Headers */,
D2AAC07B0554694100DB518D /* Sources */,
D2AAC07C0554694100DB518D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = CordovaLib;
productName = CordovaLib;
productReference = 68A32D7114102E1C006B237C /* libCordova.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1010;
TargetAttributes = {
C0C01EB11E3911D50056E6CB = {
CreatedOnToolsVersion = 10.1;
ProvisioningStyle = Automatic;
};
D2AAC07D0554694100DB518D = {
CreatedOnToolsVersion = 10.1;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "CordovaLib" */;
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 0867D691FE84028FC02AAC07;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
D2AAC07D0554694100DB518D /* CordovaLib */,
C0C01EB11E3911D50056E6CB /* Cordova */,
);
};
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
CEDDBB5523948D4C00506451 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PROJECT_DIR}/../update_podspec.sh\" -s cordova\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
C0C01EAD1E3911D50056E6CB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9052DE712150D040008E83D4 /* CDVAppDelegate.m in Sources */,
9052DE722150D040008E83D4 /* CDVCommandDelegateImpl.m in Sources */,
9052DE732150D040008E83D4 /* CDVCommandQueue.m in Sources */,
9052DE742150D040008E83D4 /* CDVConfigParser.m in Sources */,
9052DE752150D040008E83D4 /* CDVInvokedUrlCommand.m in Sources */,
9052DE762150D040008E83D4 /* CDVPlugin+Resources.m in Sources */,
9052DE772150D040008E83D4 /* CDVPlugin.m in Sources */,
9052DE782150D040008E83D4 /* CDVPluginResult.m in Sources */,
4F56D830254A2ED70063F1D6 /* CDVWebViewUIDelegate.m in Sources */,
9052DE792150D040008E83D4 /* CDVTimer.m in Sources */,
4F56D833254A2ED90063F1D6 /* CDVWebViewProcessPoolFactory.m in Sources */,
4F56D82D254A2EB50063F1D6 /* CDVWebViewEngine.m in Sources */,
9052DE7C2150D040008E83D4 /* CDVViewController.m in Sources */,
4F56D83C254A2F2F0063F1D6 /* CDVURLSchemeHandler.m in Sources */,
9052DE7D2150D040008E83D4 /* CDVWhitelist.m in Sources */,
9052DE7E2150D040008E83D4 /* NSDictionary+CordovaPreferences.m in Sources */,
9052DE7F2150D040008E83D4 /* NSMutableArray+QueueAdditions.m in Sources */,
9052DE802150D040008E83D4 /* CDVJSON_private.m in Sources */,
9052DE812150D040008E83D4 /* CDVLogger.m in Sources */,
9052DE822150D040008E83D4 /* CDVGestureHandler.m in Sources */,
9052DE832150D040008E83D4 /* CDVIntentAndNavigationFilter.m in Sources */,
9052DE842150D040008E83D4 /* CDVHandleOpenURL.m in Sources */,
2FCCEA17247E7366007276A8 /* CDVLaunchScreen.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2AAC07B0554694100DB518D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */,
7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */,
7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */,
7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */,
7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */,
7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */,
2F4D42BD23F218BA00501999 /* CDVURLSchemeHandler.m in Sources */,
7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */,
7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */,
7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */,
4E23F8FE23E16E96006CD852 /* CDVWebViewEngine.m in Sources */,
4E23F8FB23E16E96006CD852 /* CDVWebViewProcessPoolFactory.m in Sources */,
7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */,
7ED95D541AB9029B008C4574 /* CDVWhitelist.m in Sources */,
7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */,
7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */,
7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */,
4E23F8FD23E16E96006CD852 /* CDVWebViewUIDelegate.m in Sources */,
28BFF9151F355A4E00DDF01A /* CDVLogger.m in Sources */,
A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */,
3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */,
7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */,
4E714D3823F535B500A321AF /* CDVLaunchScreen.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB921F08733DC00010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PUBLIC_HEADERS_FOLDER_PATH = include/Cordova;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
1DEB922008733DC00010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PUBLIC_HEADERS_FOLDER_PATH = include/Cordova;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
1DEB922308733DC00010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = CordovaLib_Prefix.pch;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = Cordova;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
};
name = Debug;
};
1DEB922408733DC00010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = CordovaLib_Prefix.pch;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = Cordova;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
C0C01EB71E3911D50056E6CB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Cordova/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.apache.cordova.Cordova;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
C0C01EB81E3911D50056E6CB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Cordova/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.apache.cordova.Cordova;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "CordovaLib" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB921F08733DC00010E9CD /* Debug */,
1DEB922008733DC00010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "CordovaLib" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB922308733DC00010E9CD /* Debug */,
1DEB922408733DC00010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0C01EB91E3911D50056E6CB /* Build configuration list for PBXNativeTarget "Cordova" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0C01EB71E3911D50056E6CB /* Debug */,
C0C01EB81E3911D50056E6CB /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
}
@@ -0,0 +1,22 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif
+1
View File
@@ -0,0 +1 @@
6.2.0
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
# DO NOT MODIFY -- auto-generated by Apache Cordova
platform :ios, '11.0'
target 'C:\Users\tiago.kayaya\development\gabinete-digital\platforms\ios\gabinete digital' do
project 'C:\Users\tiago.kayaya\development\gabinete-digital\platforms\ios\gabinete digital.xcodeproj'
end
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# Licensed Materials - Property of IBM
#
# (C) Copyright 2015 IBM Corp.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Update buildtime in project
echo -n ${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/worklight.plist | xargs -0 /usr/libexec/PlistBuddy -c "Set :buildtime `date +\"%s\"`"
+706
View File
@@ -0,0 +1,706 @@
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/* jslint node: true */
/**
* @todo update coho to update this line.
* @todo use `package.json` instead but first
* figure out how this fit in with the platform-centered workflow structure.
* This workflow would not have the `package.json` file.
*/
// Coho updates this line
const VERSION = '6.2.0';
const fs = require('fs-extra');
const path = require('path');
const unorm = require('unorm');
const projectFile = require('./lib/projectFile');
const check_reqs = require('./lib/check_reqs');
const CordovaError = require('cordova-common').CordovaError;
const CordovaLogger = require('cordova-common').CordovaLogger;
const events = require('cordova-common').events;
const PluginManager = require('cordova-common').PluginManager;
const util = require('util');
const xcode = require('xcode');
const ConfigParser = require('cordova-common').ConfigParser;
function setupEvents (externalEventEmitter) {
if (externalEventEmitter) {
// This will make the platform internal events visible outside
events.forwardEventsTo(externalEventEmitter);
} else {
// There is no logger if external emitter is not present,
// so attach a console logger
CordovaLogger.get().subscribe(events);
}
}
function getVariableSpec (spec, options) {
return spec.includes('$') ? options.cli_variables[spec.replace('$', '')] : spec;
}
/**
* Creates a new PlatformApi instance.
*
* @param {String} [platform] Platform name, used for backward compatibility
* w/ PlatformPoly (CordovaLib).
* @param {String} [platformRootDir] Platform root location, used for backward
* compatibility w/ PlatformPoly (CordovaLib).
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*/
function Api (platform, platformRootDir, events) {
// 'platform' property is required as per PlatformApi spec
this.platform = platform || 'ios';
this.root = platformRootDir || path.resolve(__dirname, '..');
setupEvents(events);
let xcodeProjDir;
let xcodeCordovaProj;
try {
const xcodeProjDir_array = fs.readdirSync(this.root).filter(e => e.match(/\.xcodeproj$/i));
if (xcodeProjDir_array.length > 1) {
for (let x = 0; x < xcodeProjDir_array.length; x++) {
if (xcodeProjDir_array[x].substring(0, 2) === '._') {
xcodeProjDir_array.splice(x, 1);
}
}
}
xcodeProjDir = xcodeProjDir_array[0];
if (!xcodeProjDir) {
throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`);
}
const cordovaProjName = xcodeProjDir.substring(xcodeProjDir.lastIndexOf(path.sep) + 1, xcodeProjDir.indexOf('.xcodeproj'));
xcodeCordovaProj = path.join(this.root, cordovaProjName);
} catch (e) {
throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`);
}
this.locations = {
root: this.root,
www: path.join(this.root, 'www'),
platformWww: path.join(this.root, 'platform_www'),
configXml: path.join(xcodeCordovaProj, 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'),
pbxproj: path.join(this.root, xcodeProjDir, 'project.pbxproj'),
xcodeProjDir: path.join(this.root, xcodeProjDir),
xcodeCordovaProj
};
}
/**
* Creates platform in a specified directory.
*
* @param {String} destination Destination directory, where install platform to
* @param {ConfigParser} [config] ConfigParser instance, used to retrieve
* project creation options, such as package id and project name.
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.createPlatform = (destination, config, options, events) => {
setupEvents(events);
// CB-6992 it is necessary to normalize characters
// because node and shell scripts handles unicode symbols differently
// We need to normalize the name to NFD form since iOS uses NFD unicode form
const name = unorm.nfd(config.name());
let result;
try {
result = require('../../../lib/create')
.createProject(destination, config.getAttribute('ios-CFBundleIdentifier') || config.packageName(), name, options, config)
.then(() => {
// after platform is created we return Api instance based on new Api.js location
// This is required to correctly resolve paths in the future api calls
const PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('ios', destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the iOS project API.');
throw e;
}
return result;
};
/**
* Updates already installed platform.
*
* @param {String} destination Destination directory, where platform installed
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.updatePlatform = (destination, options, events) => {
setupEvents(events);
let result;
try {
result = require('../../../lib/create')
.updateProject(destination, options)
.then(() => {
const PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('ios', destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the iOS project API, you will need to do this manually.');
throw e;
}
return result;
};
/**
* Gets a CordovaPlatform object, that represents the platform structure.
*
* @return {CordovaPlatform} A structure that contains the description of
* platform's file structure and other properties of platform.
*/
Api.prototype.getPlatformInfo = function () {
const result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
result.version = Api.version();
result.projectConfig = new ConfigParser(this.locations.configXml);
return result;
};
/**
* Updates installed platform with provided www assets and new app
* configuration. This method is required for CLI workflow and will be called
* each time before build, so the changes, made to app configuration and www
* code, will be applied to platform.
*
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
* project structure and configuration, that should be applied to platform
* (contains project's www location and ConfigParser instance for project's
* config).
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.prepare = function (cordovaProject) {
cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path);
return require('./lib/prepare').prepare.call(this, cordovaProject);
};
/**
* Installs a new plugin into platform. It doesn't resolves plugin dependencies.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} installOptions An options object. Possible options below:
* @param {Boolean} installOptions.link: Flag that specifies that plugin
* sources will be symlinked to app's directory instead of copying (if
* possible).
* @param {Object} installOptions.variables An object that represents
* variables that will be used to install plugin. See more details on plugin
* variables in documentation:
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.addPlugin = function (plugin, installOptions) {
const xcodeproj = projectFile.parse(this.locations);
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = xcodeproj.getPackageName();
}
return PluginManager.get(this.platform, this.locations, xcodeproj)
.addPlugin(plugin, installOptions)
.then(() => {
if (plugin != null) {
const headerTags = plugin.getHeaderFiles(this.platform);
const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader');
if (bridgingHeaders.length > 0) {
const project_dir = this.locations.root;
const project_name = this.locations.xcodeCordovaProj.split('/').pop();
const BridgingHeader = require('./lib/BridgingHeader').BridgingHeader;
const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h'));
events.emit('verbose', 'Adding Bridging-Headers since the plugin contained <header-file> with type="BridgingHeader"');
bridgingHeaders.forEach(obj => {
const bridgingHeaderPath = path.basename(obj.src);
bridgingHeaderFile.addHeader(plugin.id, bridgingHeaderPath);
});
bridgingHeaderFile.write();
}
}
})
.then(() => {
if (plugin != null) {
const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : [];
const frameworkTags = plugin.getFrameworks(this.platform);
const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec');
return this.addPodSpecs(plugin, podSpecs, frameworkPods, installOptions);
}
})
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
};
/**
* Removes an installed plugin from platform.
*
* Since method accepts PluginInfo instance as input parameter instead of plugin
* id, caller shoud take care of managing/storing PluginInfo instances for
* future uninstalls.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
const xcodeproj = projectFile.parse(this.locations);
return PluginManager.get(this.platform, this.locations, xcodeproj)
.removePlugin(plugin, uninstallOptions)
.then(() => {
if (plugin != null) {
const headerTags = plugin.getHeaderFiles(this.platform);
const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader');
if (bridgingHeaders.length > 0) {
const project_dir = this.locations.root;
const project_name = this.locations.xcodeCordovaProj.split('/').pop();
const BridgingHeader = require('./lib/BridgingHeader').BridgingHeader;
const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h'));
events.emit('verbose', 'Removing Bridging-Headers since the plugin contained <header-file> with type="BridgingHeader"');
bridgingHeaders.forEach(obj => {
const bridgingHeaderPath = path.basename(obj.src);
bridgingHeaderFile.removeHeader(plugin.id, bridgingHeaderPath);
});
bridgingHeaderFile.write();
}
}
})
.then(() => {
if (plugin != null) {
const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : [];
const frameworkTags = plugin.getFrameworks(this.platform);
const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec');
return this.removePodSpecs(plugin, podSpecs, frameworkPods, uninstallOptions);
}
})
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
};
/**
* adding CocoaPods libraries
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform)
* @param {Object} frameworkPods: framework tags object with type === 'podspec'
* @return {Promise} Return a promise
*/
Api.prototype.addPodSpecs = function (plugin, podSpecs, frameworkPods, installOptions) {
const project_dir = this.locations.root;
const project_name = this.locations.xcodeCordovaProj.split('/').pop();
const minDeploymentTarget = this.getPlatformInfo().projectConfig.getPreference('deployment-target', 'ios');
const Podfile = require('./lib/Podfile').Podfile;
const PodsJson = require('./lib/PodsJson').PodsJson;
const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name, minDeploymentTarget);
if (podSpecs.length) {
events.emit('verbose', 'Adding pods since the plugin contained <podspecs>');
podSpecs.forEach(obj => {
// declarations
if (obj.declarations) {
Object.keys(obj.declarations).forEach(key => {
if (obj.declarations[key] === 'true') {
const declaration = Podfile.proofDeclaration(key);
const podJson = {
declaration
};
const val = podsjsonFile.getDeclaration(declaration);
if (val) {
podsjsonFile.incrementDeclaration(declaration);
} else {
podJson.count = 1;
podsjsonFile.setJsonDeclaration(declaration, podJson);
podfileFile.addDeclaration(podJson.declaration);
}
}
});
}
// sources
if (obj.sources) {
Object.keys(obj.sources).forEach(key => {
const podJson = {
source: obj.sources[key].source
};
const val = podsjsonFile.getSource(key);
if (val) {
podsjsonFile.incrementSource(key);
} else {
podJson.count = 1;
podsjsonFile.setJsonSource(key, podJson);
podfileFile.addSource(podJson.source);
}
});
}
// libraries
if (obj.libraries) {
Object.keys(obj.libraries).forEach(key => {
const podJson = Object.assign({}, obj.libraries[key]);
if (podJson.spec) {
podJson.spec = getVariableSpec(podJson.spec, installOptions);
}
const val = podsjsonFile.getLibrary(key);
if (val) {
events.emit('warn', `${plugin.id} depends on ${podJson.name}, which may conflict with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`);
podsjsonFile.incrementLibrary(key);
} else {
podJson.count = 1;
podsjsonFile.setJsonLibrary(key, podJson);
podfileFile.addSpec(podJson.name, podJson);
}
});
}
});
}
if (frameworkPods.length) {
events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.');
events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type="podspec"');
frameworkPods.forEach(obj => {
const spec = getVariableSpec(obj.spec, installOptions);
const podJson = {
name: obj.src,
type: obj.type,
spec
};
const val = podsjsonFile.getLibrary(podJson.name);
if (val) { // found
if (podJson.spec !== val.spec) { // exists, different spec, print warning
events.emit('warn', `${plugin.id} depends on ${podJson.name}@${podJson.spec}, which conflicts with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`);
}
// increment count, but don't add in Podfile because it already exists
podsjsonFile.incrementLibrary(podJson.name);
} else { // not found, write new
podJson.count = 1;
podsjsonFile.setJsonLibrary(podJson.name, podJson);
// add to Podfile
podfileFile.addSpec(podJson.name, podJson.spec);
}
});
}
if (podSpecs.length > 0 || frameworkPods.length > 0) {
// now that all the pods have been processed, write to pods.json
podsjsonFile.write();
// only write and pod install if the Podfile changed
if (podfileFile.isDirty()) {
podfileFile.write();
events.emit('verbose', 'Running `pod install` (to install plugins)');
projectFile.purgeProjectFileCache(this.locations.root);
return podfileFile.install(check_reqs.check_cocoapods)
.then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile));
} else {
events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
}
}
return Promise.resolve();
};
/**
* removing CocoaPods libraries
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform)
* @param {Object} frameworkPods: framework tags object with type === 'podspec'
* @return {Promise} Return a promise
*/
Api.prototype.removePodSpecs = function (plugin, podSpecs, frameworkPods, uninstallOptions) {
const project_dir = this.locations.root;
const project_name = this.locations.xcodeCordovaProj.split('/').pop();
const Podfile = require('./lib/Podfile').Podfile;
const PodsJson = require('./lib/PodsJson').PodsJson;
const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name);
if (podSpecs.length) {
events.emit('verbose', 'Adding pods since the plugin contained <podspecs>');
podSpecs.forEach(obj => {
// declarations
Object.keys(obj.declarations).forEach(key => {
if (obj.declarations[key] === 'true') {
const declaration = Podfile.proofDeclaration(key);
const podJson = {
declaration
};
const val = podsjsonFile.getDeclaration(declaration);
if (val) {
podsjsonFile.decrementDeclaration(declaration);
} else {
const message = util.format('plugin \"%s\" declaration \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.declaration); /* eslint no-useless-escape : 0 */
events.emit('verbose', message);
}
if (!val || val.count === 0) {
podfileFile.removeDeclaration(podJson.declaration);
}
}
});
// sources
Object.keys(obj.sources).forEach(key => {
const podJson = {
source: obj.sources[key].source
};
const val = podsjsonFile.getSource(key);
if (val) {
podsjsonFile.decrementSource(key);
} else {
const message = util.format('plugin \"%s\" source \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.source); /* eslint no-useless-escape : 0 */
events.emit('verbose', message);
}
if (!val || val.count === 0) {
podfileFile.removeSource(podJson.source);
}
});
// libraries
Object.keys(obj.libraries).forEach(key => {
const podJson = Object.assign({}, obj.libraries[key]);
if (podJson.spec) {
podJson.spec = getVariableSpec(podJson.spec, uninstallOptions);
}
const val = podsjsonFile.getLibrary(key);
if (val) {
podsjsonFile.decrementLibrary(key);
} else {
const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */
events.emit('verbose', message);
}
if (!val || val.count === 0) {
podfileFile.removeSpec(podJson.name);
}
});
});
}
if (frameworkPods.length) {
events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.');
events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type=\"podspec\"'); /* eslint no-useless-escape : 0 */
frameworkPods.forEach(obj => {
const spec = getVariableSpec(obj.spec, uninstallOptions);
const podJson = {
name: obj.src,
type: obj.type,
spec
};
const val = podsjsonFile.getLibrary(podJson.name);
if (val) { // found, decrement count
podsjsonFile.decrementLibrary(podJson.name);
} else { // not found (perhaps a sync error)
const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */
events.emit('verbose', message);
}
// always remove from the Podfile
podfileFile.removeSpec(podJson.name);
});
}
if (podSpecs.length > 0 || frameworkPods.length > 0) {
// now that all the pods have been processed, write to pods.json
podsjsonFile.write();
if (podfileFile.isDirty()) {
podfileFile.write();
events.emit('verbose', 'Running `pod install` (to uninstall pods)');
return podfileFile.install(check_reqs.check_cocoapods)
.then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile));
} else {
events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
}
}
return Promise.resolve();
};
/**
* set Swift Version for all CocoaPods libraries
*
* @param {PodsJson} podsjsonFile A PodsJson instance that represents pods.json
*/
Api.prototype.setSwiftVersionForCocoaPodsLibraries = function (podsjsonFile) {
let __dirty = false;
return check_reqs.check_cocoapods().then(toolOptions => {
if (toolOptions.ignore) {
events.emit('verbose', '=== skip Swift Version Settings For Cocoapods Libraries');
} else {
const podPbxPath = path.join(this.root, 'Pods', 'Pods.xcodeproj', 'project.pbxproj');
const podXcodeproj = xcode.project(podPbxPath);
podXcodeproj.parseSync();
const podTargets = podXcodeproj.pbxNativeTargetSection();
const podConfigurationList = podXcodeproj.pbxXCConfigurationList();
const podConfigs = podXcodeproj.pbxXCBuildConfigurationSection();
const libraries = podsjsonFile.getLibraries();
Object.keys(libraries).forEach(key => {
const podJson = libraries[key];
const name = podJson.name;
const swiftVersion = podJson['swift-version'];
if (swiftVersion) {
__dirty = true;
Object.keys(podTargets)
.filter(targetKey => podTargets[targetKey].productName === name)
.map(targetKey => podTargets[targetKey].buildConfigurationList)
.map(buildConfigurationListId => podConfigurationList[buildConfigurationListId])
.map(buildConfigurationList => buildConfigurationList.buildConfigurations)
.reduce((acc, buildConfigurations) => acc.concat(buildConfigurations), [])
.map(buildConfiguration => buildConfiguration.value)
.forEach(buildId => {
__dirty = true;
podConfigs[buildId].buildSettings.SWIFT_VERSION = swiftVersion;
});
}
});
if (__dirty) {
fs.writeFileSync(podPbxPath, podXcodeproj.writeSync(), 'utf-8');
}
}
});
};
/**
* Builds an application package for current platform.
*
* @param {Object} buildOptions A build options. This object's structure is
* highly depends on platform's specific. The most common options are:
* @param {Boolean} buildOptions.debug Indicates that packages should be
* built with debug configuration. This is set to true by default unless the
* 'release' option is not specified.
* @param {Boolean} buildOptions.release Indicates that packages should be
* built with release configuration. If not set to true, debug configuration
* will be used.
* @param {Boolean} buildOptions.device Specifies that built app is intended
* to run on device
* @param {Boolean} buildOptions.emulator: Specifies that built app is
* intended to run on emulator
* @param {String} buildOptions.target Specifies the device id that will be
* used to run built application.
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
* dry-run call, so no build artifacts will be produced.
* @param {String[]} buildOptions.archs Specifies chip architectures which
* app packages should be built for. List of valid architectures is depends on
* platform.
* @param {String} buildOptions.buildConfig The path to build configuration
* file. The format of this file is depends on platform.
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
* passed to `build` command. The purpose of this property is to pass a
* platform-specific arguments, and eventually let platform define own
* arguments processing logic.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.build = function (buildOptions) {
return check_reqs.run()
.then(() => require('./lib/build').run.call(this, buildOptions));
};
/**
* Builds an application package for current platform and runs it on
* specified/default device. If no 'device'/'emulator'/'target' options are
* specified, then tries to run app on default device if connected, otherwise
* runs the app on emulator.
*
* @param {Object} runOptions An options object. The structure is the same
* as for build options.
*
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
Api.prototype.run = function (runOptions) {
return check_reqs.run()
.then(() => require('./lib/run').run.call(this, runOptions));
};
/**
* Cleans out the build artifacts from platform's directory.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function (cleanOptions) {
return check_reqs.run()
.then(() => require('./lib/clean').run.call(this, cleanOptions))
.then(() => require('./lib/prepare').clean.call(this, cleanOptions));
};
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
* built successfully.
*
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
Api.prototype.requirements = function () {
return check_reqs.check_all();
};
Api.version = function () {
return VERSION;
};
module.exports = Api;
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var versions = require('./lib/versions.js');
versions.get_apple_ios_version().catch(err => {
console.log(err);
process.exit(2);
});
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var versions = require('./lib/versions.js');
versions.get_apple_osx_version().catch(err => {
console.log(err);
process.exit(2);
});
@@ -0,0 +1,32 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var versions = require('./lib/versions.js');
versions.get_apple_xcode_version().then(
version => {
console.log(version);
},
err => {
console.error(err);
process.exit(2);
}
);
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var args = process.argv;
var Api = require('./Api');
var nopt = require('nopt');
// Support basic help commands
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
require('./lib/build').help();
process.exit(0);
}
// Parse arguments
var buildOpts = nopt({
verbose: Boolean,
silent: Boolean,
archs: String,
debug: Boolean,
release: Boolean,
device: Boolean,
emulator: Boolean,
codeSignIdentity: String,
codeSignResourceRules: String,
provisioningProfile: String,
automaticProvisioning: Boolean,
developmentTeam: String,
packageType: String,
buildConfig: String,
buildFlag: [String, Array],
noSign: Boolean
}, { '-r': '--release', d: '--verbose' }, args);
// Make buildOptions compatible with PlatformApi build method spec
buildOpts.argv = buildOpts.argv.remain;
require('./loggingHelper').adjustLoggerLevel(buildOpts);
new Api().build(buildOpts).then(
() => {
console.log('** BUILD SUCCEEDED **');
},
err => {
var errorMessage = (err && err.stack) ? err.stack : err;
console.error(errorMessage);
process.exit(2);
}
);
@@ -0,0 +1,32 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
//
// XCode Build settings for "Debug" Build Configuration.
//
#include "build.xcconfig"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1
#include "build-extras.xcconfig"
// (CB-11792)
// @COCOAPODS_SILENCE_WARNINGS@ //
#include "../pods-debug.xcconfig"
@@ -0,0 +1,22 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
//
// Auto-generated config file to override configuration files (build-release/build-debug).
//
@@ -0,0 +1,33 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
//
// XCode Build settings for "Release" Build Configuration.
//
#include "build.xcconfig"
CODE_SIGN_IDENTITY = iPhone Distribution
CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Distribution
#include "build-extras.xcconfig"
// (CB-11792)
// @COCOAPODS_SILENCE_WARNINGS@ //
#include "../pods-release.xcconfig"
+19
View File
@@ -0,0 +1,19 @@
:: Licensed to the Apache Software Foundation (ASF) under one
:: or more contributor license agreements. See the NOTICE file
:: distributed with this work for additional information
:: regarding copyright ownership. The ASF licenses this file
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
:: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations
:: under the License
@ECHO OFF
ECHO WARN: The 'build' command is not available for cordova-ios on windows machines.>&2
+47
View File
@@ -0,0 +1,47 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
//
// XCode build settings shared by all Build Configurations.
// Settings are overridden by configuration-level .xcconfig file (build-release/build-debug).
//
HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/usr/local/lib/include" "$(OBJROOT)/UninstalledProducts/include" "$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include" "$(BUILT_PRODUCTS_DIR)"
OTHER_LDFLAGS = -ObjC
// Type of signing identity used for codesigning, resolves to first match of given type.
// "iPhone Developer": Development builds (default, local only; iOS Development certificate) or "iPhone Distribution": Distribution builds (Adhoc/In-House/AppStore; iOS Distribution certificate)
CODE_SIGN_IDENTITY = iPhone Developer
CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
// (CB-9721) Set ENABLE_BITCODE to NO in build.xcconfig
ENABLE_BITCODE = NO
// (CB-9719) Set CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to YES in build.xcconfig
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES
// (CB-10072)
SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/$(PROJECT_NAME)/Bridging-Header.h
// (CB-11854)
CODE_SIGN_ENTITLEMENTS = $(PROJECT_DIR)/$(PROJECT_NAME)/Entitlements-$(CONFIGURATION).plist
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WK_WEB_VIEW_ONLY=$(WK_WEB_VIEW_ONLY)
DEBUG_INFORMATION_FORMAT = dwarf
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var check_reqs = require('./lib/check_reqs');
// check for help flag
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) > -1) {
console.log('Usage: check_reqs or node check_reqs');
} else {
check_reqs.run().catch(err => {
console.error('Failed to check requirements due to ' + err);
process.exit(2);
});
}
+25
View File
@@ -0,0 +1,25 @@
:: Licensed to the Apache Software Foundation (ASF) under one
:: or more contributor license agreements. See the NOTICE file
:: distributed with this work for additional information
:: regarding copyright ownership. The ASF licenses this file
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
:: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations
:: under the License
@ECHO OFF
SET script_path="%~dp0check_reqs"
IF EXIST %script_path% (
node "%script_path%" %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2
EXIT /B 1
)
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var Api = require('./Api');
var nopt = require('nopt');
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
console.log('Cleans the project directory.');
process.exit(0);
}
// Do some basic argument parsing
var opts = nopt({
verbose: Boolean,
silent: Boolean
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi clean method spec
opts.argv = opts.argv.original;
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
require('./loggingHelper').adjustLoggerLevel(opts);
new Api().clean(opts).then(
() => {
console.log('** CLEAN SUCCEEDED **');
},
err => {
console.error(err);
process.exit(2);
}
);
+19
View File
@@ -0,0 +1,19 @@
:: Licensed to the Apache Software Foundation (ASF) under one
:: or more contributor license agreements. See the NOTICE file
:: distributed with this work for additional information
:: regarding copyright ownership. The ASF licenses this file
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
:: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations
:: under the License
@ECHO OFF
ECHO WARN: The 'clean' command is not available for cordova-ios on windows machines.>&2
+65
View File
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<widget xmlns = "http://www.w3.org/ns/widgets"
id = "io.cordova.helloCordova"
version = "2.0.0">
<!-- Preferences for iOS -->
<preference name="AllowInlineMediaPlayback" value="false" />
<preference name="BackupWebStorage" value="cloud" />
<preference name="DisallowOverscroll" value="false" />
<preference name="EnableViewportScale" value="false" />
<preference name="KeyboardDisplayRequiresUserAction" value="true" />
<preference name="MediaTypesRequiringUserActionForPlayback" value="none" />
<preference name="SuppressesIncrementalRendering" value="false" />
<preference name="SuppressesLongPressGesture" value="false" />
<preference name="Suppresses3DTouchGesture" value="false" />
<preference name="GapBetweenPages" value="0" />
<preference name="PageLength" value="0" />
<preference name="PaginationBreakingMode" value="page" /> <!-- page, column -->
<preference name="PaginationMode" value="unpaginated" /> <!-- unpaginated, leftToRight, topToBottom, bottomToTop, rightToLeft -->
<feature name="CDVWebViewEngine">
<param name="ios-package" value="CDVWebViewEngine" />
</feature>
<feature name="LaunchScreen">
<param name="ios-package" value="CDVLaunchScreen"/>
</feature>
<feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage"/>
</feature>
<feature name="Console">
<param name="ios-package" value="CDVLogger"/>
<param name="onload" value="true"/>
</feature>
<feature name="HandleOpenUrl">
<param name="ios-package" value="CDVHandleOpenURL"/>
<param name="onload" value="true"/>
</feature>
<feature name="IntentAndNavigationFilter">
<param name="ios-package" value="CDVIntentAndNavigationFilter"/>
<param name="onload" value="true"/>
</feature>
<feature name="GestureHandler">
<param name="ios-package" value="CDVGestureHandler"/>
<param name="onload" value="true"/>
</feature>
</widget>
+123
View File
@@ -0,0 +1,123 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
'use strict';
const fs = require('fs-extra');
const CordovaError = require('cordova-common').CordovaError;
function BridgingHeader (bridgingHeaderPath) {
this.path = bridgingHeaderPath;
this.bridgingHeaders = null;
if (!fs.existsSync(this.path)) {
throw new CordovaError('BridgingHeader.h is not found.');
}
this.bridgingHeaders = this.__parseForBridgingHeader(fs.readFileSync(this.path, 'utf8'));
}
BridgingHeader.prototype.addHeader = function (plugin_id, header_path) {
this.bridgingHeaders.push({ type: 'code', code: `#import "${header_path}"\n` });
};
BridgingHeader.prototype.removeHeader = function (plugin_id, header_path) {
this.bridgingHeaders = this.bridgingHeaders.filter(function (line) {
if (this.found) {
return true;
}
if (line.type === 'code') {
const re = new RegExp(`#import\\s+"${preg_quote(header_path)}"(\\s*|\\s.+)(\\n|$)`);
if (re.test(line.code)) {
this.found = true;
return false;
}
}
return true;
}, { found: false });
};
BridgingHeader.prototype.write = function () {
const text = this.__stringifyForBridgingHeader(this.bridgingHeaders);
fs.writeFileSync(this.path, text, 'utf8');
};
BridgingHeader.prototype.__stringifyForBridgingHeader = function (bridgingHeaders) {
return bridgingHeaders.map(obj => obj.code).join('');
};
BridgingHeader.prototype.__parseForBridgingHeader = function (text) {
let i = 0;
const list = [];
let type = 'code';
let start = 0;
while (i < text.length) {
switch (type) {
case 'comment':
if (i + 1 < text.length && text[i] === '*' && text[i + 1] === '/') {
i += 2;
list.push({ type, code: text.slice(start, i) });
type = 'code';
start = i;
} else {
i += 1;
}
break;
case 'line-comment':
if (i < text.length && text[i] === '\n') {
i += 1;
list.push({ type, code: text.slice(start, i) });
type = 'code';
start = i;
} else {
i += 1;
}
break;
case 'code':
default:
if (i + 1 < text.length && text[i] === '/' && text[i + 1] === '*') { // comment
if (start < i) {
list.push({ type, code: text.slice(start, i) });
}
type = 'comment';
start = i;
} else if (i + 1 < text.length && text[i] === '/' && text[i + 1] === '/') { // line comment
if (start < i) {
list.push({ type, code: text.slice(start, i) });
}
type = 'line-comment';
start = i;
} else if (i < text.length && text[i] === '\n') {
i += 1;
list.push({ type, code: text.slice(start, i) });
start = i;
} else {
i += 1;
}
break;
}
}
if (start < i) {
list.push({ type, code: text.slice(start, i) });
}
return list;
};
function preg_quote (str, delimiter) {
return (`${str}`).replace(new RegExp(`[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\${delimiter || ''}-]`, 'g'), '\\$&');
}
module.exports.BridgingHeader = BridgingHeader;
+418
View File
@@ -0,0 +1,418 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
'use strict';
const fs = require('fs-extra');
const path = require('path');
const util = require('util');
const {
CordovaError,
events,
superspawn: { spawn }
} = require('cordova-common');
Podfile.FILENAME = 'Podfile';
Podfile.declarationRegexpMap = {
'use_frameworks!': 'use[-_]frameworks!?',
'inhibit_all_warnings!': 'inhibit[-_]all[-_]warnings!?'
};
function Podfile (podFilePath, projectName, minDeploymentTarget) {
this.declarationToken = '##INSERT_DECLARATION##';
this.sourceToken = '##INSERT_SOURCE##';
this.podToken = '##INSERT_POD##';
this.path = podFilePath;
this.projectName = projectName;
this.minDeploymentTarget = minDeploymentTarget || '11.0';
this.contents = null;
this.sources = null;
this.declarations = null;
this.pods = null;
this.__dirty = false;
// check whether it is named Podfile
const filename = this.path.split(path.sep).pop();
if (filename !== Podfile.FILENAME) {
throw new CordovaError(util.format('Podfile: The file at %s is not `%s`.', this.path, Podfile.FILENAME));
}
if (!projectName) {
throw new CordovaError('Podfile: The projectName was not specified in the constructor.');
}
if (!fs.existsSync(this.path)) {
events.emit('verbose', util.format('Podfile: The file at %s does not exist.', this.path));
events.emit('verbose', 'Creating new Podfile in platforms/ios');
this.clear();
this.write();
} else {
events.emit('verbose', 'Podfile found in platforms/ios');
// parse for pods
const fileText = fs.readFileSync(this.path, 'utf8');
this.declarations = this.__parseForDeclarations(fileText);
this.sources = this.__parseForSources(fileText);
this.pods = this.__parseForPods(fileText);
}
}
Podfile.prototype.__parseForDeclarations = function (text) {
// split by \n
const arr = text.split('\n');
// getting lines between "platform :ios, '11.0'"" and "target 'HelloCordova'" do
const declarationsPreRE = new RegExp('platform :ios,\\s+\'[^\']+\'');
const declarationsPostRE = new RegExp('target\\s+\'[^\']+\'\\s+do');
const declarationRE = new RegExp('^\\s*[^#]');
return arr.reduce((acc, line) => {
switch (acc.state) {
case 0:
if (declarationsPreRE.exec(line)) {
acc.state = 1; // Start to read
}
break;
case 1:
if (declarationsPostRE.exec(line)) {
acc.state = 2; // Finish to read
} else {
acc.lines.push(line);
}
break;
case 2:
default:
// do nothing;
}
return acc;
}, { state: 0, lines: [] })
.lines
.filter(line => declarationRE.exec(line))
.reduce((obj, line) => {
obj[line] = line;
return obj;
}, {});
};
Podfile.prototype.__parseForSources = function (text) {
// split by \n
const arr = text.split('\n');
const sourceRE = new RegExp('source \'(.*)\'');
return arr.filter(line => {
const m = sourceRE.exec(line);
return (m !== null);
})
.reduce((obj, line) => {
const m = sourceRE.exec(line);
if (m !== null) {
const source = m[1];
obj[source] = source;
}
return obj;
}, {});
};
Podfile.prototype.__parseForPods = function (text) {
// split by \n
const arr = text.split('\n');
// aim is to match (space insignificant around the comma, comma optional):
// pod 'Foobar', '1.2'
// pod 'Foobar', 'abc 123 1.2'
// pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
// var podRE = new RegExp('pod \'([^\']*)\'\\s*,?\\s*(.*)');
const podRE = new RegExp('pod \'([^\']*)\'\\s*(?:,\\s*\'([^\']*)\'\\s*)?,?\\s*(.*)');
// only grab lines that don't have the pod spec'
return arr.filter(line => {
const m = podRE.exec(line);
return (m !== null);
})
.reduce((obj, line) => {
const m = podRE.exec(line);
if (m !== null) {
const podspec = {
name: m[1]
};
if (m[2]) {
podspec.spec = m[2];
}
if (m[3]) {
podspec.options = m[3];
}
obj[m[1]] = podspec; // i.e pod 'Foo', '1.2' ==> { 'Foo' : '1.2'}
}
return obj;
}, {});
};
Podfile.prototype.escapeSingleQuotes = function (string) {
return string.replace(/'/g, '\\\'');
};
Podfile.prototype.getTemplate = function () {
// Escaping possible ' in the project name
const projectName = this.escapeSingleQuotes(this.projectName);
return util.format(
'# DO NOT MODIFY -- auto-generated by Apache Cordova\n' +
'%s\n' +
'platform :ios, \'%s\'\n' +
'%s\n' +
'target \'%s\' do\n' +
'\tproject \'%s.xcodeproj\'\n' +
'%s\n' +
'end\n',
this.sourceToken, this.minDeploymentTarget, this.declarationToken, projectName, projectName, this.podToken);
};
Podfile.prototype.addSpec = function (name, spec) {
name = name || '';
// optional
spec = spec; /* eslint no-self-assign : 0 */
if (!name.length) { // blank names are not allowed
throw new CordovaError('Podfile addSpec: name is not specified.');
}
if (typeof spec === 'string') {
if (spec.startsWith(':')) {
spec = { name, options: spec };
} else {
spec = { name, spec };
}
}
this.pods[name] = spec;
this.__dirty = true;
events.emit('verbose', util.format('Added pod line for `%s`', name));
};
Podfile.prototype.removeSpec = function (name) {
if (this.existsSpec(name)) {
delete this.pods[name];
this.__dirty = true;
}
events.emit('verbose', util.format('Removed pod line for `%s`', name));
};
Podfile.prototype.getSpec = function (name) {
return this.pods[name];
};
Podfile.prototype.existsSpec = function (name) {
return (name in this.pods);
};
Podfile.prototype.addSource = function (src) {
this.sources[src] = src;
this.__dirty = true;
events.emit('verbose', util.format('Added source line for `%s`', src));
};
Podfile.prototype.removeSource = function (src) {
if (this.existsSource(src)) {
delete this.sources[src];
this.__dirty = true;
}
events.emit('verbose', util.format('Removed source line for `%s`', src));
};
Podfile.prototype.existsSource = function (src) {
return (src in this.sources);
};
Podfile.prototype.addDeclaration = function (declaration) {
this.declarations[declaration] = declaration;
this.__dirty = true;
events.emit('verbose', util.format('Added declaration line for `%s`', declaration));
};
Podfile.prototype.removeDeclaration = function (declaration) {
if (this.existsDeclaration(declaration)) {
delete this.declarations[declaration];
this.__dirty = true;
}
events.emit('verbose', util.format('Removed source line for `%s`', declaration));
};
Podfile.proofDeclaration = declaration => {
const list = Object.keys(Podfile.declarationRegexpMap).filter(key => {
const regexp = new RegExp(Podfile.declarationRegexpMap[key]);
return regexp.test(declaration);
});
if (list.length > 0) {
return list[0];
}
return declaration;
};
Podfile.prototype.existsDeclaration = function (declaration) {
return (declaration in this.declarations);
};
Podfile.prototype.clear = function () {
this.sources = {};
this.declarations = {};
this.pods = {};
this.__dirty = true;
};
Podfile.prototype.destroy = function () {
fs.unlinkSync(this.path);
events.emit('verbose', util.format('Deleted `%s`', this.path));
};
Podfile.prototype.write = function () {
let text = this.getTemplate();
const podsString =
Object.keys(this.pods).map(key => {
const name = key;
const json = this.pods[key];
if (typeof json === 'string') { // compatibility for using framework tag.
const spec = json;
if (spec.length) {
if (spec.indexOf(':') === 0) {
// don't quote it, it's a specification (starts with ':')
return util.format('\tpod \'%s\', %s', name, spec);
} else {
// quote it, it's a version
return util.format('\tpod \'%s\', \'%s\'', name, spec);
}
} else {
return util.format('\tpod \'%s\'', name);
}
} else {
const list = [`'${name}'`];
if ('spec' in json && json.spec.length) {
list.push(`'${json.spec}'`);
}
let options = ['tag', 'branch', 'commit', 'git', 'podspec']
.filter(tag => tag in json)
.map(tag => `:${tag} => '${json[tag]}'`);
if ('configurations' in json) {
options.push(`:configurations => [${json.configurations.split(',').map(conf => `'${conf.trim()}'`).join(',')}]`);
}
if ('options' in json) {
options = [json.options];
}
if (options.length > 0) {
list.push(options.join(', '));
}
return util.format('\tpod %s', list.join(', '));
}
}).join('\n');
const sourcesString =
Object.keys(this.sources).map(key => {
const source = this.sources[key];
return util.format('source \'%s\'', source);
}).join('\n');
const declarationString =
Object.keys(this.declarations).map(key => {
const declaration = this.declarations[key];
return declaration;
}).join('\n');
text = text.replace(this.podToken, podsString)
.replace(this.sourceToken, sourcesString)
.replace(this.declarationToken, declarationString);
fs.writeFileSync(this.path, text, 'utf8');
this.__dirty = false;
events.emit('verbose', 'Wrote to Podfile.');
};
Podfile.prototype.isDirty = function () {
return this.__dirty;
};
Podfile.prototype.before_install = function (toolOptions) {
toolOptions = toolOptions || {};
// Template tokens in order: project name, project name, debug | release
const template =
'// DO NOT MODIFY -- auto-generated by Apache Cordova\n' +
'#include "Pods/Target Support Files/Pods-%s/Pods-%s.%s.xcconfig"';
const debugContents = util.format(template, this.projectName, this.projectName, 'debug');
const releaseContents = util.format(template, this.projectName, this.projectName, 'release');
const debugConfigPath = path.join(this.path, '..', 'pods-debug.xcconfig');
const releaseConfigPath = path.join(this.path, '..', 'pods-release.xcconfig');
fs.writeFileSync(debugConfigPath, debugContents, 'utf8');
fs.writeFileSync(releaseConfigPath, releaseContents, 'utf8');
return Promise.resolve(toolOptions);
};
Podfile.prototype.install = function (requirementsCheckerFunction) {
const opts = {};
opts.cwd = path.join(this.path, '..'); // parent path of this Podfile
opts.stdio = 'pipe';
opts.printCommand = true;
let first = true;
if (!requirementsCheckerFunction) {
requirementsCheckerFunction = Promise.resolve();
}
return requirementsCheckerFunction()
.then(toolOptions => this.before_install(toolOptions))
.then(toolOptions => {
if (toolOptions.ignore) {
events.emit('verbose', '==== pod install start ====\n');
events.emit('verbose', toolOptions.ignoreMessage);
return Promise.resolve();
} else {
return spawn('pod', ['install', '--verbose'], opts)
.progress(stdio => {
if (stdio.stderr) { console.error(stdio.stderr); }
if (stdio.stdout) {
if (first) {
events.emit('verbose', '==== pod install start ====\n');
first = false;
}
events.emit('verbose', stdio.stdout);
}
});
}
})
.then(() => { // done
events.emit('verbose', '==== pod install end ====\n');
});
};
module.exports.Podfile = Podfile;
+209
View File
@@ -0,0 +1,209 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const fs = require('fs-extra');
const path = require('path');
const util = require('util');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
PodsJson.FILENAME = 'pods.json';
PodsJson.LIBRARY = 'libraries';
PodsJson.SOURCE = 'sources';
PodsJson.DECLARATION = 'declarations';
function PodsJson (podsJsonPath) {
this.path = podsJsonPath;
this.contents = null;
this.__dirty = false;
const filename = this.path.split(path.sep).pop();
if (filename !== PodsJson.FILENAME) {
throw new CordovaError(util.format('PodsJson: The file at %s is not `%s`.', this.path, PodsJson.FILENAME));
}
if (!fs.existsSync(this.path)) {
events.emit('verbose', util.format('pods.json: The file at %s does not exist.', this.path));
events.emit('verbose', 'Creating new pods.json in platforms/ios');
this.clear();
this.write();
} else {
events.emit('verbose', 'pods.json found in platforms/ios');
// load contents
const contents = fs.readFileSync(this.path, 'utf8');
this.contents = JSON.parse(contents);
}
this.__updateFormatIfNecessary();
}
PodsJson.prototype.__isOldFormat = function () {
if (this.contents !== null) {
if (this.contents.declarations === undefined ||
this.contents.sources === undefined ||
this.contents.libraries === undefined) {
return true;
}
}
return false;
};
PodsJson.prototype.__updateFormatIfNecessary = function () {
if (this.__isOldFormat()) {
this.contents = {
declarations: {},
sources: {},
libraries: this.contents
};
this.__dirty = true;
events.emit('verbose', 'Update format of pods.json');
}
};
PodsJson.prototype.getLibraries = function () {
return this.contents[PodsJson.LIBRARY];
};
PodsJson.prototype.__get = function (kind, name) {
return this.contents[kind][name];
};
PodsJson.prototype.getLibrary = function (name) {
return this.__get(PodsJson.LIBRARY, name);
};
PodsJson.prototype.getSource = function (name) {
return this.__get(PodsJson.SOURCE, name);
};
PodsJson.prototype.getDeclaration = function (name) {
return this.__get(PodsJson.DECLARATION, name);
};
PodsJson.prototype.__remove = function (kind, name) {
if (this.contents[kind][name]) {
delete this.contents[kind][name];
this.__dirty = true;
events.emit('verbose', util.format('Remove from pods.json for `%s` - `%s`', name));
}
};
PodsJson.prototype.removeLibrary = function (name) {
this.__remove(PodsJson.LIBRARY, name);
};
PodsJson.prototype.removeSource = function (name) {
this.__remove(PodsJson.SOURCE, name);
};
PodsJson.prototype.removeDeclaration = function (name) {
this.__remove(PodsJson.DECLARATION, name);
};
PodsJson.prototype.clear = function () {
this.contents = {
declarations: {},
sources: {},
libraries: {}
};
this.__dirty = true;
};
PodsJson.prototype.destroy = function () {
fs.unlinkSync(this.path);
events.emit('verbose', util.format('Deleted `%s`', this.path));
};
PodsJson.prototype.write = function () {
if (this.contents) {
fs.writeFileSync(this.path, JSON.stringify(this.contents, null, 4));
this.__dirty = false;
events.emit('verbose', 'Wrote to pods.json.');
}
};
PodsJson.prototype.__increment = function (kind, name) {
const val = this.__get(kind, name);
if (val) {
val.count++;
}
};
PodsJson.prototype.incrementLibrary = function (name) {
this.__increment(PodsJson.LIBRARY, name);
};
PodsJson.prototype.incrementSource = function (name) {
this.__increment(PodsJson.SOURCE, name);
};
PodsJson.prototype.incrementDeclaration = function (name) {
this.__increment(PodsJson.DECLARATION, name);
};
PodsJson.prototype.__decrement = function (kind, name) {
const val = this.__get(kind, name);
if (val) {
val.count--;
if (val.count <= 0) {
this.__remove(kind, name);
}
}
};
PodsJson.prototype.decrementLibrary = function (name) {
this.__decrement(PodsJson.LIBRARY, name);
};
PodsJson.prototype.decrementSource = function (name) {
this.__decrement(PodsJson.SOURCE, name);
};
PodsJson.prototype.decrementDeclaration = function (name) {
this.__decrement(PodsJson.DECLARATION, name);
};
PodsJson.prototype.__setJson = function (kind, name, json) {
this.contents[kind][name] = Object.assign({}, json);
this.__dirty = true;
events.emit('verbose', util.format('Set pods.json for `%s` - `%s`', kind, name));
};
// sample json for library
// { name: "Eureka", spec: "4.2.0", "swift-version": "4.1", count: 1 }
PodsJson.prototype.setJsonLibrary = function (name, json) {
this.__setJson(PodsJson.LIBRARY, name, json);
};
// sample json for source
// { source: "https://github.com/brightcove/BrightcoveSpecs.git", count: 1 }
PodsJson.prototype.setJsonSource = function (name, json) {
this.__setJson(PodsJson.SOURCE, name, json);
};
// sample json for declaration
// { declaration: ""}
PodsJson.prototype.setJsonDeclaration = function (name, json) {
this.__setJson(PodsJson.DECLARATION, name, json);
};
PodsJson.prototype.isDirty = function () {
return this.__dirty;
};
module.exports.PodsJson = PodsJson;
+442
View File
@@ -0,0 +1,442 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const path = require('path');
const which = require('which');
const {
CordovaError,
events,
superspawn: { spawn }
} = require('cordova-common');
const fs = require('fs-extra');
const plist = require('plist');
const util = require('util');
const check_reqs = require('./check_reqs');
const projectFile = require('./projectFile');
// These are regular expressions to detect if the user is changing any of the built-in xcodebuildArgs
/* eslint-disable no-useless-escape */
const buildFlagMatchers = {
workspace: /^\-workspace\s*(.*)/,
scheme: /^\-scheme\s*(.*)/,
configuration: /^\-configuration\s*(.*)/,
sdk: /^\-sdk\s*(.*)/,
destination: /^\-destination\s*(.*)/,
archivePath: /^\-archivePath\s*(.*)/,
configuration_build_dir: /^(CONFIGURATION_BUILD_DIR=.*)/,
shared_precomps_dir: /^(SHARED_PRECOMPS_DIR=.*)/
};
/* eslint-enable no-useless-escape */
/**
* Creates a project object (see projectFile.js/parseProjectFile) from
* a project path and name
*
* @param {*} projectPath
* @param {*} projectName
*/
function createProjectObject (projectPath, projectName) {
const locations = {
root: projectPath,
pbxproj: path.join(projectPath, `${projectName}.xcodeproj`, 'project.pbxproj')
};
return projectFile.parse(locations);
}
/**
* Returns a promise that resolves to the default simulator target; the logic here
* matches what `cordova emulate ios` does.
*
* The return object has two properties: `name` (the Xcode destination name),
* `identifier` (the simctl identifier), and `simIdentifier` (essentially the cordova emulate target)
*
* @return {Promise}
*/
function getDefaultSimulatorTarget () {
events.emit('log', 'Select last emulator from list as default.');
return require('./listEmulatorBuildTargets').run()
.then(emulators => {
let targetEmulator;
if (emulators.length > 0) {
targetEmulator = emulators[0];
}
emulators.forEach(emulator => {
if (emulator.name.indexOf('iPhone') === 0) {
targetEmulator = emulator;
}
});
return targetEmulator;
});
}
module.exports.run = buildOpts => {
let emulatorTarget = '';
const projectPath = path.join(__dirname, '..', '..');
let projectName = '';
buildOpts = buildOpts || {};
if (buildOpts.debug && buildOpts.release) {
return Promise.reject(new CordovaError('Cannot specify "debug" and "release" options together.'));
}
if (buildOpts.device && buildOpts.emulator) {
return Promise.reject(new CordovaError('Cannot specify "device" and "emulator" options together.'));
}
if (buildOpts.buildConfig) {
if (!fs.existsSync(buildOpts.buildConfig)) {
return Promise.reject(new CordovaError(`Build config file does not exist: ${buildOpts.buildConfig}`));
}
events.emit('log', `Reading build config file: ${path.resolve(buildOpts.buildConfig)}`);
const contents = fs.readFileSync(buildOpts.buildConfig, 'utf-8');
const buildConfig = JSON.parse(contents.replace(/^\ufeff/, '')); // Remove BOM
if (buildConfig.ios) {
const buildType = buildOpts.release ? 'release' : 'debug';
const config = buildConfig.ios[buildType];
if (config) {
['codeSignIdentity', 'codeSignResourceRules', 'provisioningProfile', 'developmentTeam', 'packageType', 'buildFlag', 'iCloudContainerEnvironment', 'automaticProvisioning'].forEach(
key => {
buildOpts[key] = buildOpts[key] || config[key];
});
}
}
}
return require('./listDevices').run()
.then(devices => {
if (devices.length > 0 && !(buildOpts.emulator)) {
// we also explicitly set device flag in options as we pass
// those parameters to other api (build as an example)
buildOpts.device = true;
return check_reqs.check_ios_deploy();
}
}).then(() => {
// CB-12287: Determine the device we should target when building for a simulator
if (!buildOpts.device) {
let newTarget = buildOpts.target || '';
if (newTarget) {
// only grab the device name, not the runtime specifier
newTarget = newTarget.split(',')[0];
}
// a target was given to us, find the matching Xcode destination name
const promise = require('./listEmulatorBuildTargets').targetForSimIdentifier(newTarget);
return promise.then(theTarget => {
if (!theTarget) {
return getDefaultSimulatorTarget().then(defaultTarget => {
emulatorTarget = defaultTarget.name;
events.emit('warn', `No simulator found for "${newTarget}. Falling back to the default target.`);
events.emit('log', `Building for "${emulatorTarget}" Simulator (${defaultTarget.identifier}, ${defaultTarget.simIdentifier}).`);
return emulatorTarget;
});
} else {
emulatorTarget = theTarget.name;
events.emit('log', `Building for "${emulatorTarget}" Simulator (${theTarget.identifier}, ${theTarget.simIdentifier}).`);
return emulatorTarget;
}
});
}
})
.then(() => check_reqs.run())
.then(() => findXCodeProjectIn(projectPath))
.then(name => {
projectName = name;
let extraConfig = '';
if (buildOpts.codeSignIdentity) {
extraConfig += `CODE_SIGN_IDENTITY = ${buildOpts.codeSignIdentity}\n`;
extraConfig += `CODE_SIGN_IDENTITY[sdk=iphoneos*] = ${buildOpts.codeSignIdentity}\n`;
}
if (buildOpts.codeSignResourceRules) {
extraConfig += `CODE_SIGN_RESOURCE_RULES_PATH = ${buildOpts.codeSignResourceRules}\n`;
}
if (buildOpts.provisioningProfile) {
extraConfig += `PROVISIONING_PROFILE = ${buildOpts.provisioningProfile}\n`;
}
if (buildOpts.developmentTeam) {
extraConfig += `DEVELOPMENT_TEAM = ${buildOpts.developmentTeam}\n`;
}
function writeCodeSignStyle (value) {
const project = createProjectObject(projectPath, projectName);
events.emit('verbose', `Set CODE_SIGN_STYLE Build Property to ${value}.`);
project.xcode.updateBuildProperty('CODE_SIGN_STYLE', value);
events.emit('verbose', `Set ProvisioningStyle Target Attribute to ${value}.`);
project.xcode.addTargetAttribute('ProvisioningStyle', value);
project.write();
}
if (buildOpts.provisioningProfile) {
events.emit('verbose', 'ProvisioningProfile build option set, changing project settings to Manual.');
writeCodeSignStyle('Manual');
} else if (buildOpts.automaticProvisioning) {
events.emit('verbose', 'ProvisioningProfile build option NOT set, changing project settings to Automatic.');
writeCodeSignStyle('Automatic');
}
return fs.writeFile(path.join(__dirname, '..', 'build-extras.xcconfig'), extraConfig, 'utf-8');
}).then(() => {
const configuration = buildOpts.release ? 'Release' : 'Debug';
events.emit('log', `Building project: ${path.join(projectPath, `${projectName}.xcworkspace`)}`);
events.emit('log', `\tConfiguration: ${configuration}`);
events.emit('log', `\tPlatform: ${buildOpts.device ? 'device' : 'emulator'}`);
events.emit('log', `\tTarget: ${emulatorTarget}`);
const buildOutputDir = path.join(projectPath, 'build', (buildOpts.device ? 'device' : 'emulator'));
// remove the build/device folder before building
fs.removeSync(buildOutputDir);
const xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, buildOpts.device, buildOpts.buildFlag, emulatorTarget, buildOpts.automaticProvisioning);
return spawn('xcodebuild', xcodebuildArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' });
}).then(() => {
if (!buildOpts.device || buildOpts.noSign) {
return;
}
const project = createProjectObject(projectPath, projectName);
const bundleIdentifier = project.getPackageName();
const exportOptions = { compileBitcode: false, method: 'development' };
if (buildOpts.packageType) {
exportOptions.method = buildOpts.packageType;
}
if (buildOpts.iCloudContainerEnvironment) {
exportOptions.iCloudContainerEnvironment = buildOpts.iCloudContainerEnvironment;
}
if (buildOpts.developmentTeam) {
exportOptions.teamID = buildOpts.developmentTeam;
}
if (buildOpts.provisioningProfile && bundleIdentifier) {
exportOptions.provisioningProfiles = { [bundleIdentifier]: String(buildOpts.provisioningProfile) };
exportOptions.signingStyle = 'manual';
}
if (buildOpts.codeSignIdentity) {
exportOptions.signingCertificate = buildOpts.codeSignIdentity;
}
const exportOptionsPlist = plist.build(exportOptions);
const exportOptionsPath = path.join(projectPath, 'exportOptions.plist');
const buildOutputDir = path.join(projectPath, 'build', 'device');
function checkSystemRuby () {
const ruby_cmd = which.sync('ruby', { nothrow: true });
if (ruby_cmd !== '/usr/bin/ruby') {
events.emit('warn', 'Non-system Ruby in use. This may cause packaging to fail.\n' +
'If you use RVM, please run `rvm use system`.\n' +
'If you use chruby, please run `chruby system`.');
}
}
function packageArchive () {
const xcodearchiveArgs = getXcodeArchiveArgs(projectName, projectPath, buildOutputDir, exportOptionsPath, buildOpts.automaticProvisioning);
return spawn('xcodebuild', xcodearchiveArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' });
}
return fs.writeFile(exportOptionsPath, exportOptionsPlist, 'utf-8')
.then(checkSystemRuby)
.then(packageArchive);
});
};
/**
* Searches for first XCode project in specified folder
* @param {String} projectPath Path where to search project
* @return {Promise} Promise either fulfilled with project name or rejected
*/
function findXCodeProjectIn (projectPath) {
// 'Searching for Xcode project in ' + projectPath);
const xcodeProjFiles = fs.readdirSync(projectPath).filter(name => path.extname(name) === '.xcodeproj');
if (xcodeProjFiles.length === 0) {
return Promise.reject(new CordovaError(`No Xcode project found in ${projectPath}`));
}
if (xcodeProjFiles.length > 1) {
events.emit('warn', `Found multiple .xcodeproj directories in \n${projectPath}\nUsing first one`);
}
const projectName = path.basename(xcodeProjFiles[0], '.xcodeproj');
return Promise.resolve(projectName);
}
module.exports.findXCodeProjectIn = findXCodeProjectIn;
/**
* Returns array of arguments for xcodebuild
* @param {String} projectName Name of xcode project
* @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
* @param {String} configuration Configuration name: debug|release
* @param {Boolean} isDevice Flag that specify target for package (device/emulator)
* @param {Array} buildFlags
* @param {String} emulatorTarget Target for emulator (rather than default)
* @param {Boolean} autoProvisioning Whether to allow Xcode to automatically update provisioning
* @return {Array} Array of arguments that could be passed directly to spawn method
*/
function getXcodeBuildArgs (projectName, projectPath, configuration, isDevice, buildFlags, emulatorTarget, autoProvisioning) {
let options;
let buildActions;
let settings;
const customArgs = {};
customArgs.otherFlags = [];
if (buildFlags) {
if (typeof buildFlags === 'string' || buildFlags instanceof String) {
parseBuildFlag(buildFlags, customArgs);
} else { // buildFlags is an Array of strings
buildFlags.forEach(flag => {
parseBuildFlag(flag, customArgs);
});
}
}
if (isDevice) {
options = [
'-workspace', customArgs.workspace || `${projectName}.xcworkspace`,
'-scheme', customArgs.scheme || projectName,
'-configuration', customArgs.configuration || configuration,
'-destination', customArgs.destination || 'generic/platform=iOS',
'-archivePath', customArgs.archivePath || `${projectName}.xcarchive`
];
buildActions = ['archive'];
settings = [
customArgs.configuration_build_dir || `CONFIGURATION_BUILD_DIR=${path.join(projectPath, 'build', 'device')}`,
customArgs.shared_precomps_dir || `SHARED_PRECOMPS_DIR=${path.join(projectPath, 'build', 'sharedpch')}`
];
// Add other matched flags to otherFlags to let xcodebuild present an appropriate error.
// This is preferable to just ignoring the flags that the user has passed in.
if (customArgs.sdk) {
customArgs.otherFlags = customArgs.otherFlags.concat(['-sdk', customArgs.sdk]);
}
if (autoProvisioning) {
options = options.concat(['-allowProvisioningUpdates']);
}
} else { // emulator
options = [
'-workspace', customArgs.project || `${projectName}.xcworkspace`,
'-scheme', customArgs.scheme || projectName,
'-configuration', customArgs.configuration || configuration,
'-sdk', customArgs.sdk || 'iphonesimulator',
'-destination', customArgs.destination || `platform=iOS Simulator,name=${emulatorTarget}`
];
buildActions = ['build'];
settings = [
customArgs.configuration_build_dir || `CONFIGURATION_BUILD_DIR=${path.join(projectPath, 'build', 'emulator')}`,
customArgs.shared_precomps_dir || `SHARED_PRECOMPS_DIR=${path.join(projectPath, 'build', 'sharedpch')}`
];
// Add other matched flags to otherFlags to let xcodebuild present an appropriate error.
// This is preferable to just ignoring the flags that the user has passed in.
if (customArgs.archivePath) {
customArgs.otherFlags = customArgs.otherFlags.concat(['-archivePath', customArgs.archivePath]);
}
}
return options.concat(buildActions).concat(settings).concat(customArgs.otherFlags);
}
/**
* Returns array of arguments for xcodebuild
* @param {String} projectName Name of xcode project
* @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
* @param {String} outputPath Output directory to contain the IPA
* @param {String} exportOptionsPath Path to the exportOptions.plist file
* @param {Boolean} autoProvisioning Whether to allow Xcode to automatically update provisioning
* @return {Array} Array of arguments that could be passed directly to spawn method
*/
function getXcodeArchiveArgs (projectName, projectPath, outputPath, exportOptionsPath, autoProvisioning) {
return [
'-exportArchive',
'-archivePath', `${projectName}.xcarchive`,
'-exportOptionsPlist', exportOptionsPath,
'-exportPath', outputPath
].concat(autoProvisioning ? ['-allowProvisioningUpdates'] : []);
}
function parseBuildFlag (buildFlag, args) {
let matched;
for (const key in buildFlagMatchers) {
const found = buildFlag.match(buildFlagMatchers[key]);
if (found) {
matched = true;
// found[0] is the whole match, found[1] is the first match in parentheses.
args[key] = found[1];
events.emit('warn', util.format('Overriding xcodebuildArg: %s', buildFlag));
}
}
if (!matched) {
// If the flag starts with a '-' then it is an xcodebuild built-in option or a
// user-defined setting. The regex makes sure that we don't split a user-defined
// setting that is wrapped in quotes.
/* eslint-disable no-useless-escape */
if (buildFlag[0] === '-' && !buildFlag.match(/^.*=(\".*\")|(\'.*\')$/)) {
args.otherFlags = args.otherFlags.concat(buildFlag.split(' '));
events.emit('warn', util.format('Adding xcodebuildArg: %s', buildFlag.split(' ')));
} else {
args.otherFlags.push(buildFlag);
events.emit('warn', util.format('Adding xcodebuildArg: %s', buildFlag));
}
}
}
// help/usage function
module.exports.help = function help () {
console.log('');
console.log('Usage: build [--debug | --release] [--archs=\"<list of architectures...>\"]');
console.log(' [--device | --simulator] [--codeSignIdentity=\"<identity>\"]');
console.log(' [--codeSignResourceRules=\"<resourcerules path>\"]');
console.log(' [--developmentTeam=\"<Team ID>\"]');
console.log(' [--provisioningProfile=\"<provisioning profile>\"]');
console.log(' --help : Displays this dialog.');
console.log(' --debug : Builds project in debug mode. (Default)');
console.log(' --release : Builds project in release mode.');
console.log(' -r : Shortcut :: builds project in release mode.');
/* eslint-enable no-useless-escape */
// TODO: add support for building different archs
// console.log(" --archs : Builds project binaries for specific chip architectures (`anycpu`, `arm`, `x86`, `x64`).");
console.log(' --device, --simulator');
console.log(' : Specifies, what type of project to build');
console.log(' --codeSignIdentity : Type of signing identity used for code signing.');
console.log(' --codeSignResourceRules : Path to ResourceRules.plist.');
console.log(' --developmentTeam : New for Xcode 8. The development team (Team ID)');
console.log(' to use for code signing.');
console.log(' --provisioningProfile : UUID of the profile.');
console.log(' --device --noSign : Builds project without application signing.');
console.log('');
console.log('examples:');
console.log(' build ');
console.log(' build --debug');
console.log(' build --release');
console.log(' build --codeSignIdentity="iPhone Distribution" --provisioningProfile="926c2bd6-8de9-4c2f-8407-1016d2d12954"');
// TODO: add support for building different archs
// console.log(" build --release --archs=\"armv7\"");
console.log('');
process.exit(0);
};
+168
View File
@@ -0,0 +1,168 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
'use strict';
const which = require('which');
const versions = require('./versions');
const { CordovaError } = require('cordova-common');
const SUPPORTED_OS_PLATFORMS = ['darwin'];
const XCODEBUILD_MIN_VERSION = '9.0.0';
const XCODEBUILD_NOT_FOUND_MESSAGE =
`Please install version ${XCODEBUILD_MIN_VERSION} or greater from App Store`;
const IOS_DEPLOY_MIN_VERSION = '1.9.2';
const IOS_DEPLOY_NOT_FOUND_MESSAGE =
`Please download, build and install version ${IOS_DEPLOY_MIN_VERSION} or greater from https://github.com/ios-control/ios-deploy into your path, or do 'npm install -g ios-deploy'`;
const COCOAPODS_MIN_VERSION = '1.8.0';
const COCOAPODS_NOT_FOUND_MESSAGE = `Please install version ${COCOAPODS_MIN_VERSION} or greater from https://cocoapods.org/`;
/**
* Checks if xcode util is available
* @return {Promise} Returns a promise either resolved with xcode version or rejected
*/
module.exports.run = module.exports.check_xcodebuild = () => {
return checkTool('xcodebuild', XCODEBUILD_MIN_VERSION, XCODEBUILD_NOT_FOUND_MESSAGE);
};
/**
* Checks if ios-deploy util is available
* @return {Promise} Returns a promise either resolved with ios-deploy version or rejected
*/
module.exports.check_ios_deploy = () => {
return checkTool('ios-deploy', IOS_DEPLOY_MIN_VERSION, IOS_DEPLOY_NOT_FOUND_MESSAGE);
};
module.exports.check_os = () => {
// Build iOS apps available for OSX platform only, so we reject on others platforms
return os_platform_is_supported()
? Promise.resolve(process.platform)
: Promise.reject(new CordovaError('Cordova tooling for iOS requires Apple macOS'));
};
function os_platform_is_supported () {
return (SUPPORTED_OS_PLATFORMS.indexOf(process.platform) !== -1);
}
/**
* Checks if cocoapods is available.
* @return {Promise} Returns a promise either resolved or rejected
*/
module.exports.check_cocoapods = toolChecker => {
if (os_platform_is_supported()) {
return checkTool('pod', COCOAPODS_MIN_VERSION, COCOAPODS_NOT_FOUND_MESSAGE, 'CocoaPods');
}
return Promise.resolve({
ignore: true,
ignoreMessage: `CocoaPods check and installation ignored on ${process.platform}`
});
};
/**
* Checks if specific tool is available.
* @param {String} tool Tool name to check. Known tools are 'xcodebuild' and 'ios-deploy'
* @param {Number} minVersion Min allowed tool version.
* @param {String} message Message that will be used to reject promise.
* @param {String} toolFriendlyName Friendly name of the tool, to report to the user. Optional.
* @return {Promise} Returns a promise either resolved with tool version or rejected
*/
function checkTool (tool, minVersion, message, toolFriendlyName) {
toolFriendlyName = toolFriendlyName || tool;
// Check whether tool command is available at all
const tool_command = which.sync(tool, { nothrow: true });
if (!tool_command) {
return Promise.reject(new CordovaError(`${toolFriendlyName} was not found. ${message || ''}`));
}
// check if tool version is greater than specified one
return versions.get_tool_version(tool).then(version => {
version = version.trim();
return versions.compareVersions(version, minVersion) >= 0
? Promise.resolve({ version })
: Promise.reject(new CordovaError(`Cordova needs ${toolFriendlyName} version ${minVersion} or greater, you have version ${version}. ${message || ''}`));
});
}
/**
* Object that represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements.
* @param {String} name The name of requirements. Human-readable field.
* @param {Boolean} isFatal Marks the requirement as fatal. If such requirement will fail
* next requirements' checks will be skipped.
*/
const Requirement = function (id, name, isFatal) {
this.id = id;
this.name = name;
this.installed = false;
this.metadata = {};
this.isFatal = isFatal || false;
};
/**
* Methods that runs all checks one by one and returns a result of checks
* as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
*
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = () => {
const requirements = [
new Requirement('os', 'Apple macOS', true),
new Requirement('xcode', 'Xcode'),
new Requirement('ios-deploy', 'ios-deploy'),
new Requirement('CocoaPods', 'CocoaPods')
];
const result = [];
let fatalIsHit = false;
const checkFns = [
module.exports.check_os,
module.exports.check_xcodebuild,
module.exports.check_ios_deploy,
module.exports.check_cocoapods
];
// Then execute requirement checks one-by-one
return checkFns.reduce((promise, checkFn, idx) => {
return promise.then(() => {
// If fatal requirement is failed,
// we don't need to check others
if (fatalIsHit) return Promise.resolve();
const requirement = requirements[idx];
return checkFn()
.then(version => {
requirement.installed = true;
requirement.metadata.version = version;
result.push(requirement);
}, err => {
if (requirement.isFatal) fatalIsHit = true;
requirement.metadata.reason = err;
result.push(requirement);
});
});
}, Promise.resolve())
// When chain is completed, return requirements array to upstream API
.then(() => result);
};
+47
View File
@@ -0,0 +1,47 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const path = require('path');
const fs = require('fs-extra');
const {
CordovaError,
superspawn: { spawn }
} = require('cordova-common');
const projectPath = path.join(__dirname, '..', '..');
module.exports.run = () => {
const projectName = fs.readdirSync(projectPath).filter(name => path.extname(name) === '.xcodeproj');
if (!projectName) {
return Promise.reject(new CordovaError(`No Xcode project found in ${projectPath}`));
}
const xcodebuildClean = configName => {
return spawn(
'xcodebuild',
['-project', projectName, '-configuration', configName, '-alltargets', 'clean'],
{ cwd: projectPath, printCommand: true, stdio: 'inherit' }
);
};
return xcodebuildClean('Debug')
.then(() => xcodebuildClean('Release'))
.then(() => fs.removeSync(path.join(projectPath, 'build')));
};
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { run } = require('./listDevices');
run().then(devices => {
devices.forEach(device => {
console.log(device);
});
});
@@ -0,0 +1,26 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { run } = require('./listEmulatorBuildTargets');
run().then(targets => {
console.log(JSON.stringify(targets, null, 2));
});
@@ -0,0 +1,28 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { run } = require('./listEmulatorImages');
run().then(names => {
names.forEach(name => {
console.log(name);
});
});
@@ -0,0 +1,28 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { run } = require('./listStartedEmulators');
run().then(emulators => {
emulators.forEach(emulator => {
console.log(emulator);
});
});
+42
View File
@@ -0,0 +1,42 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { superspawn: { spawn } } = require('cordova-common');
const DEVICE_REGEX = /-o (iPhone|iPad|iPod)@.*?"USB Serial Number" = "([^"]*)"/gs;
/**
* Gets list of connected iOS devices
* @return {Promise} Promise fulfilled with list of available iOS devices
*/
function listDevices () {
return spawn('ioreg', ['-p', 'IOUSB', '-l'])
.then(output => {
return [...matchAll(output, DEVICE_REGEX)]
.map(m => m.slice(1).reverse().join(' '));
});
}
// TODO: Should be replaced with String#matchAll once available
function * matchAll (s, r) {
let match;
while ((match = r.exec(s))) yield match;
}
exports.run = listDevices;
+96
View File
@@ -0,0 +1,96 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { superspawn: { spawn }, events } = require('cordova-common');
/**
* Returns a list of available simulator build targets of the form
*
* [
* { name: <xcode-destination-name>,
* identifier: <simctl-identifier>,
* simIdentifier: <cordova emulate target>
* }
* ]
*
*/
function listEmulatorBuildTargets () {
events.emit('log', 'List simulator targets');
return spawn('xcrun', ['simctl', 'list', '--json'], { printCommand: true })
.then(output => JSON.parse(output))
.then(function (simInfo) {
var devices = simInfo.devices;
var deviceTypes = simInfo.devicetypes;
return deviceTypes.reduce(function (typeAcc, deviceType) {
if (!deviceType.name.match(/^[iPad|iPhone]/)) {
// ignore targets we don't support (like Apple Watch or Apple TV)
return typeAcc;
}
var availableDevices = Object.keys(devices).reduce(function (availAcc, deviceCategory) {
var availableDevicesInCategory = devices[deviceCategory];
availableDevicesInCategory.forEach(function (device) {
if (device.name === deviceType.name || device.name === deviceType.name.replace(/-inch/g, ' inch')) {
// Check new flag isAvailable (XCode 10.1+) or legacy string availability (XCode 10 and lower)
if (device.isAvailable || (device.availability && device.availability.toLowerCase().indexOf('unavailable') < 0)) {
availAcc.push(device);
}
}
});
return availAcc;
}, []);
// we only want device types that have at least one available device
// (regardless of OS); this filters things out like iPhone 4s, which
// is present in deviceTypes, but probably not available on the user's
// system.
if (availableDevices.length > 0) {
typeAcc.push(deviceType);
}
return typeAcc;
}, []);
})
.then(function (filteredTargets) {
// the simIdentifier, or cordova emulate target name, is the very last part
// of identifier.
return filteredTargets.map(function (target) {
var identifierPieces = target.identifier.split('.');
target.simIdentifier = identifierPieces[identifierPieces.length - 1];
return target;
});
});
}
exports.run = listEmulatorBuildTargets;
/**
* Given a simIdentifier, return the matching target.
*
* @param {string} simIdentifier a target, like "iPhone-SE"
* @return {Object} the matching target, or undefined if no match
*/
exports.targetForSimIdentifier = function (simIdentifier) {
return listEmulatorBuildTargets()
.then(function (targets) {
return targets.reduce(function (acc, target) {
if (!acc && target.simIdentifier.toLowerCase() === simIdentifier.toLowerCase()) {
acc = target;
}
return acc;
}, undefined);
});
};
+30
View File
@@ -0,0 +1,30 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var iossim = require('ios-sim');
/**
* Gets list of iOS devices available for simulation
* @return {Promise} Promise fulfilled with list of devices available for simulation
*/
function listEmulatorImages () {
return Promise.resolve(iossim.getdevicetypes());
}
exports.run = listEmulatorImages;
+50
View File
@@ -0,0 +1,50 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const { superspawn: { spawn } } = require('cordova-common');
/**
* Gets list of running iOS simulators
* @return {Promise} Promise fulfilled with list of running iOS simulators
*
* @todo In the next PR, I will refactor this entire method.
*
* The process no longer contains the pattern "[i]OS Simulator".
* The process is now called "Simulator.app"
*
* Additionaly, `defaults read com.apple.iphonesimulator "SimulateDevice"` is also not valid aymore.
*
* I will replace this entire method to locate the active simulators though `simctl`
*
* Alternativly, remove this file. It is not documented in Cordova and not used anywhere in our code base.
*/
function listStartedEmulators () {
// wrap exec call into promise
return spawn('ps', ['aux'])
.then(output => {
if (output.match(/[i]OS Simulator/)) {
return spawn('defaults', ['read', 'com.apple.iphonesimulator', '"SimulateDevice"']);
}
return '';
})
.then(output => output.split('\n'));
}
exports.run = listStartedEmulators;
+387
View File
@@ -0,0 +1,387 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
'use strict';
const fs = require('fs-extra');
const path = require('path');
const util = require('util');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
// These frameworks are required by cordova-ios by default. We should never add/remove them.
const keep_these_frameworks = [
'MobileCoreServices.framework',
'CoreGraphics.framework',
'AssetsLibrary.framework'
];
const handlers = {
'source-file': {
install: function (obj, plugin, project, options) {
installHelper('source-file', obj, plugin.dir, project.projectDir, plugin.id, options, project);
},
uninstall: function (obj, plugin, project, options) {
uninstallHelper('source-file', obj, project.projectDir, plugin.id, options, project);
}
},
'header-file': {
install: function (obj, plugin, project, options) {
installHelper('header-file', obj, plugin.dir, project.projectDir, plugin.id, options, project);
},
uninstall: function (obj, plugin, project, options) {
uninstallHelper('header-file', obj, project.projectDir, plugin.id, options, project);
}
},
'resource-file': {
install: function (obj, plugin, project, options) {
const src = obj.src;
let target = obj.target;
const srcFile = path.resolve(plugin.dir, src);
if (!target) {
target = path.basename(src);
}
const destFile = path.resolve(project.resources_dir, target);
if (!fs.existsSync(srcFile)) {
throw new CordovaError(`Cannot find resource file "${srcFile}" for plugin ${plugin.id} in iOS platform`);
}
if (fs.existsSync(destFile)) {
throw new CordovaError(`File already exists at destination "${destFile}" for resource file specified by plugin ${plugin.id} in iOS platform`);
}
project.xcode.addResourceFile(path.join('Resources', target));
const link = !!(options && options.link);
copyFile(plugin.dir, src, project.projectDir, destFile, link);
},
uninstall: function (obj, plugin, project, options) {
const src = obj.src;
let target = obj.target;
if (!target) {
target = path.basename(src);
}
const destFile = path.resolve(project.resources_dir, target);
project.xcode.removeResourceFile(path.join('Resources', target));
fs.removeSync(destFile);
}
},
framework: { // CB-5238 custom frameworks only
install: function (obj, plugin, project, options) {
const src = obj.src;
const custom = !!(obj.custom); // convert to boolean (if truthy/falsy)
const embed = !!(obj.embed); // convert to boolean (if truthy/falsy)
const link = !embed; // either link or embed can be true, but not both. the other has to be false
if (!custom) {
const keepFrameworks = keep_these_frameworks;
if (keepFrameworks.indexOf(src) < 0) {
if (obj.type === 'podspec') {
// podspec handled in Api.js
} else {
project.frameworks[src] = project.frameworks[src] || 0;
project.frameworks[src]++;
const opt = { customFramework: false, embed: false, link: true, weak: obj.weak };
events.emit('verbose', util.format('Adding non-custom framework to project... %s -> %s', src, JSON.stringify(opt)));
project.xcode.addFramework(src, opt);
events.emit('verbose', util.format('Non-custom framework added to project. %s -> %s', src, JSON.stringify(opt)));
}
}
return;
}
const srcFile = path.resolve(plugin.dir, src);
const targetDir = path.resolve(project.plugins_dir, plugin.id, path.basename(src));
if (!fs.existsSync(srcFile)) throw new CordovaError(`Cannot find framework "${srcFile}" for plugin ${plugin.id} in iOS platform`);
if (fs.existsSync(targetDir)) throw new CordovaError(`Framework "${targetDir}" for plugin ${plugin.id} already exists in iOS platform`);
const symlink = !!(options && options.link);
copyFile(plugin.dir, src, project.projectDir, targetDir, symlink); // frameworks are directories
// CB-10773 translate back slashes to forward on win32
const project_relative = fixPathSep(path.relative(project.projectDir, targetDir));
// CB-11233 create Embed Frameworks Build Phase if does not exist
const existsEmbedFrameworks = project.xcode.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Frameworks');
if (!existsEmbedFrameworks && embed) {
events.emit('verbose', '"Embed Frameworks" Build Phase (Embedded Binaries) does not exist, creating it.');
project.xcode.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Embed Frameworks', null, 'frameworks');
}
const opt = { customFramework: true, embed, link, sign: true };
events.emit('verbose', util.format('Adding custom framework to project... %s -> %s', src, JSON.stringify(opt)));
project.xcode.addFramework(project_relative, opt);
events.emit('verbose', util.format('Custom framework added to project. %s -> %s', src, JSON.stringify(opt)));
},
uninstall: function (obj, plugin, project, options) {
const src = obj.src;
if (!obj.custom) { // CB-9825 cocoapod integration for plugins
const keepFrameworks = keep_these_frameworks;
if (keepFrameworks.indexOf(src) < 0) {
if (obj.type !== 'podspec') {
// this should be refactored
project.frameworks[src] = project.frameworks[src] || 1;
project.frameworks[src]--;
if (project.frameworks[src] < 1) {
// Only remove non-custom framework from xcode project
// if there is no references remains
project.xcode.removeFramework(src);
delete project.frameworks[src];
}
}
}
return;
}
const targetDir = fixPathSep(path.resolve(project.plugins_dir, plugin.id, path.basename(src)));
const pbxFile = project.xcode.removeFramework(targetDir, { customFramework: true });
if (pbxFile) {
project.xcode.removeFromPbxEmbedFrameworksBuildPhase(pbxFile);
}
fs.removeSync(targetDir);
}
},
'lib-file': {
install: function (obj, plugin, project, options) {
events.emit('verbose', '<lib-file> install is not supported for iOS plugins');
},
uninstall: function (obj, plugin, project, options) {
events.emit('verbose', '<lib-file> uninstall is not supported for iOS plugins');
}
},
asset: {
install: function (obj, plugin, project, options) {
if (!obj.src) {
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
}
if (!obj.target) {
throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
}
copyFile(plugin.dir, obj.src, project.www, obj.target);
if (options && options.usePlatformWww) copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
},
uninstall: function (obj, plugin, project, options) {
const target = obj.target;
if (!target) {
throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
}
removeFile(project.www, target);
removeFileF(path.resolve(project.www, 'plugins', plugin.id));
if (options && options.usePlatformWww) {
removeFile(project.platformWww, target);
removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
}
}
},
'js-module': {
install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory.
const moduleSource = path.resolve(plugin.dir, obj.src);
const moduleName = `${plugin.id}.${obj.name || path.basename(obj.src, path.extname(obj.src))}`;
// Read in the file, prepend the cordova.define, and write it back out.
let scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
if (moduleSource.match(/.*\.json$/)) {
scriptContent = `module.exports = ${scriptContent}`;
}
scriptContent = `cordova.define("${moduleName}", function(require, exports, module) {\n${scriptContent}\n});\n`;
const moduleDestination = path.resolve(project.www, 'plugins', plugin.id, obj.src);
fs.ensureDirSync(path.dirname(moduleDestination));
fs.writeFileSync(moduleDestination, scriptContent, 'utf-8');
if (options && options.usePlatformWww) {
const platformWwwDestination = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
fs.ensureDirSync(path.dirname(platformWwwDestination));
fs.writeFileSync(platformWwwDestination, scriptContent, 'utf-8');
}
},
uninstall: function (obj, plugin, project, options) {
const pluginRelativePath = path.join('plugins', plugin.id, obj.src);
removeFileAndParents(project.www, pluginRelativePath);
if (options && options.usePlatformWww) removeFileAndParents(project.platformWww, pluginRelativePath);
}
}
};
module.exports.getInstaller = type => {
if (handlers[type] && handlers[type].install) {
return handlers[type].install;
}
events.emit('warn', `<${type}> is not supported for iOS plugins`);
};
module.exports.getUninstaller = type => {
if (handlers[type] && handlers[type].uninstall) {
return handlers[type].uninstall;
}
events.emit('warn', `<${type}> is not supported for iOS plugins`);
};
function installHelper (type, obj, plugin_dir, project_dir, plugin_id, options, project) {
const srcFile = path.resolve(plugin_dir, obj.src);
const targetDir = path.resolve(project.plugins_dir, plugin_id, obj.targetDir || '');
const destFile = path.join(targetDir, path.basename(obj.src));
let project_ref;
const link = !!(options && options.link);
if (link) {
const trueSrc = fs.realpathSync(srcFile);
// Create a symlink in the expected place, so that uninstall can use it.
if (options && options.force) {
copyFile(plugin_dir, trueSrc, project_dir, destFile, link);
} else {
copyNewFile(plugin_dir, trueSrc, project_dir, destFile, link);
}
// Xcode won't save changes to a file if there is a symlink involved.
// Make the Xcode reference the file directly.
// Note: Can't use path.join() here since it collapses 'Plugins/..', and xcode
// library special-cases Plugins/ prefix.
project_ref = `Plugins/${fixPathSep(path.relative(fs.realpathSync(project.plugins_dir), trueSrc))}`;
} else {
if (options && options.force) {
copyFile(plugin_dir, srcFile, project_dir, destFile, link);
} else {
copyNewFile(plugin_dir, srcFile, project_dir, destFile, link);
}
project_ref = `Plugins/${fixPathSep(path.relative(project.plugins_dir, destFile))}`;
}
if (type === 'header-file') {
project.xcode.addHeaderFile(project_ref);
} else if (obj.framework) {
const opt = { weak: obj.weak };
const project_relative = path.join(path.basename(project.xcode_path), project_ref);
project.xcode.addFramework(project_relative, opt);
project.xcode.addToLibrarySearchPaths({ path: project_ref });
} else {
project.xcode.addSourceFile(project_ref, obj.compilerFlags ? { compilerFlags: obj.compilerFlags } : {});
}
}
function uninstallHelper (type, obj, project_dir, plugin_id, options, project) {
const targetDir = path.resolve(project.plugins_dir, plugin_id, obj.targetDir || '');
const destFile = path.join(targetDir, path.basename(obj.src));
let project_ref;
const link = !!(options && options.link);
if (link) {
const trueSrc = fs.readlinkSync(destFile);
project_ref = `Plugins/${fixPathSep(path.relative(fs.realpathSync(project.plugins_dir), trueSrc))}`;
} else {
project_ref = `Plugins/${fixPathSep(path.relative(project.plugins_dir, destFile))}`;
}
fs.removeSync(targetDir);
if (type === 'header-file') {
project.xcode.removeHeaderFile(project_ref);
} else if (obj.framework) {
const project_relative = path.join(path.basename(project.xcode_path), project_ref);
project.xcode.removeFramework(project_relative);
project.xcode.removeFromLibrarySearchPaths({ path: project_ref });
} else {
project.xcode.removeSourceFile(project_ref);
}
}
const pathSepFix = new RegExp(path.sep.replace(/\\/, '\\\\'), 'g');
function fixPathSep (file) {
return file.replace(pathSepFix, '/');
}
function copyFile (plugin_dir, src, project_dir, dest, link) {
src = path.resolve(plugin_dir, src);
if (!fs.existsSync(src)) throw new CordovaError(`"${src}" not found!`);
// check that src path is inside plugin directory
const real_path = fs.realpathSync(src);
const real_plugin_path = fs.realpathSync(plugin_dir);
if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError(`File "${src}" is located outside the plugin directory "${plugin_dir}"`); }
dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError(`Destination "${dest}" for source file "${src}" is located outside the project`); }
fs.ensureDirSync(path.dirname(dest));
if (link) {
linkFileOrDirTree(src, dest);
} else {
fs.copySync(src, dest);
}
}
// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
const target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path)) { throw new CordovaError(`"${target_path}" already exists!`); }
copyFile(plugin_dir, src, project_dir, dest, !!link);
}
function linkFileOrDirTree (src, dest) {
if (fs.existsSync(dest)) {
fs.removeSync(dest);
}
if (fs.statSync(src).isDirectory()) {
fs.ensureDirSync(dest);
fs.readdirSync(src).forEach(entry => {
linkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
} else {
fs.linkSync(src, dest);
}
}
// checks if file exists and then deletes. Error if doesn't exist
function removeFile (project_dir, src) {
const file = path.resolve(project_dir, src);
fs.removeSync(file);
}
// deletes file/directory without checking
function removeFileF (file) {
fs.removeSync(file);
}
function removeFileAndParents (baseDir, destFile, stopper) {
stopper = stopper || '.';
const file = path.resolve(baseDir, destFile);
if (!fs.existsSync(file)) return;
removeFileF(file);
// check if directory is empty
let curDir = path.dirname(file);
while (curDir !== path.resolve(baseDir, stopper)) {
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
fs.rmdirSync(curDir);
curDir = path.resolve(curDir, '..');
} else {
// directory not empty...do nothing
break;
}
}
}
function generateAttributeError (attribute, element, id) {
return `Required attribute "${attribute}" not specified in <${element}> element from plugin: ${id}`;
}
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const xcode = require('xcode');
const plist = require('plist');
const _ = require('underscore');
const path = require('path');
const fs = require('fs-extra');
const pluginHandlers = require('./plugman/pluginHandlers');
const CordovaError = require('cordova-common').CordovaError;
const cachedProjectFiles = {};
function parseProjectFile (locations) {
const project_dir = locations.root;
const pbxPath = locations.pbxproj;
if (cachedProjectFiles[project_dir]) {
return cachedProjectFiles[project_dir];
}
const xcodeproj = xcode.project(pbxPath);
xcodeproj.parseSync();
const xcBuildConfiguration = xcodeproj.pbxXCBuildConfigurationSection();
const plist_file_entry = _.find(xcBuildConfiguration, entry => entry.buildSettings && entry.buildSettings.INFOPLIST_FILE);
const plist_file = path.join(project_dir, plist_file_entry.buildSettings.INFOPLIST_FILE.replace(/^"(.*)"$/g, '$1').replace(/\\&/g, '&'));
const config_file = path.join(path.dirname(plist_file), 'config.xml');
if (!fs.existsSync(plist_file) || !fs.existsSync(config_file)) {
throw new CordovaError('Could not find *-Info.plist file, or config.xml file.');
}
const frameworks_file = path.join(project_dir, 'frameworks.json');
let frameworks = {};
try {
frameworks = require(frameworks_file);
} catch (e) { }
const xcode_dir = path.dirname(plist_file);
const pluginsDir = path.resolve(xcode_dir, 'Plugins');
const resourcesDir = path.resolve(xcode_dir, 'Resources');
cachedProjectFiles[project_dir] = {
plugins_dir: pluginsDir,
resources_dir: resourcesDir,
xcode: xcodeproj,
xcode_path: xcode_dir,
pbx: pbxPath,
projectDir: project_dir,
platformWww: path.join(project_dir, 'platform_www'),
www: path.join(project_dir, 'www'),
write: function () {
fs.writeFileSync(pbxPath, xcodeproj.writeSync());
if (Object.keys(this.frameworks).length === 0) {
// If there is no framework references remain in the project, just remove this file
fs.removeSync(frameworks_file);
return;
}
fs.writeFileSync(frameworks_file, JSON.stringify(this.frameworks, null, 4));
},
getPackageName: function () {
const packageName = plist.parse(fs.readFileSync(plist_file, 'utf8')).CFBundleIdentifier;
let bundleIdentifier = packageName;
const variables = packageName.match(/\$\((\w+)\)/); // match $(VARIABLE), if any
if (variables && variables.length >= 2) {
bundleIdentifier = xcodeproj.getBuildProperty(variables[1]);
}
return bundleIdentifier.replace(/^"/, '').replace(/"$/, '');
},
getInstaller: function (name) {
return pluginHandlers.getInstaller(name);
},
getUninstaller: function (name) {
return pluginHandlers.getUninstaller(name);
},
frameworks
};
return cachedProjectFiles[project_dir];
}
function purgeProjectFileCache (project_dir) {
delete cachedProjectFiles[project_dir];
}
module.exports = {
parse: parseProjectFile,
purgeProjectFileCache
};
xcode.project.prototype.pbxEmbedFrameworksBuildPhaseObj = function (target) {
return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Frameworks', target);
};
xcode.project.prototype.addToPbxEmbedFrameworksBuildPhase = function (file) {
const sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
if (sources) {
sources.files.push(pbxBuildPhaseObj(file));
}
};
xcode.project.prototype.removeFromPbxEmbedFrameworksBuildPhase = function (file) {
const sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
if (sources) {
sources.files = _.reject(sources.files, file => file.comment === longComment(file));
}
};
// special handlers to add frameworks to the 'Embed Frameworks' build phase, needed for custom frameworks
// see CB-9517. should probably be moved to node-xcode.
const util = require('util');
function pbxBuildPhaseObj (file) {
const obj = Object.create(null);
obj.value = file.uuid;
obj.comment = longComment(file);
return obj;
}
function longComment (file) {
return util.format('%s in %s', file.basename, file.group);
}
+260
View File
@@ -0,0 +1,260 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const path = require('path');
const build = require('./build');
const {
CordovaError,
events,
superspawn: { spawn }
} = require('cordova-common');
const check_reqs = require('./check_reqs');
const fs = require('fs-extra');
const cordovaPath = path.join(__dirname, '..');
const projectPath = path.join(__dirname, '..', '..');
module.exports.run = runOptions => {
// Validate args
if (runOptions.device && runOptions.emulator) {
return Promise.reject(new CordovaError('Only one of "device"/"emulator" options should be specified'));
}
// support for CB-8168 `cordova/run --list`
if (runOptions.list) {
if (runOptions.device) return module.exports.listDevices();
if (runOptions.emulator) return module.exports.listEmulators();
// if no --device or --emulator flag is specified, list both devices and emulators
return module.exports.listDevices().then(() => module.exports.listEmulators());
}
let useDevice = !!runOptions.device;
return require('./listDevices').run()
.then(devices => {
if (devices.length > 0 && !(runOptions.emulator)) {
useDevice = true;
// we also explicitly set device flag in options as we pass
// those parameters to other api (build as an example)
runOptions.device = true;
return check_reqs.check_ios_deploy();
}
}).then(() => {
if (!runOptions.nobuild) {
return build.run(runOptions);
} else {
return Promise.resolve();
}
}).then(() => build.findXCodeProjectIn(projectPath))
.then(projectName => {
let appPath = path.join(projectPath, 'build', 'emulator', `${projectName}.app`);
const buildOutputDir = path.join(projectPath, 'build', 'device');
// select command to run and arguments depending whether
// we're running on device/emulator
if (useDevice) {
return module.exports.checkDeviceConnected()
.then(() => {
// Unpack IPA
const ipafile = path.join(buildOutputDir, `${projectName}.ipa`);
// unpack the existing platform/ios/build/device/appname.ipa (zipfile), will create a Payload folder
return spawn('unzip', ['-o', '-qq', ipafile], { cwd: buildOutputDir, printCommand: true, stdio: 'inherit' });
})
.then(() => {
// Uncompress IPA (zip file)
const appFileInflated = path.join(buildOutputDir, 'Payload', `${projectName}.app`);
const appFile = path.join(buildOutputDir, `${projectName}.app`);
const payloadFolder = path.join(buildOutputDir, 'Payload');
// delete the existing platform/ios/build/device/appname.app
fs.removeSync(appFile);
// move the platform/ios/build/device/Payload/appname.app to parent
fs.moveSync(appFileInflated, appFile);
// delete the platform/ios/build/device/Payload folder
fs.removeSync(payloadFolder);
return null;
})
.then(
() => {
appPath = path.join(projectPath, 'build', 'device', `${projectName}.app`);
let extraArgs = [];
if (runOptions.argv) {
// argv.slice(2) removes node and run.js, filterSupportedArgs removes the run.js args
extraArgs = module.exports.filterSupportedArgs(runOptions.argv.slice(2));
}
return module.exports.deployToDevice(appPath, runOptions.target, extraArgs);
},
// if device connection check failed use emulator then
() => module.exports.deployToSim(appPath, runOptions.target)
);
} else {
return module.exports.deployToSim(appPath, runOptions.target);
}
});
};
module.exports.filterSupportedArgs = filterSupportedArgs;
module.exports.checkDeviceConnected = checkDeviceConnected;
module.exports.deployToDevice = deployToDevice;
module.exports.deployToSim = deployToSim;
module.exports.startSim = startSim;
module.exports.listDevices = listDevices;
module.exports.listEmulators = listEmulators;
/**
* Filters the args array and removes supported args for the 'run' command.
*
* @return {Array} array with unsupported args for the 'run' command
*/
function filterSupportedArgs (args) {
const filtered = [];
const sargs = ['--device', '--emulator', '--nobuild', '--list', '--target', '--debug', '--release'];
const re = new RegExp(sargs.join('|'));
args.forEach(element => {
// supported args not found, we add
// we do a regex search because --target can be "--target=XXX"
if (element.search(re) === -1) {
filtered.push(element);
}
}, this);
return filtered;
}
/**
* Checks if any iOS device is connected
* @return {Promise} Fullfilled when any device is connected, rejected otherwise
*/
function checkDeviceConnected () {
return spawn('ios-deploy', ['-c', '-t', '1'], { printCommand: true, stdio: 'inherit' });
}
/**
* Deploy specified app package to connected device
* using ios-deploy command
* @param {String} appPath Path to application package
* @return {Promise} Resolves when deploy succeeds otherwise rejects
*/
function deployToDevice (appPath, target, extraArgs) {
events.emit('log', 'Deploying to device');
// Deploying to device...
if (target) {
return spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath, '-i', target].concat(extraArgs), { printCommand: true, stdio: 'inherit' });
} else {
return spawn('ios-deploy', ['--justlaunch', '--no-wifi', '-d', '-b', appPath].concat(extraArgs), { printCommand: true, stdio: 'inherit' });
}
}
/**
* Deploy specified app package to ios-sim simulator
* @param {String} appPath Path to application package
* @param {String} target Target device type
* @return {Promise} Resolves when deploy succeeds otherwise rejects
*/
function deployToSim (appPath, target) {
events.emit('log', 'Deploying to simulator');
if (!target) {
// Select target device for emulator
return require('./listEmulatorImages').run()
.then(emulators => {
if (emulators.length > 0) {
target = emulators[0];
}
emulators.forEach(emulator => {
if (emulator.indexOf('iPhone') === 0) {
target = emulator;
}
});
events.emit('log', `No target specified for emulator. Deploying to "${target}" simulator.`);
return startSim(appPath, target);
});
} else {
return startSim(appPath, target);
}
}
function startSim (appPath, target) {
const logPath = path.join(cordovaPath, 'console.log');
return iossimLaunch(appPath, `com.apple.CoreSimulator.SimDeviceType.${target}`, logPath, '--exit');
}
function iossimLaunch (appPath, devicetypeid, log, exit) {
return spawn(
require.resolve('ios-sim/bin/ios-sim'),
['launch', appPath, '--devicetypeid', devicetypeid, '--log', log, exit],
{ cwd: projectPath, printCommand: true }
).progress(stdio => {
if (stdio.stderr) {
events.emit('error', `[ios-sim] ${stdio.stderr}`);
}
if (stdio.stdout) {
events.emit('log', `[ios-sim] ${stdio.stdout.trim()}`);
}
})
.then(result => {
events.emit('log', 'Simulator successfully started via `ios-sim`.');
});
}
function listDevices () {
return require('./listDevices').run()
.then(devices => {
events.emit('log', 'Available iOS Devices:');
devices.forEach(device => {
events.emit('log', `\t${device}`);
});
});
}
function listEmulators () {
return require('./listEmulatorImages').run()
.then(emulators => {
events.emit('log', 'Available iOS Simulators:');
emulators.forEach(emulator => {
events.emit('log', `\t${emulator}`);
});
});
}
module.exports.help = () => {
console.log('\nUsage: run [ --device | [ --emulator [ --target=<id> ] ] ] [ --debug | --release | --nobuild ]');
// TODO: add support for building different archs
// console.log(" [ --archs=\"<list of target architectures>\" ] ");
console.log(' --device : Deploys and runs the project on the connected device.');
console.log(' --emulator : Deploys and runs the project on an emulator.');
console.log(' --target=<id> : Deploys and runs the project on the specified target.');
console.log(' --debug : Builds project in debug mode. (Passed down to build command, if necessary)');
console.log(' --release : Builds project in release mode. (Passed down to build command, if necessary)');
console.log(' --nobuild : Uses pre-built package, or errors if project is not built.');
// TODO: add support for building different archs
// console.log(" --archs : Specific chip architectures (`anycpu`, `arm`, `x86`, `x64`).");
console.log('');
console.log('Examples:');
console.log(' run');
console.log(' run --device');
console.log(' run --emulator --target=\"iPhone-6-Plus\"'); /* eslint no-useless-escape : 0 */
console.log(' run --device --release');
console.log(' run --emulator --debug');
console.log('');
process.exit(0);
};
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Run the below to get the device targets:
# xcrun instruments -s
set -e
DEFAULT_TARGET="iPhone 5s"
TARGET=${1:-$DEFAULT_TARGET}
LIB_PATH=$( cd "$( dirname "$0" )" && pwd -P)
xcrun instruments -w "$TARGET" &> /dev/null
+119
View File
@@ -0,0 +1,119 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const {
CordovaError,
superspawn: { spawn }
} = require('cordova-common');
const semver = require('semver');
function fetchSdkVersionByType (sdkType) {
return spawn('xcodebuild', ['-showsdks'])
.then(output => {
const regexSdk = new RegExp(`^${sdkType} \\d`);
const versions = output.split('\n')
.filter(line => line.trim().match(regexSdk))
.map(line => line.match(/\d+\.\d+/)[0])
.sort(exports.compareVersions);
console.log(versions[0]);
});
}
exports.get_apple_ios_version = () => {
return fetchSdkVersionByType('iOS');
};
exports.get_apple_osx_version = () => {
return fetchSdkVersionByType('macOS');
};
exports.get_apple_xcode_version = () => {
return spawn('xcodebuild', ['-version'])
.then(output => {
const versionMatch = /Xcode (.*)/.exec(output);
if (!versionMatch) return Promise.reject(output);
return versionMatch[1];
});
};
/**
* Gets ios-deploy util version
* @return {Promise} Promise that either resolved with ios-deploy version
* or rejected in case of error
*/
exports.get_ios_deploy_version = () => {
return spawn('ios-deploy', ['--version']);
};
/**
* Gets pod (CocoaPods) util version
* @return {Promise} Promise that either resolved with pod version
* or rejected in case of error
*/
exports.get_cocoapods_version = () => {
return spawn('pod', ['--version']);
};
/**
* Gets ios-sim util version
* @return {Promise} Promise that either resolved with ios-sim version
* or rejected in case of error
*/
exports.get_ios_sim_version = () => {
return spawn('ios-sim', ['--version']);
};
/**
* Gets specific tool version
* @param {String} toolName Tool name to check. Known tools are 'xcodebuild', 'ios-sim' and 'ios-deploy'
* @return {Promise} Promise that either resolved with tool version
* or rejected in case of error
*/
exports.get_tool_version = toolName => {
switch (toolName) {
case 'xcodebuild': return exports.get_apple_xcode_version();
case 'ios-sim': return exports.get_ios_sim_version();
case 'ios-deploy': return exports.get_ios_deploy_version();
case 'pod': return exports.get_cocoapods_version();
default: return Promise.reject(new CordovaError(`${toolName} is not valid tool name. Valid names are: 'xcodebuild', 'ios-sim', 'ios-deploy', and 'pod'`));
}
};
/**
* Compares two version strings that can be coerced to semver.
*
* @param {String} version1 Version to compare
* @param {String} version2 Another version to compare
* @return {Number} Negative number if first version is lower than the second,
* positive otherwise and 0 if versions are equal.
*/
exports.compareVersions = (...args) => {
const coerceToSemverIfInvalid = v => {
const semverVersion = semver.parse(v) || semver.coerce(v);
if (!semverVersion) throw new TypeError(`Invalid Version: ${v}`);
return semverVersion;
};
const semverVersions = args.map(coerceToSemverIfInvalid);
return semver.compare(...semverVersions);
};
+23
View File
@@ -0,0 +1,23 @@
#! /bin/sh
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd -P)
tail -f "$CORDOVA_PATH/console.log"
+19
View File
@@ -0,0 +1,19 @@
:: Licensed to the Apache Software Foundation (ASF) under one
:: or more contributor license agreements. See the NOTICE file
:: distributed with this work for additional information
:: regarding copyright ownership. The ASF licenses this file
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
:: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations
:: under the License
@ECHO OFF
ECHO WARN: The 'log' command is not available for cordova-ios on windows machines.>&2
+30
View File
@@ -0,0 +1,30 @@
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const CordovaLogger = require('cordova-common').CordovaLogger;
module.exports = {
adjustLoggerLevel: function (opts) {
if (opts.verbose || (Array.isArray(opts) && opts.indexOf('--verbose') !== -1)) {
CordovaLogger.get().setLevel('verbose');
} else if (opts.silent || (Array.isArray(opts) && opts.indexOf('--silent') !== -1)) {
CordovaLogger.get().setLevel('error');
}
}
};
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../which/bin/node-which" "$@"
ret=$?
else
node "$basedir/../which/bin/node-which" "$@"
ret=$?
fi
exit $ret

Some files were not shown because too many files have changed in this diff Show More