0

I'm using this function below under a button to relaunch the application.

func relaunchApp() {    
let url = URL(fileURLWithPath: Bundle.main.resourcePath!)
let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString
let task = Process()
task.launchPath = "/usr/bin/open"
task.arguments = [path]
task.launch()
exit(0) }

This works perfectly on my Macbook Air 2015 model. However, when I send this file to anybody else with the exact same model (or a Mac Mini I've also tried this on), nothing happens after the button is pressed. This is under both conditions of being completely compiled and just doing a run from Xcode, either work on my side. How can I solve this issue?

Dylan
  • 674
  • 2
  • 8
  • 27
  • `.absoluteString` is *not* the right method to create a path from an URL, it should be `.path`. Compare http://stackoverflow.com/questions/34135305/nsfilemanager-defaultmanager-fileexistsatpath-returns-false-instead-of-true. – Martin R Dec 07 '16 at 18:54
  • Various security blocks too, depending on the OS version, security settings for the user, and whether the app/download has been started/opened before. – dfd Dec 07 '16 at 18:58
  • Function still worked on my side, but Airdropped this to the Mac Mini (macOS 10.11.6) and still nothing. @MartinR – Dylan Dec 07 '16 at 19:00
  • Checked permissions on `/usr/bin/open`, and they were the exact same for each other. @dfd. Both are on same versions of macOS. – Dylan Dec 07 '16 at 19:05
  • I'm not too involved with macOS (owned things since 1984 though) - but logic says (to me) that if a build works on one Mac but not the other, look into it. What if you **physically** installed the app on that MacMini? – dfd Dec 07 '16 at 19:10
  • The app isn't installed into the applications folder on here either, just in my Documents folder. I don't have those permissions. @dfd – Dylan Dec 07 '16 at 19:13
  • Yes, but **how** is it installed? Via Xcode? or the HTML link? If the latter then I'm out of thoughts. Good luck, and please post an answer when you find it! – dfd Dec 07 '16 at 19:15
  • I airdropped it to the other device. It's just an application sitting in my documents folder. I'll be bountying this when i can, it's a bit important. @dfd – Dylan Dec 07 '16 at 19:16

2 Answers2

1

Maybe it's a timing issue... if the open command runs before your app is fully gone, open may think it has nothing to do. Anyway, your approach of using open seems unnecessarily complicated. Try this (pardon my Objective-C, I don't know Swift):

NSError* err = nil;
[[NSWorkspace sharedWorkspace]
    launchApplicationAtURL: [[NSBundle mainBundle] bundleURL]]
    options: NSWorkspaceLaunchAsync | NSWorkspaceLaunchNewInstance
    configuration: nil
    error: &err ];
[NSApp terminate: NSApp];
JWWalker
  • 21,318
  • 5
  • 54
  • 72
  • eh, i can't really convert the async and new instance part. – Dylan Dec 08 '16 at 13:56
  • I'll try adding a `dispatchQueue` for some set times and see which (if any) work. – Dylan Dec 08 '16 at 13:59
  • What do you mean you can't convert the async and new instance part? Anything that can be done in Objective-C can be done in Swift, no? Or if you want to use `open`, you could try adding the `-n` flag. – JWWalker Dec 08 '16 at 15:23
  • I'm saying I can't convert this. I understand that anything possible in Swift is possible in Obj-C, I just don't know how to write this out in Swift. @JWWalker – Dylan Dec 09 '16 at 16:40
  • Could my issue be due to the fact that there isn't a `/` in front of the resourcepath url? The output while in Xcode says 'Launch path not accessible', and a related issue under that name defines that the process is at the same level, requiring that extra /. @JWWalker – Dylan Dec 09 '16 at 17:28
  • I don't know why there wouldn't be a slash at the start of `resourcePath`, because the docs say it returns a full pathname. But instead of getting that and then stripping 2 path components from it, why not use `bundleURL` or `bundlePath`? – JWWalker Dec 09 '16 at 17:43
  • This actually did solve my issue, I'll drop the answer. – Dylan Dec 09 '16 at 17:45
0

I was able to solve this by adding a / to the beginning of the resourcepath and using path rather than absoluteString.

func relaunchApp() {
     let fullUrl = "/" + Bundle.main.resourcePath!
     let url = URL(fileURLWithPath: fullUrl)
     let path = url.deletingLastPathComponent().deletingLastPathComponent().path
     let task = Process()
     task.launchPath = "/usr/bin/open"
     task.arguments = [path]
     task.launch()
     exit(0) }

I discovered this issue when another function I'm using that directly opens a file /System/Library/PreferencePanes/Bluetooth.prefPane, which while in Xcode outputted the following:

2016-12-09 11:51:56.749 Application[90077:1633677] launch path not accessible

Now, this function does work outside of Xcode but not within, and upon research of this error, I found this answer. Simply adding a forward slash and using the path rather than the absoluteString fixed the problem.

I'd like to thank @MartinR for mentioning the difference between path and absoluteString, @dfd, and @JWWalker for the help they provided.

Community
  • 1
  • 1
Dylan
  • 674
  • 2
  • 8
  • 27