Document based app Share menu

I have a document based app that I have been working on for more than a few years. Just recently, as I was doing a lot of updating for Mojave, a "Share" menu with sub items appeared in the File menu. It looks interesting and seems to have some cool stuff in it, however, some of the sub menu items are not relevant for my app or will cause problems. How do I remove the Share menu and all its items? I found a way to do it but it seems like a sloppy hack--I created a menu called Share, put it in the File menu, left it enabled but hidden. 🙂 It seems if the appkit finds a Share menu already there, it won't add its own. Is there a better way? Is there an entry I can put in the info.plist to tell it no, don't do that?


macOS 10.14.4, Xcode 10.2, macOS app.


Thank you!


Jana

Replies

Interesting. I hadn't noticed that before you mentioned it. Take a look at the shareDocumentWithSharingService:completionHandler: and https://developer.apple.com/documentation/appkit/nsdocument/2902326-preparesharingservicepicker?language=objcprepareSharingServicePicker: methods. They should do what you want. Unfortunately, they are only a couple of years old and, as is typical for Apple, completely undocumented. I suspect they are very similar to the standard share picker that you would use in the toolbar. That service is a few years old and does have some documentation. Just to warn you though - it is a relatively new control and, as is typical for Apple, works differently than any previous controls. It requires a special mouseDown event hook to work and will scold you from the console if you don't do it right.

Hi John,

Thank you for your response. This has pointed me in a direction.

First set the document as the delegate for the NSSharingServicePicker

override func prepare(_ sharingServicePicker: NSSharingServicePicker)
{
  sharingServicePicker.delegate = self
}


and then you can use the delegate functions. For example:


func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], prosedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService]
  {
    let emailService: NSSharingService? = NSSharingService(named: NSSharingService.Name.composeEmail)
    let airDropService: NSSharingService? = NSSharingService(named: NSSharingService.Name.sendViaAirDrop)
    let messageService: NSSharingService? = NSSharingService(named: NSSharingService.Name.composeMessage)
    var cloudSharingService: NSSharingService? = nil
    if #available(OSX 10.12, *)
    {
      cloudSharingService = NSSharingService(named: NSSharingService.Name.cloudSharing)
    }
    
    // only selected the ones that are available (airdrop is only available when wifi is supported,
    // email when an email-client is installed)
    return [emailService, messageService, airDropService, cloudSharingService].compactMap{$0}
  }

I think the OP's app pre-dates Swift.

Thank you John and Darkwing for your help. John is correct in stating I am not a swifty.


Although the code posted by Darkwing is mostly gibberish to me, it did give me the clues I needed. Here are my results so far:


// set myself as the delegate

-(void)prepareSharingServicePicker:(NSSharingServicePicker *)sharingServicePicker {

[sharingServicePicker setDelegate:self];

}



-(NSArray<NSSharingService *> *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker

sharingServicesForItems:(NSArray *)items

proposedSharingServices:(NSArray<NSSharingService *> *)proposedServices {

NSLog(@"proposed: %@",proposedServices);

/*

// This is the dump from the above NSLog

"<NSSharingService: 0x600000005ef0> [com.apple.share.Mail.compose] - enabled:YES",

"<NSSharingService: 0x600000005ed0> [com.apple.messages.ShareExtension] - enabled:YES",

"<NSSharingService: 0x600000005f00> [com.apple.share.AirDrop.send] - enabled:YES",

"<_NSCloudSharingService: 0x600002932d80> [com.apple.share.CloudSharing] - enabled:YES",

"<NSSharingService: 0x600000005ee0> [com.apple.Notes.SharingExtension] - enabled:YES"

*/

NSArray *myItems = [NSArray arrayWithObject:[proposedServices objectAtIndex:0]];

// as a hack to test it, I just put the mail service into the new array (myItems) and returned it.

// sure enough, the only thing that shows up in the share menu is the mail service. If I decide

// to use code like this, I would want to do a search in the array to make sure that the

// mail service is the object at index 0 since the order of them could be arbitrary.

// if you want to add something to the service menu, this code below will do it. You would want

// the array to include the above items and then add the custom service to it. This works

// even with the nil for the images although I get a warning. One would want real images.

// NSSharingService *customService = [[NSSharingService alloc] initWithTitle:@"Service Title"

// image:nil alternateImage:nil handler:^{ [self doCustomServiceWithItems:items]; }];


return myItems;

}


-(void)doCustomServiceWithItems:(NSArray *)items {

NSLog(@"do something");

}

I found the answer so I thought I would post it.


I was mucking around in the NSDocumentController and found that if I override:


-(BOOL)allowsAutomaticShareMenu {

return NO;

}


then the Share menu is no longer available (which I what I wanted).