I’m building an app which needs to load Objective C files.
If the user gives me access to the ‘.m’ file, I need to be able to open the ‘.h’ file.
You don’t get this ability without a bit of a dance. I didn’t find any clear tutorial or example, so I’m adding one now.
Firstly – define the related filetypes that you want to access by adding the file extension to document types in your info.plist
Here, I have added h as an an extension type, and set NSIsRelatedItemType to true.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeExtensions</key> <array> <string>h</string> </array> <key>CFBundleTypeName</key> <string>Header File</string> <key>CFBundleTypeRole</key> <string>None</string> <key>NSIsRelatedItemType</key> <true/> </dict> </array> </dict> </plist>
Now, you can access the file – but only by using a FilePresenter.
And the file presenter _must_ be registered with NSFileCoordinator
class RelatedFilePresenter: NSObject, NSFilePresenter { var primaryPresentedItemURL: URL? var presentedItemURL: URL? var presentedItemOperationQueue: OperationQueue = OperationQueue.main enum Fail:Error { case unableToConstrucURL } init(primaryItemURL: URL) { self.primaryPresentedItemURL = primaryItemURL } func readDataFromRelated(fileExtension:String, completion:(Result<Data, Error>)->Void) { presentedItemURL = primaryPresentedItemURL?.deletingPathExtension().appendingPathExtension(fileExtension) guard let url = presentedItemURL else { completion(.failure(Fail.unableToConstrucURL)) return } let fc = NSFileCoordinator(filePresenter: self) var error:NSError? NSFileCoordinator.addFilePresenter(self) fc.coordinate(readingItemAt: url, options: .withoutChanges, error: &error) { (url) in do { let data = try Data(contentsOf: url, options: .uncached) completion(.success(data)) } catch { completion(.failure(error)) } } if let error { completion(.failure(error)) } NSFileCoordinator.removeFilePresenter(self) } }
This class provides a method to read the data from a related file, but you could adapt it for any other operation.
You can then read the data with something like
let filePresenter = RelatedFilePresenter(primaryItemURL: mFileURL) filePresenter.readDataFromRelated(fileExtension: "h") { result in if case .success(let data) = result { //do something with data } }