代码共享扩展和iOS应用程序之间的文件path/文件

我已经为我的应用添加了一个共享扩展,例如SAMPLE(app store中已经存在),称为SAMPLESHARE。 无论何时,当用户拍摄照片并尝试分享照片时,我都希望他们通过Open Infunction的视图控制器,而不会从Apple获得Post对话,基本上绕过了它。 所以我试图通过创build一个应用程序和插件之间共享的应用程序组,然后将文件path传递给我的应用程序的应用程序委托的openURL来共享共享扩展和我的应用程序之间的图片。

所以在我的主要应用程序委托

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [[SAMPLEExternalFileHandler shared] handleExternalFileURL:url]; } 

基本上我用来检查每一次,如果我有一个URL文件path需要打开不同的stream量。

在我的SHAREEXTENSION中

 #import "ShareViewController.h" #import <MobileCoreServices/UTCoreTypes.h> //Macro to hide post dialog or not, if defined, will be hidden, comment during debugging #define HIDE_POST_DIALOG @interface ShareViewController () @end @implementation ShareViewController NSUInteger m_inputItemCount = 0; // Keeps track of the number of attachments we have opened asynchronously. NSString * m_invokeArgs = NULL; // A string to be passed to your AIR app with information about the attachments. NSString * APP_SHARE_GROUP = @"group.com.SAMPLE.SAMPLESHAREPLUGIN"; const NSString * APP_SHARE_URL_SCHEME = @"SAMPLE"; CGFloat m_oldAlpha = 1.0; // Keeps the original transparency of the Post dialog for when we want to hide it. - (BOOL)isContentValid { // Do validation of contentText and/or NSExtensionContext attachments here return YES; } - ( void ) didSelectPost { #ifdef HIDE_POST_DIALOG return; #endif [ self passSelectedItemsToApp ]; // Note: This call is expected to be made here. Ignore it. We'll tell the host we are done after we've invoked the app. // [ self.extensionContext completeRequestReturningItems: @[] completionHandler: nil ]; } - ( void ) addImagePathToArgumentList: ( NSString * ) imagePath { assert( NULL != imagePath ); // The list of arguments we will pass to the AIR app when we invoke it. // It will be a comma-separated list of file paths: /path/to/image1.jpg,/path/to/image2.jpg if ( NULL == m_invokeArgs ) { m_invokeArgs = imagePath; } else { m_invokeArgs = [ NSString stringWithFormat: @"%@,%@", m_invokeArgs, imagePath ]; } } - ( NSString * ) saveImageToAppGroupFolder: ( UIImage * ) image imageIndex: ( int ) imageIndex { assert( NULL != image ); NSData * jpegData = UIImageJPEGRepresentation( image, 1.0 ); NSURL * containerURL = [ [ NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: APP_SHARE_GROUP ]; NSString * documentsPath = containerURL.path; // Note that we aren't using massively unique names for the files in this example: NSString * fileName = [ NSString stringWithFormat: @"image%d.jpg", imageIndex ]; NSString * filePath = [ documentsPath stringByAppendingPathComponent: fileName ]; [ jpegData writeToFile: filePath atomically: YES ]; return filePath; } - ( void ) passSelectedItemsToApp { NSExtensionItem * item = self.extensionContext.inputItems.firstObject; // Reset the counter and the argument list for invoking the app: m_invokeArgs = NULL; m_inputItemCount = item.attachments.count; // Iterate through the attached files for ( NSItemProvider * itemProvider in item.attachments ) { // Check if we are sharing a JPEG if ( [ itemProvider hasItemConformingToTypeIdentifier: ( NSString * ) kUTTypeImage ] ) { // Load it, so we can get the path to it [ itemProvider loadItemForTypeIdentifier: ( NSString * ) kUTTypeImage options: NULL completionHandler: ^ ( UIImage * image, NSError * error ) { static int itemIdx = 0; if ( NULL != error ) { NSLog( @"There was an error retrieving the attachments: %@", error ); return; } // The app won't be able to access the images by path directly in the Camera Roll folder, // so we temporary copy them to a folder which both the extension and the app can access: NSString * filePath = [ self saveImageToAppGroupFolder: image imageIndex: itemIdx ]; // Now add the path to the list of arguments we'll pass to the app: [ self addImagePathToArgumentList: filePath ]; // If we have reached the last attachment, it's time to hand control to the app: if ( ++itemIdx >= m_inputItemCount ) { [ self invokeApp: m_invokeArgs ]; } } ]; } } } - ( void ) invokeApp: ( NSString * ) invokeArgs { // Prepare the URL request // this will use the custom url scheme of your app // and the paths to the photos you want to share: NSString * urlString = [ NSString stringWithFormat: @"%@://%@", APP_SHARE_URL_SCHEME, ( NULL == invokeArgs ? @"" : invokeArgs ) ]; NSURL * url = [ NSURL URLWithString: urlString ]; NSString *className = @"UIApplication"; if ( NSClassFromString( className ) ) { id object = [ NSClassFromString( className ) performSelector: @selector( sharedApplication ) ]; [ object performSelector: @selector( openURL: ) withObject: url ]; } // Now let the host app know we are done, so that it unblocks its UI: [ super didSelectPost ]; } #ifdef HIDE_POST_DIALOG - ( NSArray * ) configurationItems { // Comment out this whole function if you want the Post dialog to show. [ self passSelectedItemsToApp ]; // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. return @[]; } #endif #ifdef HIDE_POST_DIALOG - ( void ) willMoveToParentViewController: ( UIViewController * ) parent { // This is called at the point where the Post dialog is about to be shown. // Make it transparent, so we don't see it, but first remember how transparent it was originally: m_oldAlpha = [ self.view alpha ]; [ self.view setAlpha: 0.0 ]; } #endif #ifdef HIDE_POST_DIALOG - ( void ) didMoveToParentViewController: ( UIViewController * ) parent { // Restore the original transparency: [ self.view setAlpha: m_oldAlpha ]; } #endif #ifdef HIDE_POST_DIALOG - ( id ) init { if ( self = [ super init ] ) { // Subscribe to the notification which will tell us when the keyboard is about to pop up: [ [ NSNotificationCenter defaultCenter ] addObserver: self selector: @selector( keyboardWillShow: ) name: UIKeyboardWillShowNotification object: nil ]; } return self; } #endif #ifdef HIDE_POST_DIALOG - ( void ) keyboardWillShow: ( NSNotification * ) note { // Dismiss the keyboard before it has had a chance to show up: [ self.view endEditing: true ]; } #endif @end 

