Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 into j3k0-master

* 'master' of https://github.com/j3k0/cordova-plugin-openwith:
  Fix missing quotes
  Change code sign indentity and style
  Fix async call
  Added a copy files to after install hook
  IOS : Fix __BUNDLE_IDENTIFIER__ to prevent double suffix
  IOS : Add ShareExtension-Entitlements.plist to package.json
  IOS : Remove "iOS Setup" section
  IOS : Fix App Group creation and fix Share Extension Bundle Identifier
  IOS : Fix getCordovaParameter() to use package.json Because after_prepare hook does not pass installation variables in process.argv
  IOS : Move hooks on before and after prepare This allows ShareExtension to be created and updated in any case
  2.0.0
  Bump version 2.0.0
  change hook triggering iosAddTarget.js. Fixes the target not being added to xcode project. (j3k0#72)
  Fix iOS build flags (j3k0#84)
  Fix extension crash on iOS 13 (j3k0#69)
  1.3.0
  Set version in plugin.xml
  • Loading branch information
dnlek committed Jul 22, 2021
2 parents c6b2a2d + d6a2bf2 commit 88bf819
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 67 deletions.
21 changes: 2 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<YOUR_APP_BUNDLE_ID>.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.<YOUR_APP_BUNDLE_ID>.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.
Expand Down
57 changes: 39 additions & 18 deletions hooks/iosAddTarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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');
Expand All @@ -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)) {
Expand All @@ -151,16 +152,16 @@ 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 [{
key: '__DISPLAY_NAME__',
value: projectName
}, {
key: '__BUNDLE_IDENTIFIER__',
value: plist.CFBundleIdentifier + BUNDLE_SUFFIX
value: bundleIdentifier + BUNDLE_SUFFIX
} ,{
key: '__GROUP_IDENTIFIER__',
value: group
Expand Down Expand Up @@ -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');
// }
Expand All @@ -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));
Expand All @@ -241,15 +246,15 @@ 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.');
}

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);
Expand All @@ -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) {
Expand All @@ -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');
Expand All @@ -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!');
}
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down
7 changes: 4 additions & 3 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cc.fovea.cordova.openwith"
version="1.0.0">
version="2.0.0">

<name>OpenWith</name>
<description>Cordova "Open With" plugin for iOS and Android</description>
Expand Down Expand Up @@ -75,8 +75,9 @@ SOFTWARE.
</config-file>

<hook type="before_plugin_install" src="hooks/npmInstall.js" />
<hook type="before_plugin_install" src="hooks/iosCopyShareExtension.js" />
<hook type="after_plugin_install" src="hooks/iosAddTarget.js" />
<hook type="after_plugin_install" src="hooks/iosCopyShareExtension.js" />
<hook type="before_prepare" src="hooks/iosCopyShareExtension.js" />
<hook type="after_prepare" src="hooks/iosAddTarget.js" />
<hook type="before_plugin_uninstall" src="hooks/iosRemoveTarget.js" />

<framework src="MobileCoreServices.framework" />
Expand Down
10 changes: 10 additions & 0 deletions src/ios/ShareExtension/ShareExtension-Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>__GROUP_IDENTIFIER__</string>
</array>
</dict>
</plist>
46 changes: 20 additions & 26 deletions src/ios/ShareExtension/ShareViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -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<NSString *, id> *options = [NSDictionary dictionary];
void (^completion)(BOOL success) = ^void(BOOL success) {
NSLog(@"Completions block: %i", success);
};

[invocation setTarget: responder];
[invocation setSelector: selector];
Expand Down

0 comments on commit 88bf819

Please sign in to comment.