Use the popular macOS Sparkle library in your macOS Avalonia projects! With this setup, you can still develop in Visual Studio normally without bundling the .app
-- the native lib will make sure you're in a .app
with a few Info.plist
values defined before it runs Sparkle. When your project is ready for prime-time and you've got a .app
ready, you can use this setup to run Sparkle with your app!
Requirements:
- Xcode
- macOS computer (for building native library, etc.)
- This only works within a
.app
file, so your program needs to be bundled in a.app
for this to work. - Your
.app
needs a validInfo.plist
defined with all the things Sparkle requires (SUFeedURL
, etc.). - This project comes with a pre-built
Sparkle.framework
. If you don't feel comfortable using that for whatever reason, just clone the Sparkle repo, build it yourself, and make sure your build's output Sparkle.framework is in the same place as the current one.
- Download/clone this project. It'll work just fine within your current project structure.
- Open up
SparkleUpdater/SparkleUpdater.xcodeproj
in Xcode. Build the project in both debug and release. The output defaults toSparkleUpdater/build/{Configuration}/libSparkleUpdater.dylib
. - In your
.csproj
for your Avalonia project, add the following:
<ItemGroup Condition="'$(Configuration)' == 'Debug' AND '$([MSBuild]::IsOSPlatform(OSX))' == 'true'">
<Content Include="path/to/Debug/libSparkleUpdater.dylib">
<PackagePath>runtimes/osx/native/libSparkleUpdater.dylib</PackagePath>
<Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' AND '$([MSBuild]::IsOSPlatform(OSX))' == 'true'">
<Content Include="path/to/Release/libSparkleUpdater.dylib">
<PackagePath>runtimes/osx/native/libSparkleUpdater.dylib</PackagePath>
<Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
- You'll also need to add this to your
.csproj
so that Sparkle.framework is copied to your build directory. Otherwise, anyP/INVOKE
calls that happen when you're building your application and it's not in a.app
file will fail, since your.dylib
native library won't be able to findSparkle.framework
.
<PropertyGroup Condition=" '$([MSBuild]::IsOSPlatform(OSX))' == 'true' ">
<CustomCommands>
<CustomCommands>
<Command>
<type>AfterBuild</type>
<command>rsync -a --ignore-existing -l ${SolutionDir}/path/to/Sparkle.framework ${TargetDir}</command>
</Command>
</CustomCommands>
</CustomCommands>
</PropertyGroup>
- Make sure that when you build your
.app
that you copyNetSparkle.framework
intoMyApp.app/Contents/Frameworks/NetSparkle.app
. - When your software has started, run this code to initialize Sparkle and check for updates silently (the update window will only show if an update is available):
using System.Runtime.InteropServices;
[DllImport("libSparkleUpdater")]
private static extern void InitSparkle();
public SomeFunc()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
InitSparkle();
}
}
It's probably best to call this after your main window has been shown.
- If you want the user to be able to manually check for updates, you can use this code:
using System.Runtime.InteropServices;
[DllImport("libSparkleUpdater")]
private static extern void CheckForSparkleUpdates();
public SomeFunc()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
CheckForSparkleUpdates();
}
}
Ta-da! Sparkle updates will now work as provided in their documentation.
When I code sign my app with a hardened runtime (for notarization), do I have to do anything special for NetSparkle?
Yup! As outlined in this GitHub comment, you need to code sign AutoUpdate.app
. You can do it like this:
!#/bin/bash
IDENTITY="Developer ID: MyCompany, Inc."
codesign --verbose --force --deep --options=runtime --sign "$IDENTITY" "MyApp.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/AutoUpdate.app"
codesign --verbose --force --options=runtime --sign "$IDENTITY" "MyApp.app/Contents/Frameworks/Sparkle.framework/Versions/A"
codesign --verbose --force --options=runtime --sign "$IDENTITY" "MyApp.app/Contents/Frameworks/Sparkle.framework"
What about all the Sparkle delegate functions, etc.?
You'll need to modify the native lib yourself if you want to use those, as I haven't done the leg work myself to get that running. Maybe you'd be willing to contribute those improvements back to this library?