This is a continuation of this question I asked on Ask Different.
I am trying get the list of recently opened documents in Pages. I found the file at ~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.apple.iwork.pages.sfl2
and successfully used plutil -convert xml1
to convert it to an xml plist file. Thanks to @Graham Miln I know that the file is encoded with an NSCoder
.
I know absolutely nothing about Objective-C and am looking for a way to decode the file within terminal. I am decoding this file for a raycast extension which will be written in JavaScript. Therefore, I am either looking for a JavaScript solution or a terminal command that I can run(because it is really easy to run terminal commands in the js code).
Below is a Swift script that decodes any sfl12
file, and prints each recent file, one file per line.
Save it and either execute with with the swift /path/to/saved/file </path/to/sfl12/file>
, or make the file executable and directly execute it.
The script expects the path to the sfl12
file as first argument.
Note that you need to install the Xcode Command Line Tools in order to be able to execute swift scripts.
#!/usr/bin/swift
import Foundation
do {
guard CommandLine.arguments.count > 1 else {
throw "Please provide the path to the file"
}
let data = try Data(contentsOf: URL(filePath: CommandLine.arguments[1]))
guard let root = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self],
from: data) as? [String: Any] else {
throw "Invalid top level object, expected a dictionary"
}
guard let items = root["items"] as? [[String: Any]] else {
throw "Invalid items property, expected an array of dictionaries"
}
for item in items {
var stale = false
if let bookmarkData = item["Bookmark"] as? Data,
let url = try? URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &stale) {
print(url.path)
}
}
} catch {
print(error.localizedDescription)
exit(-1)
}
extension String: LocalizedError {
public var errorDescription: String? { self }
}
Sample output:
> swift ~/Downloads/recent-files.swift "/Users/<myusername>/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.apple.iwork.pages.sfl2"
/Users/<myusername>/Documents/test2.pages
/Users/<myusername>/Desktop/test.pages