2015-08-26 9 views
14

Ich habe eine Freigabeerweiterung für meine App hinzugefügt SAMPLE (bereits im App Store vorhanden), genannt sagen SAMPLESHARE. Immer wenn ein Benutzer, sagen wir, ein Bild macht und versucht, es zu teilen, möchte ich, dass sie einen View-Controller einer Open-In-Funktion durchlaufen und den Post-Dialog von Apple nicht bekommen, sondern ihn im Prinzip umgehen. Daher versuche ich, das Bild zwischen der Freigabeerweiterung und meiner App zu teilen, indem ich eine App-Gruppe erstelle, die zwischen App und Plugin geteilt wird, und dann die Dateipfade an die openURL des Anwendungsdelegaten meiner App übergebe.Code, um Dateipfad/Datei zwischen einer Freigabeerweiterung und iOS-App freizugeben

Also in meinem Haupt Delegat habe ich

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 
{ 

    return [[SAMPLEExternalFileHandler shared] handleExternalFileURL:url]; 
} 

, die ich im Grunde für die Überprüfung jedes Mal verwenden, wenn ich eine URL Dateipfad haben, der einen anderen Fluss öffnen muss.

In meinem SHAREEXTENSION Ich habe

#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 

Und mein info.plist für die Erweiterung ist

<?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> 

ich im Grunde einige Commons-Lizenz-Code aus dem Internet (angeblich Ort), verwendet haben, die behauptet, den App-Store-Überprüfungsprozess bestanden haben. Im Code gibt es zwei Problemumgehungen: Die OpenURL von der Freigabeerweiterung aus aufzurufen (was von SO scheint, ist normalerweise ohne Workarounds auf iOS 8.3 und höher nicht möglich) und die zweite ist das Verstecken des Posts Dialog und die Tastatur, die Apple standardmäßig zur Verfügung stellt, wenn jemand auf Freigabe klickt. Das funktioniert.

Ich habe zwei Fragen

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? 

UPDATE:

So durch die Dokumentation zum Scheuern, ich habe eine Lösung für die Frage 2 gefunden, und dies änderte sich. Jetzt funktioniert alles, und es gibt keine TRUEPREDICATE, wird dies im Laden akzeptiert oder gibt es eine andere Möglichkeit, dies zu tun?

UPDATE 2:

ich jetzt NSUserDefaults verwendet haben, die Daten aus der Erweiterung der App passieren, dass erraten ist auch für den Austausch von Daten eine Anforderung.

+0

ich werde es in ein wenig aktualisieren. Ja, es in der Bewertung – Slartibartfast

+0

Vielen Dank für diesen hilfreich Beitrag angenommen wurde, verbrachte ich einige Stunden zu diesem Thema der Suche nach Informationen –

Antwort

7

UPDATE

Die App in der Überprüfung angenommen wurde mit NSUSERDEFAULTS als die Nachricht Mechanismus vorbei. Hier sind die Schritte.

1.) Teile Erweiterung:

#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. Im openURL Methode Ihrer Anwendung delegiert

     //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 
        } 
    

Dank EasyNativeExtensions für Zeiger

+0

Vielen Dank. Das hilft mir wirklich. –

0

Ihre Frage ist ein bisschen durcheinander, aber wenn es über die Weitergabe von Daten von einer Anwendung zu einer anderen App ist haben Sie eine große Lösung für das, was UIPasteboard

Wenn Sie irgendein Problem haben, nicht zwischen Anwendungen für das Springen Wenn Sie benutzerdefinierte URL-Handler verwenden, haben Sie noch 2 weitere Schritte übrig.

Schritt 1
In Ihrer ersten Anwendung, die zur Übergabe von Daten diese Methoden implementieren verantwortlich ist, und dann benutzerdefinierte URL aufrufen.

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


Schritt 2
In Ihrem Ziel-View-Controller UIPasteboard wieder einfachen Anruf und die Daten von ihm erhalten.

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


Bitte beachten Sie, dass Sie eine UIImage passieren und Sie es in der gleichen Art erhalten

+0

Ich glaube nicht, dass Sie die Absicht der Frage verstehen. Der Wunsch besteht darin, jede App, die den UiActivityViewController verwenden kann, in die Lage zu versetzen, Daten mit der App des OPs zu teilen, nicht nur mit einer anderen App, die vom OP geschrieben wurde. –

+0

@ RocketGarden du kannst das nicht tun. Nicht alle Apps akzeptieren Freigabe, und wer akzeptiert einige von ihnen akzeptieren Links einige von ihnen nummeriert einige von ihnen Bilder. Es ist durcheinander. Ihre einzige Option ist, http://stackoverflow.com/questions/13498459/how-to-display-the-default-ios-6-share-action-sheet-with-available-share-options –

+0

Aber das OP wollte seine Bewerbung um die Aktie zu akzeptieren, nicht umgekehrt. Er definiert die Regeln, die die Daten bestimmen, die er akzeptiert, und dann kann jede App, die UIActivityViewController präsentiert, seine App als Ziel für die Freigabe darstellen, wenn der richtige Datentyp ausgewählt ist. –

0
  1. Wenn Sie nicht zeigen Dialog Standard von Apple wollen. Erben von UIViewController nicht @ Schnittstelle ShareViewController: SLComposeServiceViewController
  2. In Apple-Entwickler-Dokument, nicht zulassen, dass Erweiterungen App direkt mit App mit Ausnahme der heutigen Erweiterung App geöffnet.
+0

Es gibt jetzt Beispiele, wo andere Apps share extensions auf diese Weise verwenden. Zum Beispiel Pixelmator eine Aktie Aktion verwendet hatte, um ein Bild mit Snapseed, der Anteil Aktion zu teilen keine UI und openURL auf der Extension aufgerufen, dass die Aktie in einer ähnlichen Art und Weise zu bewirken, für eine heute Erweiterung gezeigt.Ein weiteres Beispiel, aus Fotos, können Sie viele Bilder als PDF in iBooks teilen. Wenn Sie ein paar Bilder auswählen, wird keine Benutzeroberfläche angezeigt. Wenn es viele kleine UIView-Controller gibt, die eine Fortschrittsleiste anzeigen, wird iBooks mit der in der Bibliothek angezeigten PDF geöffnet. –