Using UIApplication openURL will open the file directly, but the file location corresponding to finder cannot be opened. There are many resource files in the folder, so it will be difficult to locate specific files if only the folder is opened
Apparently, it can only be done using NSWorkspace. Which is part of AppKit. Which can't be imported into the code compiled directly into your frameworks.
However...
I heard others say code importing AppKit can be included in a "plug-in", which can be added for only the Mac-os version of your app and can be loaded at run time.
So here's my code for this:
I create a plug-in called "AppKitCompatibility".
The plug-in bundle is listed in the General pane of the project editor of my maccatalyst app, with Platforms set to "macOS" and Embed set to "Embed & Sign".
It contains 2 obj-c files.:
WorkspaceCompatibility.h
WorkspaceCompatibility.m
The plug-in's info.plist lists WorkspaceCompatibility as the principal class. I don't think that's strictly necessary, as the swift code below loads it by name.
Then in the swift UIKit portion of the app :
I normally hate singletons, but in this case the MacCompatibility is held as a singleton for 3 reasons: 1) the bundle must remain in memory as long as the code will be run, 2) it can be accessed from anywhere just as the workspace was, and 3) it's so little code, surely it's not a problem.
I have tested this, and it works. But I have not shipped it. I have heard other people say using plug-ins is supported by the MacAppStore.
However...
I heard others say code importing AppKit can be included in a "plug-in", which can be added for only the Mac-os version of your app and can be loaded at run time.
So here's my code for this:
I create a plug-in called "AppKitCompatibility".
The plug-in bundle is listed in the General pane of the project editor of my maccatalyst app, with Platforms set to "macOS" and Embed set to "Embed & Sign".
It contains 2 obj-c files.:
WorkspaceCompatibility.h
Code Block #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface WorkspaceCompatibility : NSObject + (void)showInFinder:(NSArray<NSURL *> *)urls; @end NS_ASSUME_NONNULL_END
WorkspaceCompatibility.m
Code Block #import "WorkspaceCompatibility.h" #import <AppKit/AppKit.h> @implementation WorkspaceCompatibility + (void)showInFinder:(NSArray<NSURL *> *)urls { [NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:urls]; } @end
The plug-in's info.plist lists WorkspaceCompatibility as the principal class. I don't think that's strictly necessary, as the swift code below loads it by name.
Then in the swift UIKit portion of the app :
Code Block import Foundation class MacCompatibility { static let shared:MacCompatibility = MacCompatibility() private init() { } #if targetEnvironment(macCatalyst) //the classes loaded only live as long as the bundle instance, so the bundle must be retained for the duration of the app private lazy var bundle:Bundle? = newBundle() private func newBundle()->Bundle? { let bun = Bundle(path: Bundle.main.builtInPlugInsPath?.appending("/AppKitCompatibility.bundle") ?? "") bun?.load() return bun } #endif func revealUrlsInFinder(_ urls:[URL]) { #if targetEnvironment(macCatalyst) let files = urls as [NSURL] let fileArray = files as NSArray guard let macClass:AnyClass = bundle?.classNamed("WorkspaceCompatibility") ,let macCompatibility = macClass as AnyObject as? NSObjectProtocol //in Obj-c, classes are objects, too else { return } _ = macCompatibility.perform(NSSelectorFromString("showInFinder:"), with:fileArray) //so we can call a class method on the class #endif } }
I normally hate singletons, but in this case the MacCompatibility is held as a singleton for 3 reasons: 1) the bundle must remain in memory as long as the code will be run, 2) it can be accessed from anywhere just as the workspace was, and 3) it's so little code, surely it's not a problem.
I have tested this, and it works. But I have not shipped it. I have heard other people say using plug-ins is supported by the MacAppStore.