Swift - Get file path of currently opened document in another application
Thanks to the help of another post from @JamesWaldrop, I was able to answer this myself and wanted to post here for anyone looking for something similar:
let proToolsBundleIdentifier = "com.avid.ProTools"
let proToolsApp : NSRunningApplication? = NSRunningApplication
.runningApplications(withBundleIdentifier: proToolsBundleIdentifier).last as NSRunningApplication?
if let pid = proToolsApp?.processIdentifier {
var result = [AXUIElement]()
var windowList: AnyObject? = nil // [AXUIElement]
let appRef = AXUIElementCreateApplication(pid)
if AXUIElementCopyAttributeValue(appRef, "AXWindows" as CFString, &windowList) == .success {
result = windowList as! [AXUIElement]
}
var docRef: AnyObject? = nil
if AXUIElementCopyAttributeValue(result.first!, "AXDocument" as CFString, &docRef) == .success {
let result = docRef as! AXUIElement
print("Found Document: \(result)")
let filePath = result as! String
print(filePath)
}
}
This gets the AXDocument just like the AppleScript does. Would still be open to other methods of doing this that may be better or not using Accessibility.
How to get file paths for current open documents in a macOS project in Swift?
You can get the documents from NSDocumentController
and the URL from each document.
for document in NSDocumentController.shared().documents {
print("\(document.fileURL)")
}
How to get file paths for current open documents in a macOS project in Swift?
You can get the documents from NSDocumentController
and the URL from each document.
for document in NSDocumentController.shared().documents {
print("\(document.fileURL)")
}
iOS file path is changing at every launch/rerun the application
Edit 1:
Hi I created the new project and use the same code I posted in main, and it's working. But in the real project it not working.
Not sure what exactly going on in your project, try to debug it. It's part of development as well. :)
If you are in hurry to fix this issue in this weekend try to use the following code snippet.
// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)
// try to write data in document directory
do {
let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
// use your `constFileURL`
} catch (let error as FileOperationError) {
switch error {
case .fileAlreadyExists(let url):
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8))
case .IOError(let error):
print("IO Error \(error)")
}
} catch {
print("Unknown Error \(error)")
}
// Helpers
enum FileOperationError: Error {
case fileAlreadyExists(url: URL)
case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
// final destination path
let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
// check for file's existance and throw error if found
guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
throw FileOperationError.fileAlreadyExists(url: destURLPath)
}
// Create Intermidiate Folders
let intermidiateDicPath = destURLPath.deletingLastPathComponent()
if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
do {
try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
} catch {
throw FileOperationError.IOError(error)
}
}
// File Writing
do {
try data.write(to: destURLPath, options: .atomic)
} catch {
throw FileOperationError.IOError(error)
}
return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}
Original Answer
Why don't you just return "MyFolder/\(fileName)"
on successful file operation? If you need to access the path later you can always do that using FileManager
APIs.
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")
// Access const file data
do {
let fileData = try Data(contentsOf: constFilePath)
// Use you data for any further checking
} catch {
// Error in reading file data
print("Error in file data access : \(error)")
}
How to handle a file sent with 'Open in...' from another app to my own iOS app?
This is handled in your AppDelegate
, more precisely, you get passed an URL to the document and then you handle it from there in optional function, e.g.:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
do {
let data = try Data(contentsOf: url)
// Do something with the file
} catch {
print("Unable to load data: \(error)")
}
return true
}
More info: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application
Different path URL for FileManager everytime I open the app
You are using the wrong API. absoluteString
(in rootURL.absoluteString
) returns the string representation including the scheme file://
. The correct API for file system URLs is path
I recommend to use the URL related API as much as possible
public func directoryExists(at url: URL) -> Bool {
let fileManager = FileManager.default
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: url.path, isDirectory:&isDir) {
return isDir.boolValue
} else {
return false
}
}
and compose the URL in a more reliable way
func createARObjectDirectory() {
let rootURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if directoryExists(at: rootURL.appendingPathComponent(DefaultURL.arObjectUrlDirectoryName) {
Logger.logServer("ARObject directly found")
} else {
createNewDirectory(name: DefaultURL.arObjectUrlDirectoryName)
}
}
And this is swiftier too
public func createNewDirectory(name: String) {
let documentDirectory = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dirURL = documentDirectory.appendingPathComponent(name)
do
{
try FileManager.default.createDirectory(at: dirURL, withIntermediateDirectories: false, attributes: nil)
}
catch let error as NSError
{
Logger.logError("Unable to create directory \(error.debugDescription)")
}
Logger.logInfo("Dir Path = \(dirPath.path)")
}
Related Topics
Function That Takes a Protocol and a Conforming Class (!) Instance as Parameters
Swiftui: How to Make a Button Open a Url in Safari
Swift Protocol to Require Properties as Protocol
Playing a Sound in a Swift Playground
Swift Can't Infer Generic Type When Generic Type Is Being Passed Through a Parameter
Missing Argument for Parameter 'From' in Call When Creating Instance of Codable Class
How to Handle Two Different Types in an Array in Swift for a Uitableview
Swift, Sprite Kit Game: Have Circle Disappear in Clockwise Manner? on Timer
Swift & Firebase - How to Store More User Data Other Than Email and Password
Is There a Daylight Savings Check in Swift
Extend Generic Array<T> to Adopt Protocol
Using Applescript with Apple Events in MACos - Script Not Working
How to Implement iOServicematchingcallback in Swift
Instance Member Cannot Be Used on Type Class
Swift/Firebase - Sort Posts in Tableview by Date
Difference Between Nsrange and Nsmakerange
How to Shuffle an Array So That No Two Consecutive Values Are the Same