2011-12-12 8 views
7

Come si invia un file a un'altra app sapendo quale UTI l'app supporta? Diciamo che il file non ha estensione di file, ma capita di conoscere l'UTI del file.UIDocumentInteractionController, nessuna estensione file ma UTI

Ho provato il seguente:

// target is a NSURL with the location of the extension less file on the system 
// knownUTI is a NSString containing the UTI of the file 
    UIDocumentInteractionController* dic = [UIDocumentInteractionController interactionControllerWithURL:target]; 
    [dic retain]; 

    dic.delegate = self; 
    dic.UTI = knownUTI; 
    [dic presentOpenInMenuFromRect:CGRectZero inView:superController.view animated:YES] 

Essa mostra App supportato, tuttavia, se seleziono esso, non passerà App. Il delegato chiama il

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application 

ma

- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application 

non è mai chiamato e l'applicazione non è mai commutazione.

L'obiettivo App esporta la sua UTI nel seguente:

<key>CFBundleDocumentTypes</key> 
    <array> 
     <dict> 
      <key>CFBundleTypeIconFiles</key> 
      <array/> 
      <key>CFBundleTypeName</key> 
      <string>Migration DocType</string> 
      <key>CFBundleTypeRol</key> 
      <string>Shell</string> 
      <key>LSHandlerRank</key> 
      <string>Owner</string> 
      <key>LSItemContentTypes</key> 
      <array> 
       <string>com.mycomp.customstring</string> 
      </array> 
     </dict> 
    </array> 

... 

<key>UTExportedTypeDeclarations</key> 
    <array> 
     <dict> 
      <key>UTTypeConformsTo</key> 
      <array> 
       <string>public.data</string> 
      </array> 
      <key>UTTypeDescription</key> 
      <string>My custom UTI</string> 
      <key>UTTypeIdentifier</key> 
      <string>com.mycomp.customstring</string> 
     </dict> 
    </array> 

Dato che questo non ha funzionato, ho anche provato ad aggiungere un'estensione personalizzata. Tuttavia, non funzionerebbe in questo modo. Quando aggiungo l'estensione personalizzata al file, lo consegno allo DocumentInteractionController e funziona. Tuttavia, l'elenco delle applicazioni mostra tutte le altre applicazioni che supportano la stessa estensione di file indipendentemente dal tipo di UTI di tipo I.

dire che mi dichiaro 2 IVU in 2 diverse applicazioni:

App1 with UTI1: com.mycomp.a with extension .abc 
App2 with UTI2: com.mycomp.b with extension .abc 

Al momento della consegna il file sul DocumentInteractionController, e l'impostazione del UTI per com.mycomp.a mostrerà anche App2 come una possibile applicazione in grado di gestire il file .

ho definito un UTI con l'estensione nel modo seguente:

<key>UTExportedTypeDeclarations</key> 
    <array> 
     <dict> 
      <key>UTTypeConformsTo</key> 
      <array> 
       <string>public.data</string> 
      </array> 
      <key>UTTypeDescription</key> 
      <string>My UTI Type</string> 
      <key>UTTypeIdentifier</key> 
      <string>com.mycomp.a</string> 
      <key>UTTypeTagSpecification</key> 
      <dict> 
       <key>public.filename-extension</key> 
       <string>abc</string> 
       <key>public.mime-type</key> 
       <string>application/abc</string> 
      </dict> 
     </dict> 
    </array> 

Vorrei davvero apprezzare il vostro aiuto, io sono un po 'bloccato. Quindi, ancora una volta la domanda: come posso inviare un file a un'app con UTI noto o senza estensione o con la stessa estensione di altri file di cui non voglio mostrare le applicazioni come scelte in DocumentInteractionController?

Grazie

risposta

2

ho trovato una soluzione a questo problema. Tuttavia, penso che non sia molto buono.

Durante il test, ho scoperto che quando si lascia l'estensione del file, lo UIDocumentInteractionController mostrerà le applicazioni a seconda dello UTI specificato. Quando si invia il file all'applicazione di destinazione, non accadrebbe nulla. Ho concluso che ho bisogno di un'estensione di file per l'invio finale.

