2011-12-12 15 views
7

¿Cómo envío un archivo a una aplicación diferente sabiendo qué UTI es compatible con la aplicación? Digamos que el archivo no tiene extensión de archivo, pero conozco la UTI del archivo.UIDocumentInteractionController, no hay extensión de archivo pero UTI

He intentado lo siguiente:

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

Se muestra la aplicación compatible, sin embargo, si lo selecciono, no va a cambiar de la aplicación. El delegado llama a la

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

pero

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

Nunca se llama y la aplicación nunca está cambiando.

El objetivo App exporta su infección urinaria en lo siguiente:

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

Como esto no funcionó, también he intentado añadir una extensión personalizada. Aún así, no funcionaría de esta manera. Al agregar la extensión personalizada al archivo entrego al DocumentInteractionController y funciona. Sin embargo, la lista de aplicaciones muestra todas las demás aplicaciones que admiten la misma extensión de archivo, independientemente del tipo de UTI que suministre.

Di Declaro 2 IU en 2 aplicaciones diferentes:

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

Al entregar el archivo a la DocumentInteractionController y ajuste de la UTI a com.mycomp.a también se mostrará App2 como una posible aplicación de ser capaz de manejar el archivo .

que define una UTI con la extensión de la siguiente manera:

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

Realmente agradecería su ayuda, estoy un poco atascado. Entonces, de nuevo la pregunta: ¿cómo envío un archivo a una aplicación con UTI conocida sin extensión o con la misma extensión que otros archivos de los que no deseo mostrar las aplicaciones como opción en DocumentInteractionController?

Gracias

Respuesta

2

He encontrado una solución a este problema. Sin embargo, creo que no es muy bueno.

Durante la prueba descubrí que al dejar la extensión del archivo, el UIDocumentInteractionController mostrará las aplicaciones según el UTI que especifiqué. Al enviar el archivo a la aplicación de destino, no pasaría nada. Concluí que necesito una extensión de archivo para hacer el envío final.

Mi enfoque era modificar la propiedad URL antes de enviar el archivo a la aplicación de destino y proporcionarle el mismo archivo pero con una extensión de archivo que acepta la aplicación de destino. Sin embargo, mi aplicación simplemente se colgó. Lo rediseñé con Instruments y descubrí que el problema se debía a que UIDocumentInteractionController excedía algún objeto proxy. También vi que el sobrerrelleno final estaba en un método llamado _invalidate de UIDocumentInteractionController(Private) categoría que liberaba el objeto. Como las categorías no pueden ser reemplazadas por otras categorías decidí cambiar el método de categoría con mi propia verificación de implementación si la URL contenía una extensión de archivo y redirigir la llamada al método original _invalidate o simplemente no hacer nada.

Los siguientes códigos muestra lo que hice:

#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 

Este código intercambios el original _invalidate método con _invalidateMY, lo que resulta en cada llamada a _invalidate llamando _invalidateMY y viceversa.

El código siguiente muestra cómo manejo el 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]; 

Y este código muestra el método delegado 's la UIDocumentInteractionController que intercambia la 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]); 
} 

Esta solución funciona, pero en mi opinión es un truco sucio, debe haber una mejor solución.

+0

Encontré una solución más simple. El código de 'willBeginSendingToApplication:' ya se puede ejecutar después de que se haya llamado con éxito al menú Abrir en, ¡esto borra la necesidad de cambiar los métodos! – Jan

+2

Proporcione el código para esto. He intentado configurar el nombre de la propiedad, pero no se refleja en la aplicación de terceros. – slott

+0

Sí, una descripción detallada de la solución * real * sería de gran ayuda. – buildsucceeded

Cuestiones relacionadas