NetSparkleUpdater / NetSparkle

NetSparkle is a C#, cross-platform, highly-configurable software update framework with pre-built UI for .NET developers compatible with .NET 4.6.2/.NET 6+, WinForms, WPF, and Avalonia; uses Ed25519 signatures. View basic usage here in the README and try the samples for yourself.
https://netsparkleupdater.github.io/NetSparkle/
MIT License
606 stars 84 forks source link

Xamarin.mac support #97

Closed mphill closed 10 months ago

mphill commented 4 years ago

Running a Xamarin.mac app that uses NetSparkle crashes on instantiation of NetSparkle with version 2.0 with the exception: Attribute of type System.Reflection.AssemblyProductAttribute

Supporting NetSparkle on Xamarin.mac would be a great addition. I noticed the core package has some windows specific references like Microsoft.Win32.Registry. If that was moved from the core package and into the Windows specific UI implementation it would allow NetSparkle to be used on Linux, Mac, Windows and maybe even iOS/Android (In-app dialogs about new versions).

Thanks.

mphill commented 4 years ago

A quick update, this is working on master now.

However, iOS and MacOS apps are not supported.

                if (_configuration == null)
                {
#if NETSTANDARD
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        _configuration = new RegistryConfiguration(_appReferenceAssembly);
                    }
                    else
                    {
                        _configuration = new JSONConfiguration(_appReferenceAssembly);
                    }
#else
                    _configuration = new RegistryConfiguration(_appReferenceAssembly);
#endif
                }

Are you Ok if I do a pull request to add an InfoPlistConfiguration?

On iOS and MacOS Info.plist is the analog to AssemblyInfo in some respects.

        protected Configuration(string referenceAssembly, bool isReflectionBasedAssemblyAccessorUsed)
        {
            // set default values
            InitWithDefaultValues();

            // set the value
            UseReflectionBasedAssemblyAccessor = isReflectionBasedAssemblyAccessorUsed;
            // save the reference assembly
            ReferenceAssembly = referenceAssembly;

            try
            {
                // set some value from the binary
                AssemblyAccessor accessor = new AssemblyAccessor(referenceAssembly, UseReflectionBasedAssemblyAccessor);
                ApplicationName = accessor.AssemblyProduct;
                InstalledVersion = accessor.AssemblyVersion;
            }
            catch
            {
                CheckForUpdate = false;
            }

        }

This code would need to be refactored to abstract the AssemblyAccessor out.

Deadpikle commented 4 years ago

Yes, you'd be welcome to open a PR. A few things, though:

  1. If you're on macOS, I highly recommend using Sparkle instead of this lib as it is a native library that has stood the test of time in terms of features and usage. (Also has delta updates.) I got it working on an Avalonia app, so I think it should work on Xamarin.Mac. See my directions for using Sparkle with Avalonia perhaps.
  2. A macOS/iOS-specific Configuration would have to be a mix of Info.plist use and JSONConfiguration use, since we won't be able to write to the Info.plist file. However, a better way to do this would be to implement a new IAssemblyAccessor implementation that reads from Info.plist, since the JSONConfiguration should work fine for everything else. We'd need some way to pass in the appropriate AssemblyAccessor to the Configuration subclass, which might take a little more work. (The configuration classes need read/write capabilities and store info on when updates were checked, etc. The assembly accessor classes just give info on the actual assembly and only need read permissions.)

If you're working on it, why don't you do an IAssemblyAccessor subclass that reads from Info.plist? I would need a little more time to see how to do the other work.

Edit: Also, believe it or not, Microsoft.Win32.Registry targets .NET Standard!

mphill commented 4 years ago

If you're on macOS, I highly recommend using Sparkle instead of this lib

Thanks for the recommendation, but maintaining native bindings has its own set of issues, and it can be very time consuming. I'd like to explore what supporting Xamarin (Mac, Android, iOS) apps might look like. Using sharpie to generate ApiDefintions is very tedious.

If you're working on it, why don't you do an IAssemblyAccessor subclass that reads from Info.plist?

I can do this, but in order to read the Info.plist at runtime, I would need to add Xamarin.Mac as a dependency in order to call NSBundle.MainBundle.ObjectForInfoDictionary i.e.

        public string AssemblyVersion => GetInfoPlistValue("CFBundleShortVersionString");

        private string GetInfoPlistValue(string identifier)
        {
            return NSBundle.MainBundle.ObjectForInfoDictionary(identifier).ToString();
        }

