diff --git a/README.md b/README.md index 3aceadb..b707671 100644 --- a/README.md +++ b/README.md @@ -72,28 +72,11 @@ cordova plugin add cc.fovea.cordova.openwith \ | `IOS_URL_SCHEME` | uniquelonglowercase | **iOS only** Any random long string of lowercase alphabetical characters | | `IOS_UNIFORM_TYPE_IDENTIFIER` | public.image | **iOS only** UTI of documents you want to share (check [Apple's System-Declared UTI](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html#//apple_ref/doc/uid/TP40009259-SW1)) | | `IOS_GROUP_IDENTIFIER` | group.my.app.id | **iOS only** Custom app group name. Default is `group..shareextension`. | -| `SHAREEXT_PROVISIONING_PROFILE` | 9dfsdf-.... | **iOS only** Developer account teamId | -| `SHAREEXT_DEVELOPMENT_TEAM` | 00B000A09l | **iOS only** UUID of provisioning profile for singing | +| `SHAREEXT_PROVISIONING_PROFILE` | 9dfsdf-.... | **iOS only** UUID of provisioning profile for singing | +| `SHAREEXT_DEVELOPMENT_TEAM` | 00B000A09l | **iOS only** Developer account teamId | It shouldn't be too hard. But just in case, I [posted a screencast of it](https://youtu.be/eaE4m_xO1mg). -### iOS Setup - -After having installed the plugin, with the ios platform in place, 1 operation needs to be done manually: setup the App Group on both the Cordova App and the Share Extension. - - 1. open the **xcodeproject** for your application - 1. select the root element of your **project navigator** (the left-side pane) - 1. select the **target** of your application - 1. select **capabilities** - 1. scroll down to **App Groups** - 1. make sure it's **ON** - 1. create and activate an **App Group** called: `group..shareextension` - 1. repeat the previous five steps for the **ShareExtension target**. - -You might also have to select a Team for both the App and Share Extension targets, make sure to select the same. - -Build, XCode might complain about a few things to setup that it will fix for you (creation entitlements files, etc). - ### Advanced installation options If you do not need anything fancy, you can skip this section. diff --git a/hooks/iosAddTarget.js b/hooks/iosAddTarget.js index cd8e204..3b88ed1 100755 --- a/hooks/iosAddTarget.js +++ b/hooks/iosAddTarget.js @@ -34,6 +34,8 @@ const BUNDLE_SUFFIX = '.shareextension'; var fs = require('fs'); var path = require('path'); +var packageJson; +var bundleIdentifier; function redError(message) { return new Error('"' + PLUGIN_ID + '" \x1b[1m\x1b[31m' + message + '\x1b[0m'); @@ -93,24 +95,19 @@ function getPreferenceValue(configXml, name) { } function getCordovaParameter(configXml, variableName) { - var variable; - var arg = process.argv.filter(function(arg) { - return arg.indexOf(variableName + '=') == 0; - }); - if (arg.length >= 1) { - variable = arg[0].split('=')[1]; - } else { + var variable = packageJson.cordova.plugins[PLUGIN_ID][variableName]; + if (!variable) { variable = getPreferenceValue(configXml, variableName); } return variable; } // Get the bundle id from config.xml -// function getBundleId(context, configXml) { -// var elementTree = require('elementtree'); -// var etree = elementTree.parse(configXml); -// return etree.getroot().get('id'); -// } +function getBundleId(context, configXml) { + var elementTree = require('elementtree'); + var etree = elementTree.parse(configXml); + return etree.getroot().get('id'); +} function parsePbxProject(context, pbxProjectPath) { var xcode = require('xcode'); @@ -127,6 +124,10 @@ function parsePbxProject(context, pbxProjectPath) { function forEachShareExtensionFile(context, callback) { var shareExtensionFolder = path.join(iosFolder(context), 'ShareExtension'); + if (!fs.existsSync(shareExtensionFolder)) { + console.error('!! Shared extension files have not been copied yet!!'); + return; + } fs.readdirSync(shareExtensionFolder).forEach(function(name) { // Ignore junk files like .DS_Store if (!/^\..*/.test(name)) { @@ -151,8 +152,8 @@ function projectPlistJson(context, projectName) { function getPreferences(context, configXml, projectName) { var plist = projectPlistJson(context, projectName); - var group = "group." + plist.CFBundleIdentifier + BUNDLE_SUFFIX; - if (getCordovaParameter(configXml, 'GROUP_IDENTIFIER') !== "") { + var group = "group." + bundleIdentifier + BUNDLE_SUFFIX; + if (getCordovaParameter(configXml, 'IOS_GROUP_IDENTIFIER')) { group = getCordovaParameter(configXml, 'IOS_GROUP_IDENTIFIER'); } return [{ @@ -160,7 +161,7 @@ function getPreferences(context, configXml, projectName) { value: projectName }, { key: '__BUNDLE_IDENTIFIER__', - value: plist.CFBundleIdentifier + BUNDLE_SUFFIX + value: bundleIdentifier + BUNDLE_SUFFIX } ,{ key: '__GROUP_IDENTIFIER__', value: group @@ -215,6 +216,8 @@ module.exports = function (context) { var Q = require('q'); var deferral = new Q.defer(); + packageJson = require(path.join(context.opts.projectRoot, 'package.json')); + // if (context.opts.cordova.platforms.indexOf('ios') < 0) { // log('You have to add the ios platform before adding this plugin!', 'error'); // } @@ -224,6 +227,8 @@ module.exports = function (context) { configXml = configXml.substring(configXml.indexOf('<')); } + bundleIdentifier = getBundleId(context, configXml); + findXCodeproject(context, function(projectFolder, projectName) { console.log(' - Folder containing your iOS project: ' + iosFolder(context)); @@ -241,7 +246,7 @@ module.exports = function (context) { }); // Find if the project already contains the target and group - var target = pbxProject.pbxTargetByName('ShareExt'); + var target = pbxProject.pbxTargetByName('ShareExt') || pbxProject.pbxTargetByName('"ShareExt"'); if (target) { console.log(' ShareExt target already exists.'); } @@ -249,7 +254,7 @@ module.exports = function (context) { if (!target) { // Add PBXNativeTarget to the project target = pbxProject.addTarget('ShareExt', 'app_extension', 'ShareExtension'); - + // Add a new PBXSourcesBuildPhase for our ShareViewController // (we can't add it to the existing one because an extension is kind of an extra app) pbxProject.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid); @@ -261,7 +266,7 @@ module.exports = function (context) { // Create a separate PBXGroup for the shareExtensions files, name has to be unique and path must be in quotation marks var pbxGroupKey = pbxProject.findPBXGroupKey({name: 'ShareExtension'}); - if (pbxProject) { + if (pbxGroupKey) { console.log(' ShareExtension group already exists.'); } if (!pbxGroupKey) { @@ -287,6 +292,20 @@ module.exports = function (context) { pbxProject.addResourceFile(file.name, {target: target.uuid}, pbxGroupKey); }); + var configurations = pbxProject.pbxXCBuildConfigurationSection(); + for (var key in configurations) { + if (typeof configurations[key].buildSettings !== 'undefined') { + var buildSettingsObj = configurations[key].buildSettings; + if (typeof buildSettingsObj['PRODUCT_NAME'] !== 'undefined') { + buildSettingsObj['CODE_SIGN_ENTITLEMENTS'] = '"ShareExtension/ShareExtension-Entitlements.plist"'; + var productName = buildSettingsObj['PRODUCT_NAME']; + if (productName.indexOf('ShareExt') >= 0) { + buildSettingsObj['PRODUCT_BUNDLE_IDENTIFIER'] = bundleIdentifier+BUNDLE_SUFFIX; + } + } + } + } + //Add development team and provisioning profile var PROVISIONING_PROFILE = getCordovaParameter(configXml, 'SHAREEXT_PROVISIONING_PROFILE'); var DEVELOPMENT_TEAM = getCordovaParameter(configXml, 'SHAREEXT_DEVELOPMENT_TEAM'); @@ -301,6 +320,8 @@ module.exports = function (context) { if (productName.indexOf('ShareExt') >= 0) { buildSettingsObj['PROVISIONING_PROFILE'] = PROVISIONING_PROFILE; buildSettingsObj['DEVELOPMENT_TEAM'] = DEVELOPMENT_TEAM; + buildSettingsObj['CODE_SIGN_STYLE'] = 'Manual'; + buildSettingsObj['CODE_SIGN_IDENTITY'] = '"iPhone Distribution"'; console.log('Added signing identities for extension!'); } } diff --git a/package.json b/package.json index b40476d..1059368 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc.fovea.cordova.openwith", - "version": "1.2.0", + "version": "2.0.0", "description": "Cordova \"Open With\" plugin for iOS and Android", "cordova": { "id": "cc.fovea.cordova.openwith", @@ -67,6 +67,7 @@ "src/android/cc/fovea/openwith/Serializer.java", "src/ios/OpenWithPlugin.m", "src/ios/ShareExtension/MainInterface.storyboard", + "src/ios/ShareExtension/ShareExtension-Entitlements.plist", "src/ios/ShareExtension/ShareExtension-Info.plist", "src/ios/ShareExtension/ShareViewController.h", "src/ios/ShareExtension/ShareViewController.m", diff --git a/plugin.xml b/plugin.xml index 96ffbd9..29072b5 100644 --- a/plugin.xml +++ b/plugin.xml @@ -26,7 +26,7 @@ SOFTWARE. + version="2.0.0"> OpenWith Cordova "Open With" plugin for iOS and Android @@ -75,8 +75,9 @@ SOFTWARE. - - + + + diff --git a/src/ios/ShareExtension/ShareExtension-Entitlements.plist b/src/ios/ShareExtension/ShareExtension-Entitlements.plist new file mode 100644 index 0000000..93e1fc7 --- /dev/null +++ b/src/ios/ShareExtension/ShareExtension-Entitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + __GROUP_IDENTIFIER__ + + + diff --git a/src/ios/ShareExtension/ShareViewController.m b/src/ios/ShareExtension/ShareViewController.m index d70dcaf..3457f92 100644 --- a/src/ios/ShareExtension/ShareViewController.m +++ b/src/ios/ShareExtension/ShareViewController.m @@ -82,39 +82,33 @@ - (BOOL) isContentValid { } - (void) openURL:(nonnull NSURL *)url { - if (@available(iOS 13.0, *)) { - SEL selector = NSSelectorFromString(@"openURL:"); - - UIResponder* responder = self; - while ((responder = [responder nextResponder]) != nil) { - NSLog(@"responder = %@", responder); - if([responder respondsToSelector:selector] == true) { - NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:"); + + UIResponder* responder = self; + while ((responder = [responder nextResponder]) != nil) { + NSLog(@"responder = %@", responder); + if([responder respondsToSelector:selector] == true) { + NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + // Arguments + void (^completion)(BOOL success) = ^void(BOOL success) { + NSLog(@"Completions block: %i", success); + }; + if (@available(iOS 13.0, *)) { + UISceneOpenExternalURLOptions * options = [[UISceneOpenExternalURLOptions alloc] init]; + options.universalLinksOnly = false; [invocation setTarget: responder]; [invocation setSelector: selector]; [invocation setArgument: &url atIndex: 2]; - + [invocation setArgument: &options atIndex:3]; + [invocation setArgument: &completion atIndex: 4]; [invocation invoke]; break; - } - } - }else{ - SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:"); - - UIResponder* responder = self; - while ((responder = [responder nextResponder]) != nil) { - NSLog(@"responder = %@", responder); - if([responder respondsToSelector:selector] == true) { - NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; - - // Arguments + } else { NSDictionary *options = [NSDictionary dictionary]; - void (^completion)(BOOL success) = ^void(BOOL success) { - NSLog(@"Completions block: %i", success); - }; [invocation setTarget: responder]; [invocation setSelector: selector];