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
}
}