而我的info.plist的扩展是

 <?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>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleDisplayName</key> <string>SAMPLESHARE</string> <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> <string>com.org.SAMPLE.$(PRODUCT_NAME:rfc1034identifier)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>$(PRODUCT_NAME)</string> <key>CFBundlePackageType</key> <string>XPC!</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>1</string> <key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>NSExtensionActivationRule</key> <dict> <key>NSExtensionActivationSupportsImageWithMaxCount</key> <integer>1</integer> </dict> </dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.share-services</string> </dict> </dict> </plist> 

我已经基本上使用了互联网(知名网站)的一些公共许可证代码,声称已通过应用程序商店审查过程。

在代码中有两个解决方法,一个是从共享扩展中调用OpenURL(从看来似乎在iOS 8.3和更高版本中没有解决方法似乎仍然是不可能的),第二个是隐藏后对话和键盘当任何人点击共享时,苹果默认提供。 这工作。

我有两个问题

 1.) Will this be accepted on the app store? -- basically how are apps like facebook/whatsapp doing it and they are being accepted? 2.) Whenever I run this, it says `NSExtensionActivationRule` if set to `TRUEPREDICATE` will be rejected in review, what should the value be? 

更新:

所以通过文档search,我发现问题2的修复,并改变了这一点。 现在一切正常,没有TRUEPREDICATE ,这将在商店被接受或有另一种方式来做到这一点?

更新2:

我现在已经使用NSUserDefaults将数据从扩展传递到应用程序,猜测这也是共享数据的一个要求。

UPDATE

该应用程序被接受在审查中使用NSUSERDEFAULTS作为消息传递机制。 这是步骤。