Il mio approccio era modificare la proprietà URL prima che il file venga inviato all'applicazione di destinazione e fornire lo stesso file ma con un'estensione di file accettata dall'applicazione di destinazione. Tuttavia, la mia applicazione si è appena bloccata. L'ho analizzato con gli strumenti e ho scoperto che il problema era dovuto a UIDocumentInteractionController che sovrascriveva alcuni oggetti proxy. Ho anche visto che la versione finale in eccesso era in un metodo chiamato _invalidate della classe UIDocumentInteractionController(Private) che ha rilasciato l'oggetto.

Poiché le categorie non possono essere sostituite da altre categorie, ho deciso di convertire il metodo di categoria con la mia implementazione, verificando se l'URL contenesse o meno un'estensione del file e reindirizzando la chiamata al metodo originale _invalidate o semplicemente non intervenendo.

I seguenti codici mostra cosa ho fatto:

#include <objc/runtime.h> 

@interface UIDocumentInteractionController(InvalidationRedirect) 

-(void)_invalidateMY; 
+(void)load; 
void Swizzle(Class c, SEL orig, SEL newSEL); 
@end 

@implementation UIDocumentInteractionController(InvalidationRedirect) 

void Swizzle(Class c, SEL orig, SEL newSEL) 
{ 
    Method origMethod = class_getInstanceMethod(c, orig); 
    Method newMethod = class_getInstanceMethod(c, newSEL); 
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) 
     class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); 
    else 
     method_exchangeImplementations(origMethod, newMethod); 
} 

-(void)_invalidateMY{ 
    @synchronized(self) { 
     if(![[[[self.URL lastPathComponent] componentsSeparatedByString:@"."] lastObject] isEqualToString:@"extension"]) { 
      [self _invalidateMY]; 
     } 
    } 
} 

+(void)load 
{ 
    Swizzle([UIDocumentInteractionController class], @selector(_invalidate), @selector(_invalidateMY)); 
} 

@end 

Questo codice scambi il metodo originale _invalidate con _invalidateMY, causando ogni chiamata a _invalidate chiamata _invalidateMY e viceversa.

Il codice seguente mostra come gestire il UIDocumentInteractionController:

// create a file without extension 
    NSString *fileName = @"myFile"; 

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

    NSString *documentsDirectory = [paths objectAtIndex:0]; 

    NSURL* target = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", documentsDirectory, fileName]]; 

    if([[@"THIS IS JUST A TEST STRING" dataUsingEncoding:NSUTF8StringEncoding] writeToURL:target atomically:NO]) { 
     NSLog(@"file written successfully"); 
    }else { 
     NSLog(@"Error.. file writing failed"); 
    } 

    UIDocumentInteractionController* dic = [UIDocumentInteractionController interactionControllerWithURL:target]; 
    [dic retain]; 
    dic.delegate = self; 


    // set the UTI to the known UTI we want to list applications for 
    dic.UTI = @"com.mycomp.a"; 

    [dic presentOpenInMenuFromRect:CGRectZero inView:superController.view animated:YES]; 

E questo codice mostra metodo delegato il UIDocumentInteractionController s', che scambia l'URL:

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application 
{ 
    NSFileManager *fileMgr = [NSFileManager defaultManager]; 
    NSError *error; 
    NSURL* newTarget = [NSURL URLWithString:[NSString stringWithFormat:@"%@.extension", controller.URL]]; 
    // rename file to file with extension 
    if (![fileMgr moveItemAtURL:controller.URL toURL:newTarget error:&error] && error) { 
     NSLog(@"Error moving file: %@", [error localizedDescription]); 
    } 
    @synchronized(controller) { 
     //exchange URL with URL+extension 
     controller.URL = newTarget; //<- this results in calling _invalidate 
    } 
    NSLog(@"%@", [NSString stringWithContentsOfURL:controller.URL encoding:NSUTF8StringEncoding error:nil]); 
} 

Questa soluzione funziona, ma nella mia opinione è un trucco sporco, ci deve essere una soluzione migliore.

+0

Ho trovato una soluzione più semplice. Il codice di 'willBeginSendingToApplication:' può essere già eseguito dopo che l'apertura nel menu è stata richiamata con successo, questo elimina la necessità di swizzle i metodi! – Jan

+2

Si prega di fornire il codice per questo. Ho provato a impostare la proprietà name ma non si riflette nell'app di terze parti. – slott

+0

Sì, una descrizione dettagliata della soluzione * reale * sarebbe molto utile. – buildsucceeded

Problemi correlati