UIDocumentInteractionController, нет расширения файла, но UTI - PullRequest
7 голосов
/ 12 декабря 2011

Как отправить файл в другое приложение, зная, какой UTI поддерживает приложение?Допустим, файл не имеет расширения, но я случайно узнал UTI этого файла.

Я попробовал следующее:

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

Показывает поддерживаемое приложение, однако, если явыберите его, он не будет переключать приложение.Делегат вызывает

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

, но

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

никогда не вызывается, и приложение никогда не переключается.

Целевое приложение экспортирует свой UTI следующим образом:

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

Поскольку это не помогло, я также попытался добавить собственное расширение.Тем не менее, это не будет работать таким образом.При добавлении пользовательского расширения в файл я передаю DocumentInteractionController, и оно работает.Однако в списке приложений показаны все другие приложения, поддерживающие одно и то же расширение файла, независимо от типа UTI, поставляемого I.

Скажем, я объявляю 2 UTI в 2 разных приложениях:

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

При передачефайл для DocumentInteractionController, и установка UTI на com.mycomp.a также покажет App2 как возможное приложение, способное обработать файл.

Я определил UTI с расширением следующим образом:

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

Буду очень признателен за вашу помощь, я застрял.Итак, еще раз вопрос: как я могу отправить файл в приложение с известным UTI или без расширения или с тем же расширением, что и другие файлы, для которых я не хочу показывать приложения в качестве выбора в DocumentInteractionController?

Спасибо

1 Ответ

2 голосов
/ 13 декабря 2011

Я нашел решение этой проблемы.Тем не менее, я думаю, что это не очень хорошо.

Во время тестирования я обнаружил, что при удалении расширения файла, UIDocumentInteractionController будет показывать приложения в зависимости от UTI, который я указал.При отправке файла в целевое приложение ничего не произойдет.Я пришел к выводу, что для окончательной отправки мне нужно расширение файла.

Мой подход заключался в изменении свойства URL перед отправкой файла в целевое приложение и предоставлении ему того же файла, но с расширением файла.целевое приложение принимает.Тем не менее, мое приложение просто упало.Я профилировал его с помощью Instruments и обнаружил, что проблема была в UIDocumentInteractionController перевыпуске некоторого прокси-объекта.Я также увидел, что окончательный перевыпуск был вызван методом _invalidate из категории UIDocumentInteractionController(Private), который освободил объект.

Поскольку категории не могут быть переопределены другими категориями, я решил изменить метод категории с помощью моегособственная реализация, проверяющая, содержит ли URL расширение файла, или перенаправляет вызов на оригинальный метод _invalidate или просто ничего не делает.

В следующих кодах показано, что я сделал:

#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

Этот код заменяет оригинальный метод _invalidate на _invalidateMY, в результате каждый вызов _invalidate вызывает _invalidateMY ии наоборот.

Следующий код показывает, как я обрабатываю 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];

И этот код показывает метод делегата UIDocumentInteractionController, который обменивается 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]);
}

Это решение работает, но, на мой взгляд, это грязный взлом, должно быть лучшее решение.

...