Creating a "Uninstaller" for a program on Mac OS - macos

I have a requirement to create a cross platform application that launches a web link to a feedback form when its uninstalled.
This is obviously normal sort of behaviour on windows..., but on a Mac is is proving to be more complicated as applications are not technically installed and uninstalled in a windows sense, aka you just copy the .app file into Applications and delete it when you're finished.
How can I achieve this website launching requirement? (Should I even be trying, is this process too alien to Mac users?)
I tired packing the application with an uninstall shell script that deletes the app and lunches the site, but obviously the script can't delete itself.

I don't think this is the best idea, since the process would be a bit unusual to OS X users. As you noted, most applications are installed simply by dragging a .app file to /Applications (or some other location). Some apps do have an installer, but even apps with an installer only occasionally have an uninstaller; and furthermore, as a Mac user, I'd be immediately suspicious of an app that installed itself and some sort of unknown shell script.

Mac OS applications should not need to be uninstalled in any way other than the user dragging them into the trash.
Also, I would rethink very carefully your plan to make a cross-platform applications. Cross-platform applications that treat Mac OS as an afterthought and try to push foreign paradigms onto Mac OS are really irritating. If you want a Mac client, keep your backend code, but rewrite the front-end from scratch. Don't use something like Qt, no matter how tempting the portability is.
So, long story short, you're right. The process is alien to Mac users (except for things like plugins). So my suggestion is just to go with the normal Mac OS behavior (drag to trash). Best of luck!

I would recommend against it. You could create an uninstaller but nothing is going to stop a user from just deleting it from the application folder or using something like AppZapper. Most people don't even look for an uninstaller application, they just trash the app, so even if you wrote one there would be no guarantee it'll be used.
I'd certainly avoid an uninstaller shell script, no way in the world I'd personally run it.

An uninstaller on a Mac makes no sense and would be awkward to implement, if you could even implement it at all in a way where people would use it.
Consider trying to get user feedback using alternate methods, such as:
Add a menu item that opens the feedback form
Require registration when the software is downloaded, then send an email to the user at some point in the future to ask for feedback
Ask for feedback occasionally on application quit (could be annoying, though)

I don't think that it's a good idea to ask for a feedback when the app is uninstalled. However, here is a good way to provide an uninstaller for a MACOS app in case it needs to do some clean up.

Related

How to generate an installer package for Mac app?

