// // HPShareExtensionHelper.m // HotPocket // // Created by Tomek Wójcik on 27/09/2025. // #import #import "HPShareExtensionHelper.h" #import "HPSharedItem.h" #import "HPSharedItemsContainer.h" @implementation HPShareExtensionHelper (HPShareExtensionHelperPrivate) @end @implementation HPShareExtensionHelper -(instancetype)initWithContext:(NSExtensionContext *)context { if (self = [super init]) { self.context = context; self.items = [[HPSharedItemsContainer alloc] init]; } return self; } -(void)processItems:(HPShareExtensionHelperHandleItemsCompletionHandler)completionHandler { // Depending on the app, the URL might be stored in `public.url` attachment or elsewhere. // For example, the YouTube app passes it in `public.plain-text`. Because of course it does. // Furthermore, for some bizarre reason the recommended way of extracting the URL when sharing from a browser // is to run a JS snippet and examine its output. // This method will iterate through all the shared items and their attachments and attempt to extract // the URL candidates. // // Also note that handler for `public.url` explicitly requests the payload to be corced to `NSURL *`. Leaving it // at `NSData *` would cause iOS to, wait for it!, fetch the URL and dump the response body in the payload :D. // // This is so _so_ *so* dumb. But hey, at least I learned how to to "chords" in CGD ¯\_(ツ)_/¯ UTType *propertyListType = [UTType typeWithFilenameExtension:@"plist"]; dispatch_group_t dispatchGroup = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("HPShareExtensionHelper.processItems.queue", DISPATCH_QUEUE_SERIAL); for (NSExtensionItem *inputItem in self.context.inputItems) { #ifdef DEBUG NSLog(@"-[HPShareExtensionHelper processItems:] inputItem.userInfo=`%@`", inputItem); #endif [inputItem.attachments enumerateObjectsUsingBlock:^(NSItemProvider *attachment, NSUInteger index, BOOL *stop) { dispatch_group_enter(dispatchGroup); if ([attachment hasItemConformingToTypeIdentifier:propertyListType.identifier] == YES) { [attachment loadItemForTypeIdentifier:propertyListType.identifier options:nil completionHandler:^(NSDictionary *payload, NSError *error) { dispatch_async(queue, ^{ self.items.primaryItem = [[HPSharedItem alloc] initWithPayload:payload typeIdentifier:propertyListType.identifier error:error]; dispatch_group_leave(dispatchGroup); }); }]; } else if ([attachment hasItemConformingToTypeIdentifier:@"public.url"] == YES) { [attachment loadItemForTypeIdentifier:@"public.url" options:nil completionHandler:^(NSURL *payload, NSError *error) { dispatch_async(queue, ^{ [self.items.candidateItems addObject:[[HPSharedItem alloc] initWithPayload:payload typeIdentifier:@"public.url" error:error]]; dispatch_group_leave(dispatchGroup); }); }]; } else if ([attachment hasItemConformingToTypeIdentifier:@"public.plain-text"] == YES) { [attachment loadItemForTypeIdentifier:@"public.plain-text" options:nil completionHandler:^(NSString *payload, NSError *error) { dispatch_async(queue, ^{ [self.items.candidateItems addObject:[[HPSharedItem alloc] initWithPayload:payload typeIdentifier:@"public.plain-text" error:error]]; dispatch_group_leave(dispatchGroup); }); }]; } else { dispatch_group_leave(dispatchGroup); } }]; dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ NSURL *result = [self.items resolveURL]; completionHandler(result); }); } } @end