6

I've got a custom file UTI attachment almost working in my App when sharing as a message. It embeds the file as an attachment with the correct file type of 'kitlist' in the message and is received on both iPads and iPhones. If you tap on the attachment from the normal message display nothing happens. However, if you tap on the info button at the top right corner and then choose Attachment, tap on the file and share this, the App appears as a possible destination. Choosing it will open the file successfully in my App. At the moment, choosing Mail as the share destination doesn't cause the attachment to appear in the email message; I don't know if this is relevant.I've also tried using sub-classing UIActivityItemProvider rather than implementing the protocol UIActivityItemSource sub-classing NSObject but with no difference. Airdropping to another iOS device works fine.

Here is the relevant fragment of my plist:

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeIconFiles</key>
        <array/>
        <key>CFBundleTypeName</key>
        <string>KitList Transfer File Type</string>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>LSHandlerRank</key>
        <string>Owner</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>co.SM.KitListFile.kitlist</string>
        </array>
    </dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.data</string>
        </array>
        <key>UTTypeDescription</key>
        <string>KitList Transfer File</string>
        <key>UTTypeIdentifier</key>
        <string>co.SM.KitListFile.kitlist</string>
        <key>UTTypeSize320IconFile</key>
        <string>BergenBig</string>
        <key>UTTypeSize64IconFile</key>
        <string>Bergen</string>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>public.filename-extension</key>
            <string>kitlist</string>
        </dict>
    </dict>
</array>

The code to make the attachment is:

@interface SMCustomAttachmentTransfer()
@property (nonatomic, strong) NSMutableArray *images;

@end

@implementation SMCustomAttachmentTransfer

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    self.images =  [NSMutableArray array];
    [aCoder encodeObject:self.list forKey:@"list"];
    [self walkTheList:[self.list valueForKey:keySubItems] withCoder:aCoder];
    [aCoder encodeObject:self.images forKey:@"images"];
}

- (void)walkTheList:(NSDictionary *)items withCoder:(NSCoder *)aCoder
{
    for (NSDictionary *item in items)
    {
        if ( [item valueForKey:keyDetailImage] )
        {
            [self.images addObject:[item valueForKey:keyDetailImage]];
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *documentsDirectory = [paths objectAtIndex:0];
            NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:[item valueForKey:keyDetailImage]];
            [aCoder encodeObject:[NSData dataWithContentsOfFile:getImagePath] forKey:[item valueForKey:keyDetailImage]];
        }
        if ( [item valueForKey:keySubItems] )
        {
            [self walkTheList:[item valueForKey:keySubItems] withCoder:aCoder];
        }
    }
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self)
    {
        self.list = [aDecoder decodeObjectForKey:@"list"];
        NSArray *images = [aDecoder decodeObjectForKey:@"images"];
        for (NSString *image in images)
        {
            NSData *pngData = [aDecoder decodeObjectForKey:image];
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *documentsPath = [paths objectAtIndex:0];
            [pngData writeToFile:[documentsPath stringByAppendingPathComponent:image] atomically:YES];
        }
    }
    return self;
}

- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
    // must be data placeholder for attachment to be embedded in a message.
    return [NSData data];
}

- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType
{
    return [self.list valueForKey:keyName];
}

- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType
{
   return [NSKeyedArchiver archivedDataWithRootObject:self];
}

- (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType
{
    return @"co.SM.KitListFile.kitlist";
}
Nick
  • 4,820
  • 18
  • 31
  • 47

1 Answers1

5

Update: This is how opening a custom attachment now works in iOS 15.6.

Touch the profile icon for the conversation. This either a picture or initials at the top of the screen. Scroll down to the attachments section. Open the attachment from there.

enter image description here

KeithTheBiped
  • 832
  • 10
  • 21
  • Red on black (or dark colors) - just don't. – LinusGeffarth Jun 24 '17 at 17:56
  • When you first posted this answer, @KeithTheBiped, it didn't work for me. However, I've just added a stub 'Quick Look Preview Extension' to my app and now it does enable your answer to work. – Nick Apr 30 '19 at 21:19
  • It worked for me without the Quick Look Preview Extension, but to get to the info button on the latest iOS, you have to click the empty space where the old button was. That reveals the info/facetime/audio buttons. – Mike M Jul 08 '19 at 14:08