How can I create a single installer package for an OS X binary as well as a few configuration and script files?
Final folders should look like this:
Any help would be appreciated. Thanks.
Installers are great if you want various things to be placed in different spots – app here, documentation there, support files over here, etc. They're also great for providing configurability of the installation experience (optional extras), or hand-holding for an unusual type of installation that the user might not otherwise understand, or extra work (configuration scripts, permissions modifications, authentication, compatibility checking, etc.) that need to run during the installation process. There is nothing wrong with installers, contrary to the answer from #d00dle, although there is also nothing wrong with distributing your app through the App Store, or as a dmg.
For setting up your own installers, I highly recommend a program called Packages (http://s.sudre.free.fr/Software/Packages/about.html). I am in no way connected to it, but I use it to build the installer for an app that I work on. It greatly smoothes the process of making a complex installer, and has an excellent GUI interface.
There's also macOS Installer Builder, which is a CLI you can use to create an installer wizard for your .pkg: https://github.com/KosalaHerath/macos-installer-builder
macOS does not normally use installers. Applications are packaged in app containers with the extension .app. This container is "executable" but you're also able to dig in and see what is inside. This is also the format distributed through App Store.
You can create .pkg or .dmg "installers" if necessary, however this is clearly not something apple aims to be standard. I would advise to use the .app pattern and any scripts needed should be self contained and executed on first run.
You can use .dmg to distribute your application outside of App Store (this is still fairly normal).
macOS also includes a terminal program called productbuild that builds a product archive for the macOS Installer or the Mac App Store. Enter man productbuild into the Terminal on a Mac for the manual page.

Uninstalling items installed by an .app when user deletes it, including SMJobBless helpers

The short version: is it possible to delete helper tools which were set up by the app (SMJobBless() etc.) when the app is deleted? If so, how?
The long version:
The Mac app we are developing unfortunately requires admin privileges to perform an occasional operation, and it also requires a background task to be live for other apps' plugins to connect to even when the app itself isn't running (this one can be unprivileged). The app will be signed with a Developer ID certificate, and distributed only outside the App Store.
We'd like the app to be a "good citizen" as far as possible, also on uninstall.
For the background task, we're using a login item, created using SMLoginItemSetEnabled(). This isn't amazing, because XPC messaging doesn't seem to work (we're using CFMessagePort instead - alternative suggestions welcome), but if the user deletes the app, the login item at least doesn't get loaded anymore on next login. I suspect there's still a trace of it somewhere in the system, but the executable inside the .app bundle is used, and when that disappears, the login item no longer runs.
For the occasional operation requiring admin rights, we've got a privileged helper tool which our app installs using SMJobBless(), and which implements a named XPC service, so the task spins up on demand when it receives a message from the main app. This is what Apple recommends and describes in its Even Better Authorization Sample.
The helper executable is copied to /Library/PrivilegedHelperTools/ by SMJobBless(), and the embedded launchd.plist ends up in /Library/LaunchDaemons/. Even though the OS has the information on which app "owns" the helper, it doesn't seem to uninstall it when the user deletes the app. Apple's sample is silent on uninstalling, other than the uninstall.sh script which is apparently intended to be used during development only. We don't need this helper while the app isn't running, so installing it as a full-blown launch daemon is slightly overkill, but we'd also like to avoid repeatedly annoying the user with the password prompt too. Besides, Apple advises against other forms of running code with admin privileges than SMJobBless() these days - for example SMJobSubmit() is marked deprecated.
So how do we clean up after ourselves?
I've found SMJobRemove(), but (a) when would we call that in our case - you can't run code on .app bundle deletion, or can you? and (b) it doesn't actually seem to clean up.
The only 2 things I can think of are not terribly satisfying:
Some kind of uninstaller app or script. But that seems pretty ugly too.
Don't worry about it and just leave a mess behind when the user deletes our app.
Update:
There have been some changes in this area with macOS 13.0 Ventura; there's an introduction to the new mechanism in the WWDC22 session 'What’s new in privacy'. The new SMAppService APIs support automatic cleanup for daemons, agents and login items. Unfortunately you'll of course still have to find a workaround for any older macOS versions you support.
Original answer:
There has been a similar question on the Apple Developer Forums at https://forums.developer.apple.com/thread/66821 - the recommendation by Apple is a manual uninstall mechanism, and consuming as few resources as possible if the user does not do this.
Apple DTS staff further recommended implementing a self-uninstall mechanism in the privileged launch daemon, to be triggered from the app via XPC. This is what we're going with.
I think the only solution you have right now is to use the uninstall shell code that you mentioned in order to physically remove the privileged helper from disk or to build an uninstaller for it. Either way you will have to ask the user to enter his/her password. This what all installers / uninstallers that require privileged access to the system do, and for a very good reason. That's why I avoid like the plague to use privileged helpers, but I understand that sometimes you really have to. I don't think it is good that you leave such a helper in the user's system, because it will reload next time the user starts up the computer.
I just checked ServiceManagement.h header and they state that SMJobRemove will be replaced by an API that will be made available through libxpc in the future. (Sometimes you really need to go to the headers to get extra info that the documentation does not give you.) Hopefully this promised replacement will uninstall it for us. However, I'd file a bug report and ask for that enhancement.
One solution you could consider is to include an uninstaller script or program in your .app bundle.
You can then pass the path of this small tool to your helper tool (via IPC) and have the execute the the uninstaller, thereby deleting itself. You will have to be careful that components are removed in the right order but it can be made to work.
You're correct that Apple does not provide an API to uninstall a helper tool installed with SMJobBless nor do they do so automatically. As for why macOS doesn't automatically do an uninstall, my educated guess is because macOS fundamentally doesn't have a unified concept of "install". While it's convention for apps to be located in /Applications (and a few other locations), it's perfectly valid for apps to be located and run from anywhere on the system including external drives and network drives. For example should macOS uninstall helper tools when apps disappear because the drive they're on is disconnected?
In terms of how to uninstall, doing so requires root permission and so realistically have the helper tool itself do the uninstall is the easiest option. You can have your app via XPC tell the helper to uninstall itself. Here's an example in Swift of how to do this; it's part of SwiftAuthorizationSample. The basic idea is:
Use the launchctl command line tool to unload the helper tool
Delete the helper tool executable
Delete the helper tool launchd plist
But there's a bit of additional complexity involved because launchctl won't let you unload a running process.

How to create application to launch wine and Windows application