I doubt you want Xamarin as a project dependency, is there a way to pass this in somehow. I didn't see anything obvious in code.

JSONConfiguration is not public so I can't access that in the main project - but also it reeds from the file system. In Xamarin apps you'd want to use app preferences or NSUserDefaults

Is there a way to allow a developer manually create something to pass in the constructor like an IConfiguration

Deadpikle commented 4 years ago

Thanks for the recommendation, but maintaining native bindings has its own set of issues, and it can be very time consuming. I'd like to explore what supporting Xamarin (Mac, Android, iOS) apps might look like. Using sharpie to generate ApiDefintions is very tedious.

Understood.

Is there a way to allow a developer manually create something to pass in the constructor like an IConfiguration

You can set SparkleUpdater.Configuration = myConfig; (see here) or do something like

var sparkle = new SparkleUpdater(...)
{
    Configuration = new MyConfig()
};

Just don't call the default constructor in the subclass for now.

Hm...regarding Info.plist: I don't see yet why we couldn't just read that in as XML. We don't really need a lib for reading that file, do we?

Regarding NSUserDefaults -- I can see why we'd need a dependency for this one. I don't know of a way to do this without some sort of native code. I am not unopposed to adding a NetSparkleUpdater.XamarinMac project or similar that has the extra dependency in it along with special Configuration or other classes that are specific to Xamarin. That way it's not a dependency in the main project.

(I have to apologize, here: normally I'd do a bit more research on my end, but life is very busy right now, and I don't have a lot of time during the work week for extra things. I've not used Xamarin before, so I am depending on your experience here.)

mphill commented 4 years ago

Hm...regarding Info.plist: I don't see yet why we couldn't just read that in as XML. We don't really need a lib for reading that file, do we?

I think that would work. I thought in the past I saw binary data in the info.plist once compiled, but looking around I don't see it. I think parsing the info.plist as xml works, but if you add a new project it could all be done with native calls.

I would propose this, a new project named:

NetSparkleUpdater.Xamarin with NetSparkleUpdater as a reference.

It would hold OS specific implications for iOS and MacOS to start, with Android added a little later as I find time.

In the main project I think there would be compiler directive to do this:

#if __MACOS__ || __IOS__
using NetSparkleUpdater.Xamarin;
#endif

...

#if __MACOS__ || __IOS__
     _configuration = new PListInfoConfiguration(_appReferenceAssembly); // class located in NetSparkleUpdater.Xamarin
#endif

Thoughts?

Deadpikle commented 4 years ago

NetSparkleUpdater.Xamarin sounds fine along with the ref. You don't need to touch the main project though since you can set the Configuration property on SparkleUpdater without SparkleUpdater needing to know about the extra refs.

Deadpikle commented 4 years ago

Posted this in PR #98 too, but for historical reference regarding referencing Xamarin in VS:

I'll try to look at the Xamarin.Mac thing too. I do have a Mac on hand and can take a look, I think. Perhaps if the project were created on macOS then referenced from the SLN manually? I'm not opposed to having a separate SLN if we have to. I did a quick test, and a Xamarin library project can reference the latest previews of NetSparkle on macOS, so it should be possible...

Deadpikle commented 4 years ago

I was able to add a Xamarin project to the solution from Visual Studio for macOS. Visual Studio on Windows is complaining on my end, but no big deal.

I had to run a manual dotnet restore from within src/NetSparkle before Visual Studio for macOS would build the Xamarin DLL project.

Once things are working, a new GitHub action can be set up to build and deploy that package to NuGet.

So, I would actually recommend a few things. Configuration classes, as they are right now, are more for saving and loading the info on last version seen, etc. The IAssemblyAccessor classes are used by Configuration subclasses to load things. So, I think the following needs to happen for Xamarin support:

Deadpikle commented 10 months ago

Given that all Xamarin support is ending on May 1, 2024 (https://dotnet.microsoft.com/en-us/platform/support/policy/xamarin), and no one else has asked for this nor helped to contribute code to this, I don't see the point of adding this to the NetSparkle library at this time. :)