J'ai ajouté une extension de partage pour mon application dire SAMPLE (existe déjà sur l'App Store), appelé laisse dire SAMPLESHARE. Chaque fois qu'un user, par exemple, prend une photo et essaie de la partager, je veux qu'il passe par un controller de vue d'une fonctionnalité Open In, et qu'il n'obtienne pas le dialog Post d'Apple, en le contournant fondamentalement. J'essaie donc de partager l'image entre l'extension de partage et mon application, en créant un groupe d'applications partagé entre l'application et le plugin, puis en transmettant les paths d'access au file openURL du délégué d'application de mon application.
Donc, dans mon principal requestur d'application, j'ai
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSSsortingng *)sourceApplication annotation:(id)annotation { return [[SAMPLEExternalFileHandler shared] handleExternalFileURL:url]; }
ce que j'utilise fondamentalement pour vérifier chaque fois si j'ai un path de file d'URL qui doit ouvrir un stream différent.
Dans mon PARTAGE, j'ai
#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. NSSsortingng * m_invokeArgs = NULL; // A ssortingng to be passed to your AIR app with information about the attachments. NSSsortingng * APP_SHARE_GROUP = @"group.com.SAMPLE.SAMPLESHAREPLUGIN"; const NSSsortingng * 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: ( NSSsortingng * ) 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 = [ NSSsortingng ssortingngWithFormat: @"%@,%@", m_invokeArgs, imagePath ]; } } - ( NSSsortingng * ) saveImageToAppGroupFolder: ( UIImage * ) image imageIndex: ( int ) imageIndex { assert( NULL != image ); NSData * jpegData = UIImageJPEGRepresentation( image, 1.0 ); NSURL * containerURL = [ [ NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: APP_SHARE_GROUP ]; NSSsortingng * documentsPath = containerURL.path; // Note that we aren't using massively unique names for the files in this example: NSSsortingng * fileName = [ NSSsortingng ssortingngWithFormat: @"image%d.jpg", imageIndex ]; NSSsortingng * filePath = [ documentsPath ssortingngByAppendingPathComponent: 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: ( NSSsortingng * ) kUTTypeImage ] ) { // Load it, so we can get the path to it [ itemProvider loadItemForTypeIdentifier: ( NSSsortingng * ) kUTTypeImage options: NULL completionHandler: ^ ( UIImage * image, NSError * error ) { static int itemIdx = 0; if ( NULL != error ) { NSLog( @"There was an error resortingeving 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: NSSsortingng * 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: ( NSSsortingng * ) 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: NSSsortingng * urlSsortingng = [ NSSsortingng ssortingngWithFormat: @"%@://%@", APP_SHARE_URL_SCHEME, ( NULL == invokeArgs ? @"" : invokeArgs ) ]; NSURL * url = [ NSURL URLWithSsortingng: urlSsortingng ]; NSSsortingng *className = @"UIApplication"; if ( NSClassFromSsortingng( className ) ) { id object = [ NSClassFromSsortingng( 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
Et mon info.plist pour l'extension est
<?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> <ssortingng>en</ssortingng> <key>CFBundleDisplayName</key> <ssortingng>SAMPLESHARE</ssortingng> <key>CFBundleExecutable</key> <ssortingng>$(EXECUTABLE_NAME)</ssortingng> <key>CFBundleIdentifier</key> <ssortingng>com.org.SAMPLE.$(PRODUCT_NAME:rfc1034identifier)</ssortingng> <key>CFBundleInfoDictionaryVersion</key> <ssortingng>6.0</ssortingng> <key>CFBundleName</key> <ssortingng>$(PRODUCT_NAME)</ssortingng> <key>CFBundlePackageType</key> <ssortingng>XPC!</ssortingng> <key>CFBundleShortVersionSsortingng</key> <ssortingng>1.0</ssortingng> <key>CFBundleSignature</key> <ssortingng>????</ssortingng> <key>CFBundleVersion</key> <ssortingng>1</ssortingng> <key>NSExtension</key> <dict> <key>NSExtensionAtsortingbutes</key> <dict> <key>NSExtensionActivationRule</key> <dict> <key>NSExtensionActivationSupportsImageWithMaxCount</key> <integer>1</integer> </dict> </dict> <key>NSExtensionMainStoryboard</key> <ssortingng>MainInterface</ssortingng> <key>NSExtensionPointIdentifier</key> <ssortingng>com.apple.share-services</ssortingng> </dict> </dict> </plist>
J'ai fondamentalement utilisé un certain code de licence de commons d'Internet (site réputé), qui prétend avoir passé le process d'examen de magasin d'application.
Il y a deux solutions de contournement dans le code, l'une est d'appeler OpenURL depuis l'extension de partage (ce qui semble impossible n'est pas encore possible normalement sans solutions de contournement sur iOS 8.3 et supérieur) et la seconde est de masquer le dialog post et le keyboard cette pomme fournit par défaut quand quelqu'un clique sur partager. Cela marche.
J'ai deux questions
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?
METTRE À JOUR:
Donc, parcourant la documentation, j'ai trouvé un correctif pour la question 2, et j'ai changé cela. Maintenant tout fonctionne, et il n'y a pas de TRUEPREDICATE
, cela sera-t-il accepté dans le magasin ou existe-t-il un autre moyen de le faire?
MISE À JOUR 2:
J'ai maintenant utilisé NSUserDefaults
pour passer datatables de l'extension à l'application, devinez que c'est aussi une condition pour le partage de données.
METTRE À JOUR
L'application a été acceptée dans la révision en utilisant NSUSERDEFAULTS
comme mécanisme de transmission de message. Voici les étapes.
1.) Extension de partage:
#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. NSSsortingng * m_invokeArgs = NULL; // A ssortingng to be passed to your AIR app with information about the attachments. NSSsortingng * APP_SHARE_GROUP = @"group.com.schemename.nameofyourshareappgroup"; const NSSsortingng * 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: ( NSSsortingng * ) 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 = [ NSSsortingng ssortingngWithFormat: @"%@,%@", m_invokeArgs, imagePath ]; } } - ( NSSsortingng * ) saveImageToAppGroupFolder: ( UIImage * ) image imageIndex: ( int ) imageIndex { assert( NULL != image ); NSData * jpegData = UIImageJPEGRepresentation( image, 1.0 ); NSURL * containerURL = [ [ NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: APP_SHARE_GROUP ]; NSSsortingng * documentsPath = containerURL.path; // Note that we aren't using massively unique names for the files in this example: NSSsortingng * fileName = [ NSSsortingng ssortingngWithFormat: @"image%d.jpg", imageIndex ]; NSSsortingng * filePath = [ documentsPath ssortingngByAppendingPathComponent: 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: ( NSSsortingng * ) kUTTypeImage ] ) { // Load it, so we can get the path to it [ itemProvider loadItemForTypeIdentifier: ( NSSsortingng * ) kUTTypeImage options: NULL completionHandler: ^ ( UIImage * image, NSError * error ) { static int itemIdx = 0; if ( NULL != error ) { NSLog( @"There was an error resortingeving 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: NSSsortingng * 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: ( NSSsortingng * ) 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: NSSsortingng * urlSsortingng = [ NSSsortingng ssortingngWithFormat: @"%@://%@", APP_SHARE_URL_SCHEME, ( NULL == invokeArgs ? @"" : invokeArgs ) ]; NSURL * url = [ NSURL URLWithSsortingng: urlSsortingng ]; NSSsortingng *className = @"UIApplication"; if ( NSClassFromSsortingng( className ) ) { id object = [ NSClassFromSsortingng( 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
Dans la méthode openURL de votre délégué d'application
//Slartibartfast -- For the case where we are opening app from an extension NSSsortingng *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]; NSSsortingng *path=nil; if(defaults) { [defaults synchronize]; path = [defaults ssortingngForKey:@"url"]; } if(path.length != 0) { NSData *data; //Get file path from url shared NSSsortingng * newFilePathConverted = [STATIC_FILE_HANDLE ssortingngByAppendingSsortingng:path]; url = [ NSURL URLWithSsortingng: newFilePathConverted ]; data = [NSData dataWithContentsOfURL:url]; //Create a regular access path because this app cant preview a shared app group path NSSsortingng *regularAccessPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSSsortingng *uuid = [[NSUUID UUID] UUIDSsortingng]; //Copy file to a jpg image(ignore extension, will convert from png) NSSsortingng *uniqueFilePath= [ NSSsortingng ssortingngWithFormat: @"/image%@.jpg", uuid]; regularAccessPath = [regularAccessPath ssortingngByAppendingSsortingng:uniqueFilePath]; NSSsortingng * newFilePathConverted1 = [STATIC_FILE_HANDLE ssortingngByAppendingSsortingng:regularAccessPath]; url = [ NSURL URLWithSsortingng: newFilePathConverted1 ]; //Dump existing shared file path data into newly created file. [data writeToURL:url atomically:YES]; //Reset NSUserDefaults to Nil once file is copyd. [defaults setObject:nil forKey:@"url"]; } //Do what you want }
Merci à EasyNativeExtensions pour les pointeurs
Votre question est un peu foirée, mais s'il s'agit de transmettre des données d'une application à une autre application, vous avez une excellente solution pour ce qui est de UIPasteboard
Si vous ne rencontrez aucun problème pour passer d'une application à l'autre à l'aide de gestionnaires d'URL personnalisés, il vous rest deux étapes supplémentaires.
Étape 1
Dans votre première application qui est responsable du transfert des données, implémentez ces methods, puis appelez l'URL personnalisée.
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [[UIPasteboard generalPasteboard] setImage:passImage];
Étape 2
Dans votre controller de vue cible, appelez de nouveau UIPasteboard
et récupérez datatables.
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; UIImage *getImage = pasteboard.image;
S'il vous plaît noter que vous passez un UIImage
et vous l'obtenez dans le même type