Modularity in Xamarin.Forms

CytophCytoph Member ✭✭
edited January 12 in Xamarin.Forms
The company I'm currently working at has a product with a few core functionalities (forms), that can be accessed by a kind of main menu. Now I'm trying to implement customer specific functionalities with some kind of modules, so this can be developed (and later loaded) only for this specific customer. The key problem is, that we want to develop the customer specific modules in separate solutions/repositories and have our core product load them later, but only the ones that are activated for the specific customer.

In know something like this can for example be done with Prism, but only in Wpf. Therefore I posted this in this forum, cause I need this for Xamarin.Forms.

Is something like this possible in any way or absolutely not?

Answers

  • JohnHardmanJohnHardman GBUniversity mod

    @Cytoph said:
    Is something like this possible in any way or absolutely not?

    It depends on how you distribute the app and for which platforms. Assuming that you build an iOS version, if it goes via the app store, you'll need to do integration at build-time, not at run-time, as Apple do not permit dynamically loading libraries.

  • CytophCytoph Member ✭✭
    edited January 13

    @Nicolas.ETIENNE.31 said:
    I guess this thread may give you one answer

    This answer the question of loading modules on run-time. But this is not exactly what I asked for.

    @JohnHardman said:
    It depends on how you distribute the app and for which platforms. Assuming that you build an iOS version, if it goes via the app store, you'll need to do integration at build-time, not at run-time, as Apple do not permit dynamically loading libraries.

    I know that it's not possible to do this due to the limitations of iOS.

    My question focuses on the code structuring. That should mean, if it's possible to have a project with core functionality that has a public list property, in which modules can add themselves, and then another solution which adds a reference to this project (from another solution), add themselves to this list property and when built, build an app (APK, etc.) with the core functionality plus the functionality of the module that builds it.

  • JohnHardmanJohnHardman GBUniversity mod

    @Cytoph said:
    My question focuses on the code structuring. That should mean, if it's possible to have a project with core functionality that has a public list property, in which modules can add themselves, and then another solution which adds a reference to this project (from another solution), add themselves to this list property and when built, build an app (APK, etc.) with the core functionality plus the functionality of the module that builds it.

    It could be done using convoluted build scripts, depending on what you mean by "load them later" in your original post.

    However, you might want to look at things the other way around. Rather than have one common solution loading the customer specific bits, have one solution per customer, with each customer-specific solution pulling in NuGets that contain the reusable functions required by that customer.

  • CytophCytoph Member ✭✭

    @JohnHardman said:
    However, you might want to look at things the other way around. Rather than have one common solution loading the customer specific bits, have one solution per customer, with each customer-specific solution pulling in NuGets that contain the reusable functions required by that customer.

    Yes, that was what I mean with my post. Sorry for not explaining it the right way. My idea is to have the core functionality as nuget packages and then customer specific solutions that use these packages.

    My question is if it it's possible to have the "main logic", let's say a main menu for example that displays all modules, in the core solution then have a customer specific solution build and run the app, start to the core main menu and have its own logic displayed there?

  • JohnHardmanJohnHardman GBUniversity mod
    edited January 13

    @Cytoph said:
    My question is if it it's possible to have the "main logic", let's say a main menu for example that displays all modules, in the core solution then have a customer specific solution build and run the app, start to the core main menu and have its own logic displayed there?

    You can have the main menu "shell" (for want of a better word) in a NuGet that is loaded by all of the customer-specific solutions. The customer-specific solutions populate the menu shell with whatever functions that customer gets access to, providing for each function either (a) a tuple containing properties such as the user-friendly name of the function, an icon, and a delegate that gives access to the functionality, or (b) an interface that can be called to get those same properties.

  • NMackayNMackay GBInsider, University mod

    @Cytoph

    Prism 7.1 handles modularity, I've used it an my previous app to handle loading business modules based on authenticated users claim credentials. We're reworking the app I'm currently working on to be modularised, splitting up your app to have a shell, common (shared code) and model etc makes it easier to accomplish this pattern.

    https://github.com/PrismLibrary/Prism-Samples-Forms/tree/master/UsingModules

    Modularity support is really good in Prism 7+, I'd strongly use checking out Prismlib, it's teh only Xamarin MVVM framework out there that supports modularity.....or roll your own but why reinvent the wheel.

  • NMackayNMackay GBInsider, University mod

    In my previous role we have three apps that shared the same common, model & modules etc. It is not WPF though, you cannot load modules dynamically as they have to be part of teh APK, IPA etc but you can choose when to instantiate them with Prism

  • Nicolas.ETIENNE.31Nicolas.ETIENNE.31 USMember ✭✭

    And at last, if you'd really wanted to have "dynamic" modules, you could also use REST service in order to allow the user
    to access some without providing in the APK all the features even for not allowed people...
    (but this means that you have to create REST API, manage users rights, profiles, and so on...)

  • MassimoAristideMassimoAristide CHUniversity ✭✭

    Hi everyone, I came across this discussion as I'm currently experiencing a strange behavior in my current Xamarin.Forms project. Along Xamarin.Forms 3.6.x we use the Prism library (7.1) as a tool to realize a modular application. I know Prism from a couple of other WPF projects and I'm aware of that there are differences between the Prism functionality available on WPF and Xamarin.Froms. But I can't explain myself my observation.

    Following the basic structure of the project.

    Foo.App references: Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.iOS references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.Android references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.UWP references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.ModuleA
    Foo.App.ModuleB

    In the Foo.App.App.xaml.cs I have the following override:

    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
            {
                base.ConfigureModuleCatalog(moduleCatalog);
                foreach (var item in moduleCatalog.DiscoverModules())
                {
                    System.Diagnostics.Debug.WriteLine(item.FullName);
                    var info = ModuleCatalogExtension.CreateModuleInfo(item);
                    System.Diagnostics.Debug.WriteLine(info.ToString());
                    moduleCatalog.AddModule(info);
                }
            }
    

    Further I've a ModuleCatalogExtension with this code:

    public static class ModuleCatalogExtension
        {
            public static IEnumerable<Type> DiscoverModules(this IModuleCatalog moduleCatalog)
            {
                var type = typeof(IModule);
    
                var modulTypes = AppDomain.CurrentDomain.GetAssemblies()
                    .SelectMany(s => s.GetTypes())
                    .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
    
                return modulTypes;
            }
    
            public static ModuleInfo CreateModuleInfo(Type type)
            {
                string moduleName = type.Name;
                List<string> dependsOn = new List<string>();
                bool onDemand = false;
    
                ModuleInfo moduleInfo = new ModuleInfo(moduleName, type.AssemblyQualifiedName)
                {
                    InitializationMode =
                        onDemand
                            ? InitializationMode.OnDemand
                            : InitializationMode.WhenAvailable,
                    Ref = type.Assembly.CodeBase,
                };
                return moduleInfo;
            }
        }
    

    The idea of this was to get rid of code lines like this in App.xaml.cs

    moduleCatalog.AddModule<SampleModule.SampleModule>(InitializationMode.OnDemand);

    which you can see in the UsingModules Prism sample.

    What I observe is, that this code works on UWP and Android and finds the referenced Assemblies and IModule implementer but on iOS the call Appdomain.CurrentDomain.GetAssemblies() is just missing the Foo.App.ModuleA and Foo.App.ModuleB assemblies in the returned collection.

    By googling I found some posts on Stackoverflow and other sources stating that the GetAssemblies call return only a collection of already loaded assemblies. This confuses me even more as the behavior is not equal on each platform.

    Any ideas what I'm missing here?

  • MassimoAristideMassimoAristide CHUniversity ✭✭
    edited July 26

    Hi everyone, I came across this discussion as I'm currently experiencing a strange behavior in my current Xamarin. Forms project. Along Xamarin. Forms 3.6.x we use the Prism library (7.1) as a tool to realize a modular application. I know Prism from a couple of other WPF projects and I'm aware of that there are differences between the Prism functionality available on WPF and Xamarin.Froms. But I can't explain myself my observation.

    Following the basic structure of the project.

    Foo.App ---------->references: Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.iOS ------>references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.Android ->references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.UWP ----->references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.ModuleA
    Foo.App.ModuleB
    ....

    In the Foo. App.App.xaml.cs I have the following override:

    Protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
     {
         base.ConfigureModuleCatalog(moduleCatalog);
         for each (var item in moduleCatalog.DiscoverModules())
         {
             System. Diagnostics. Debug.WriteLine(item.Filename);
             var info = ModuleCatalogExtension.CreateModuleInfo(item);
             System. Diagnostics. Debug.WriteLine(info.ToString());
             moduleCatalog.Module(info);
         }
     }
    

    Further I've a ModuleCatalogExtension with this code:

    public static class ModuleCatalogExtension
        {
            public static IEnumerable<Type> DiscoverModules(this IModuleCatalog moduleCatalog)
            {
                var type = typeof(IModule);
    
                var modulTypes = AppDomain.CurrentDomain.GetAssemblies()
                    .SelectMany(s => s.GetTypes())
                    .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
    
                return modulTypes;
            }
    
            public static ModuleInfo CreateModuleInfo(Type type)
            {
                string moduleName = type.Name;
                List<string> dependsOn = new List<string>();
                bool onDemand = false;
    
                ModuleInfo moduleInfo = new ModuleInfo(moduleName, type.AssemblyQualifiedName)
                {
                    InitializationMode =
                        onDemand
                            ? InitializationMode.OnDemand
                            : InitializationMode.WhenAvailable,
                    Ref = type.Assembly.CodeBase,
                };
                return moduleInfo;
            }
        }
    

    The idea of this was to get rid of code lines like this:

    moduleCatalog.Module(InitializationMode.OnDemand);

    which you can see in the App.xaml.cs file of the UsingModules Sample.

    What I observe is, that this code works on UWP and Android and finds the referenced Assemblies and Module implementer but on iOS the call App domain.CurrentDomain.GetAssemblies() is just missing the Foo. App.ModuleA and Foo. App.ModuleB assemblies in the returned collection.

    By googling I found some posts on Stack overflow and other sources stating that the GetAssemblies call return only a collection of already loaded assemblies. This confuses me even more as the behavior is not equal on each platform.

    Any ideas what I'm missing here?

  • NMackayNMackay GBInsider, University mod

    @MassimoAristide said:
    Hi everyone, I came across this discussion as I'm currently experiencing a strange behavior in my current Xamarin. Forms project. Along Xamarin. Forms 3.6.x we use the Prism library (7.1) as a tool to realize a modular application. I know Prism from a couple of other WPF projects and I'm aware of that there are differences between the Prism functionality available on WPF and Xamarin.Froms. But I can't explain myself my observation.

    Following the basic structure of the project.

    Foo.App ---------->references: Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.iOS ------>references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.Android ->references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.UWP ----->references: Foo.App, Foo.App.ModuleA, Foo.App.ModuleB
    Foo.App.ModuleA
    Foo.App.ModuleB
    ....

    In the Foo. App.App.xaml.cs I have the following override:

    Protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
     {
       base.ConfigureModuleCatalog(moduleCatalog);
       for each (var item in moduleCatalog.DiscoverModules())
       {
           System. Diagnostics. Debug.Priceline(item.Filename);
           var info = ModuleCatalogExtension.CreateModuleInfo(item);
           System. Diagnostics. Debug.Priceline(info.ToString());
           moduleCatalog.Module(info);
       }
     }
    

    Further I've a ModuleCatalogExtension with this code:

    public static class ModuleCatalogExtension
        {
            public static IEnumerable<Type> DiscoverModules(this IModuleCatalog moduleCatalog)
            {
                var type = typeof(IModule);
    
                var modulTypes = AppDomain.CurrentDomain.GetAssemblies()
                    .SelectMany(s => s.GetTypes())
                    .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
    
                return modulTypes;
            }
    
            public static ModuleInfo CreateModuleInfo(Type type)
            {
                string moduleName = type.Name;
                List<string> dependsOn = new List<string>();
                bool onDemand = false;
    
                ModuleInfo moduleInfo = new ModuleInfo(moduleName, type.AssemblyQualifiedName)
                {
                    InitializationMode =
                        onDemand
                            ? InitializationMode.OnDemand
                            : InitializationMode.WhenAvailable,
                    Ref = type.Assembly.CodeBase,
                };
                return moduleInfo;
            }
        }
    

    The idea of this was to get rid of code lines like this:

    moduleCatalog.Module(InitializationMode.OnDemand);

    which you can see in the App.xaml.cs file of the UsingModules Sample.

    What I observe is, that this code works on UWP and Android and finds the referenced Assemblies and Module implementer but on iOS the call App domain.CurrentDomain.GetAssemblies() is just missing the Foo. App.ModuleA and Foo. App.ModuleB assemblies in the returned collection.

    By googling I found some posts on Stack overflow and other sources stating that the GetAssemblies call return only a collection of already loaded assemblies. This confuses me even more as the behavior is not equal on each platform.

    Any ideas what I'm missing here?

    Not sure what you mean, we use modularity but you have to configure the catalog in App.xaml.cs...like the sample

    https://github.com/PrismLibrary/Prism-Samples-Forms/blob/master/UsingModules/UsingModules/UsingModules/App.xaml.cs

    Struggle to see what's so terrible about using the standard approach that works on all platforms.

  • MassimoAristideMassimoAristide CHUniversity ✭✭

    I know that it could be done like in the sample. But I don't see what is wrong doing / trying it the way I did. Ok it looks like xamarin.form on iOS has an issue with that. I'm trying to figure out why this works on android and uwp

  • NMackayNMackay GBInsider, University mod

    Prism slack channel might be a better bet for this one.

  • MassimoAristideMassimoAristide CHUniversity ✭✭

    thx will try there

Sign In or Register to comment.