1.)分享分机:

 #import "ShareViewController.h" #import <MobileCoreServices/UTCoreTypes.h> //Macro to hide post dialog or not, if defined, will be hidden, comment during debugging #define HIDE_POST_DIALOG @interface ShareViewController () @end @implementation ShareViewController NSUInteger m_inputItemCount = 0; // Keeps track of the number of attachments we have opened asynchronously. NSString * m_invokeArgs = NULL; // A string to be passed to your AIR app with information about the attachments. NSString * APP_SHARE_GROUP = @"group.com.schemename.nameofyourshareappgroup"; const NSString * APP_SHARE_URL_SCHEME = @"schemename"; CGFloat m_oldAlpha = 1.0; // Keeps the original transparency of the Post dialog for when we want to hide it. - (BOOL)isContentValid { // Do validation of contentText and/or NSExtensionContext attachments here return YES; } - ( void ) didSelectPost { #ifdef HIDE_POST_DIALOG return; #endif [ self passSelectedItemsToApp ]; // Note: This call is expected to be made here. Ignore it. We'll tell the host we are done after we've invoked the app. // [ self.extensionContext completeRequestReturningItems: @[] completionHandler: nil ]; } - ( void ) addImagePathToArgumentList: ( NSString * ) imagePath { assert( NULL != imagePath ); // The list of arguments we will pass to the AIR app when we invoke it. // It will be a comma-separated list of file paths: /path/to/image1.jpg,/path/to/image2.jpg if ( NULL == m_invokeArgs ) { m_invokeArgs = imagePath; } else { m_invokeArgs = [ NSString stringWithFormat: @"%@,%@", m_invokeArgs, imagePath ]; } } - ( NSString * ) saveImageToAppGroupFolder: ( UIImage * ) image imageIndex: ( int ) imageIndex { assert( NULL != image ); NSData * jpegData = UIImageJPEGRepresentation( image, 1.0 ); NSURL * containerURL = [ [ NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: APP_SHARE_GROUP ]; NSString * documentsPath = containerURL.path; // Note that we aren't using massively unique names for the files in this example: NSString * fileName = [ NSString stringWithFormat: @"image%d.jpg", imageIndex ]; NSString * filePath = [ documentsPath stringByAppendingPathComponent: fileName ]; [ jpegData writeToFile: filePath atomically: YES ]; //Mahantesh -- Store image url to NSUserDefaults NSUserDefaults *defaults=[[NSUserDefaults alloc] initWithSuiteName:@"group.com.schemename.nameofyourshareappgroup"]; [defaults setObject:filePath forKey:@"url"]; [defaults synchronize]; return filePath; } - ( void ) passSelectedItemsToApp { NSExtensionItem * item = self.extensionContext.inputItems.firstObject; // Reset the counter and the argument list for invoking the app: m_invokeArgs = NULL; m_inputItemCount = item.attachments.count; // Iterate through the attached files for ( NSItemProvider * itemProvider in item.attachments ) { // Check if we are sharing a Image if ( [ itemProvider hasItemConformingToTypeIdentifier: ( NSString * ) kUTTypeImage ] ) { // Load it, so we can get the path to it [ itemProvider loadItemForTypeIdentifier: ( NSString * ) kUTTypeImage options: NULL completionHandler: ^ ( UIImage * image, NSError * error ) { static int itemIdx = 0; if ( NULL != error ) { NSLog( @"There was an error retrieving the attachments: %@", error ); return; } // The app won't be able to access the images by path directly in the Camera Roll folder, // so we temporary copy them to a folder which both the extension and the app can access: NSString * filePath = [ self saveImageToAppGroupFolder: image imageIndex: itemIdx ]; // Now add the path to the list of arguments we'll pass to the app: [ self addImagePathToArgumentList: filePath ]; // If we have reached the last attachment, it's time to hand control to the app: if ( ++itemIdx >= m_inputItemCount ) { [ self invokeApp: m_invokeArgs ]; } } ]; } } } - ( void ) invokeApp: ( NSString * ) invokeArgs { // Prepare the URL request // this will use the custom url scheme of your app // and the paths to the photos you want to share: NSString * urlString = [ NSString stringWithFormat: @"%@://%@", APP_SHARE_URL_SCHEME, ( NULL == invokeArgs ? @"" : invokeArgs ) ]; NSURL * url = [ NSURL URLWithString: urlString ]; NSString *className = @"UIApplication"; if ( NSClassFromString( className ) ) { id object = [ NSClassFromString( className ) performSelector: @selector( sharedApplication ) ]; [ object performSelector: @selector( openURL: ) withObject: url ]; } // Now let the host app know we are done, so that it unblocks its UI: [ super didSelectPost ]; } #ifdef HIDE_POST_DIALOG - ( NSArray * ) configurationItems { // Comment out this whole function if you want the Post dialog to show. [ self passSelectedItemsToApp ]; // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. return @[]; } #endif #ifdef HIDE_POST_DIALOG - ( void ) willMoveToParentViewController: ( UIViewController * ) parent { // This is called at the point where the Post dialog is about to be shown. // Make it transparent, so we don't see it, but first remember how transparent it was originally: m_oldAlpha = [ self.view alpha ]; [ self.view setAlpha: 0.0 ]; } #endif #ifdef HIDE_POST_DIALOG - ( void ) didMoveToParentViewController: ( UIViewController * ) parent { // Restore the original transparency: [ self.view setAlpha: m_oldAlpha ]; } #endif #ifdef HIDE_POST_DIALOG - ( id ) init { if ( self = [ super init ] ) { // Subscribe to the notification which will tell us when the keyboard is about to pop up: [ [ NSNotificationCenter defaultCenter ] addObserver: self selector: @selector( keyboardWillShow: ) name: UIKeyboardWillShowNotification object: nil ]; } return self; } #endif #ifdef HIDE_POST_DIALOG - ( void ) keyboardWillShow: ( NSNotification * ) note { // Dismiss the keyboard before it has had a chance to show up: [ self.view endEditing: true ]; } #endif @end 
  1. 在应用程序委托的openURL方法中

      //Slartibartfast -- For the case where we are opening app from an extension NSString *STATIC_FILE_HANDLE = @"file://"; //If app is opened from share extension, do the following /* 1.) Get path of shared file from NSUserDefaults 2.) Get data from file and store in some variable 3.) Create a new accesible unique file path 4.) Dump data created into this file. */ NSUserDefaults *defaults=[[NSUserDefaults alloc] initWithSuiteName:YOURAPP_STATIC_APP_GROUP_NAME]; NSString *path=nil; if(defaults) { [defaults synchronize]; path = [defaults stringForKey:@"url"]; } if(path.length != 0) { NSData *data; //Get file path from url shared NSString * newFilePathConverted = [STATIC_FILE_HANDLE stringByAppendingString:path]; url = [ NSURL URLWithString: newFilePathConverted ]; data = [NSData dataWithContentsOfURL:url]; //Create a regular access path because this app cant preview a shared app group path NSString *regularAccessPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *uuid = [[NSUUID UUID] UUIDString]; //Copy file to a jpg image(ignore extension, will convert from png) NSString *uniqueFilePath= [ NSString stringWithFormat: @"/image%@.jpg", uuid]; regularAccessPath = [regularAccessPath stringByAppendingString:uniqueFilePath]; NSString * newFilePathConverted1 = [STATIC_FILE_HANDLE stringByAppendingString:regularAccessPath]; url = [ NSURL URLWithString: newFilePathConverted1 ]; //Dump existing shared file path data into newly created file. [data writeToURL:url atomically:YES]; //Reset NSUserDefaults to Nil once file is copied. [defaults setObject:nil forKey:@"url"]; } //Do what you want } 

感谢EasyNativeExtensions提供的指针

你的问题有点混乱,但如果是从一个应用程序传递数据到另一个应用程序,你有一个很好的解决scheme,这是UIPasteboard

如果您在使用自定义URL处理程序的应用程序之间跳转时没有任何问题,则还剩下2个步骤。

步骤1
在您负责传递数据的第一个应用程序中实现这些方法,然后调用自定义URL。

 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [[UIPasteboard generalPasteboard] setImage:passImage]; 

第2步
在您的目标视图控制器中,再次调用UIPasteboard并从中获取数据。

 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; UIImage *getImage = pasteboard.image; 

请注意,你通过一个UIImage ,你得到它在相同的types

  1. 如果你不想从苹果的显示对话框默认。 应该从UIViewControllerinheritance而不是@interface ShareViewController:SLComposeServiceViewController
  2. 在Apple开发人员文档中,不允许直接打开扩展应用程序包含除今天扩展应用程序以外的应用程序。