I'm trying to use "wine" on MAC osX Sierra version 10.12, wine is version 1.9.19
In the terminal I can launch Windows applications, however its a pain to have to keep typing in:
/Applications/Wine\ Staging.app/Contents/MacOS/wine ~/.wine/drive_c/Program\ Files/HeidiSQL/heidisql.exe
I've searched around for a post on how to create shortcuts/applications to add to the launchpad, but so far none of the information has led me to a working end result. Either the locations of wine is different or it just doesn't work.
I've tried creating an application script:
do shell script "/Applications/Wine Staging.app/Contents/MacOS/wine ~/.wine/drive_c/Program Files/HeidiSQL/heidisql.exe"
But this won't run either.
For anyone having the same problem, in the end I created scripts which reside in my home folder:
Launch vi, create a file called HeidiSQL, insert:
wine ~/.wine/drive_c/Program\ Files/HeidiSQL/heidisql.exe
Save and exit file, grant file execute permissions:
chmod +x HeidiSQL
Launch wine terminal and type in ./HeidiSQL to launch, I then did the same for PSPad.exe:
wine ~/.wine/drive_c/Program\ Files\ \(x86\)/PSPad\ editor/PSPad.exe
I know this thread is a little old but I was just looking for something like this to launch HeidiSQL and I came up with these three solutions which I have tried and all of them work. I am putting my findings down here for it may help someone:
Wineskin (http://wineskin.urgesoftware.com), Playonmac (https://www.playonmac.com/en) and Winebottler
(http://winebottler.kronenberg.org)
Wineskin is a mac app that download and install (and manages, updates, etc) "wine" for you. It then creates a HeidiSQL.app (any name you want with any icon you want - but you need to configure it) around the windows.exe that includes the wine version selected and is completely self contained (does not need wine installed separately). Personally this is the neatest solution and my preferred even though there its a little more technical than Playonmac and similar to Winebottler. You need to read the instructions (which are very good) and you have choices to make as to the wine version to use and to configure the app. Noteworthy is that you need to change the windows version to XP rather than 7 or some buttons won't work.
Playonmac on the other hand is very user friendly. It has HeidiSQL listed on its website as compatible and its almost a single click install. You just select HeidiSQL from the list of programs and it will download everything you need for you. The only reason I prefer Wineskin is that it does not create a true self contained HeidiSQL.app. You can create a shortcut for it in your Applications folder but this will launch Playonmac and the app needs to be installed inside Playonmac. On the plus side, Playonmac will chose all the right settings for you to run the app correctly, the correct windows and wine version etc which is something you need to fiddle with with with Wineskin.
Winebottler again makes an app like Wineskin. The only real difference I could see is that with Wineskin the configuration app is actually part of the package whereas in Winebottler you have to recreate the package each time you make a change. I stuck with Wineskin. YMMV.

Auto-update .dmg on mac

As i am newbie on mac and .dmg for me is a black box. And i don't know is it possible rewrite some files in it like on Windows.
Someone can explain how user can update my app for one click: "There new version available. Do you want to install it now?" - [OK] [Cancel]
A .dmg container is read-only (typically, you can make them writable though). The recommended behaviour with an OS X application is to do one of two things:
Put a note in the .dmg to move the application to Applications (see: Creating nice dmg "installer" for Mac OS X)
Auto-move the Application on first run, however you need to be incredibly explicit to the user about what's happening and where they need to go to load the application in future.
I always go for option 1, it's clear and concise and there's a wealth of other software that go for it so it's understood by most OS X users.
In terms of doing an autoupdate, once it's in the user filesystem you can do it a number of ways. There's the Sparkle Framework, you can build a simple HTTP request service to grab the new .app bundle and copy it into place and then restart. Sparkle is good, well tested and very popular for OS X autoupdating, if you're going cross-platform then build the HTTP service.

Escalate Privileges in OSX

I'm writing a simple OSX application to write firmware to a USB device. I'm using libusb to perform I/O.
My first pass at this works perfectly, provided I run it with sudo. I'm now creating a GUI version of the tool and I'd like to prompt the user to escalate.
I know the "correct" way to do this is with a privileged helper but I also need to report back progress to the UI, and I don't feel like building out IPC for something this simple.
Is there a simple way to request root or USB write privilege from the user for a GUI app?
I gave up and used the deprecated AuthorizationExecuteWithPrivilege API to re-launch my app if I didn't have root privilege.
It strikes me as a way less than optimal solution, if anyone has a better idea, I'd love to hear it.

Resources