Dependency Injection

Hello.

How might I implement the Dependency Injection pattern using Xojo? I’d like to permit 3rd parties to create custom add-ins built with Xojo and access those from my compiled Xojo application at runtime.

For example, applications like Microsoft Outlook and Excel support the creation of 3rd party add-ins that can be dynamically loaded at runtime. This makes my application more modular such that others and/or I can (using Xojo) build new functionality via separate library modules and then have my application load/register those at runtime for use by the end user.

Couldn’t you do this with Xojoscripts?

It’s at least one approach.

There are any number of ways to implement this kind of extensibility in Xojo, but no easy way, and you’ll have to carefully consider how much functionality you want to expose. Rough examples of various depths of integration:

Level 1: execute a script from a menu that takes actions outside of the app. For example, a menu that presents a list of AppleScripts.

Level 2: expose functionality that lets a piece of external code run certain predefined commands within the application. For example, a command that exports the current document as PDF.

Level 3: exposes an object and command model that lets external code create, manipulate, and delete objects; execute commands that control and connect those objects and other aspects of the app’s configuration and behavior. For example, a piece of code that creates a document, creates several objects and sets their colors to blue, and then saves the result into a file.

Level 4: same as level 3 but lets the user record their activities using the application into a script that the user can understand and modify.

The level of implementation complexity between the levels is not linear. Level 1 is simple; Level 2 is hard; Level 3 will likely require you to refactor your application; Level 4 will require you to refactor it a second and possibly third time. :slight_smile:

That said, the benefits of this sort of extensibility cannot be overstated. There’s a reason AppleScript is one of the few non-core technologies that made the transition from the old Mac OS to OS X[1]: it was (and is) widely used to automate complex tasks, tying together multiple applications in ways that nobody could predict ahead of time, to accomplish tasks that no single application could complete. From the day AppleScript was released in 1993, professional users immediately latched on to it to get their work done, and nothing has ever come close to replacing it. The sheer flexibility of the model has stood the test of time.

Of course, AppleScript is a Mac-only technology, and you may want to use something else if your app is cross-platform or not on the Mac at all. But if your app is on the Mac, and you decide to go to level 3 or 4, I really urge you to take a look at implementing AppleScript. The fundamentals that you have to implement for AppleScript will be useful no matter what language you settle on.


  1. It’s a short list that includes AppleScript (1993), ColorSync (1993), AppleTalk (1985), Keychain (part of PowerTalk, 1993), and perhaps a few others. AppleTalk is long gone but AppleScript and ColorSync seem here to stay, having shown that they are well-suited to purpose and unlikely to be improved upon through replacement. Keychain is now Passwords but the underlying framework survives largely intact. ↩︎

I would say that not even level 1 is simple. There is an AppleScript framework from Thomas Tempelmann. I think there should be some alternatives floating around like making an App Lua scriptable by Björn.

Thanks so much for the feedback, Eric!

I kind of figured it wouldn’t be a simple way to accomplish this and this would need to be a cross-platform application (not macOS only). I’ve built applications in the past (using other languages) with an extensible framework. The last one I created allowed the 3rd party add-in to dynamically add its own menu items and toolbar items to my core application without having to refactor my app.

There were a few steps:

  • The developer would build the add-in, which implemented one of two Interface types that my application required it to implement. The developer would simply reference the compiled library I created which included the two Interface definitions and any additional classes that could be inherited into his/her project.
  • Once the developer compiled their solution as a library, my application allowed the end user to simply browse for that binary (i.e. add-in) and then “register” it. Essentially copying it to a special folder in my app’s folder hierarchy (I typically would create a sub-folder named <>).
  • The registration process would check to determine if the 3rd party library contained any classes that implemented one or both of my interfaces and if so, it would be copied to that special folder.
  • The user would then be able to flag whether or not (via a checkbox) any of the add-ins should be started each time the application was run. This way, any menu items or toolbar items that the add-in needed to create would be created and it pretty much seemed as if this functionality was core to my main application. Two of the methods in my Interfaces allows the add-in creator to specify a human-readable name for the add-in as well as a description (essentially metadata that my app would use to allow the user to know the name of the add-in and what it did).

Essentially the class(es) in the add-in “inject” themselves into my core application at runtime so that their methods could be executed by my app. In the .NET world, there’s something called the System.Reflection namespace. It contains functionality that allows you to dynamically load other .NET libraries at runtime. From there, you can (for the lack of a better world) “interrogate” the library to (among other things) find any classes that implement either or both of my interfaces.

From there, my app would do something like this in order to invoke the methods within that add-in:

// I’ll pretend that I’m using Xojo
var lib As XojoLibrary = SpecialFolder.LoadXojoLibrary(“path and name of custom Xojo Library”)
var clsObjects() As XojoClass = lib.GetClasses()

For Each obj As XojoClass In clsObjects
If obj IsA MyInterfaceType Then
var aMethod As XojoLibrary.GetMethodInfo = obj.GetMethod(“MethodName”)
var aValue As String = aMethod.Invoke(someValuePassedIn)

MessageBox(aValue)

End If

Next

Lib = Nil
clsObjects = Nil

I hope this makes sense?

It’s simple because all it does is execute an AppleScript (or some other executable). The app doesn’t implement any scriptable functionality.

For starters, this isn’t “dependency injection” - that is a completely different design pattern that has nothing to do with application extensibility. :slight_smile: You are talking about creating a plugin architecture.

What you’ve described is actually easy, if all you want is buttons and menu item that a user can push and select. The difficult part is getting them to actually do anything useful within the context of your application, and It won’t take the form you are familiar with. In compiled Xojo apps, the classes you can use are limited to the ones that were present during compilation. There would be no point in interrogating an external file for its classes, because those cannot be instantiated within your app.

(I’m glossing over some exceptions here. For example, it is possible to use Declares to instantiate classes that the operating system provides. However, these are not first class Xojo objects like what you are describing, and orthogonal to the problem being discussed.)

Instead, you would need to define an interface that allows your code to interact with external code. Again, the implementations vary, but typically you are looking at either implementing a system standard like AppleScript; implementing your own little language (I’d suggest starting with XojoScript); or designing a custom communications protocol that travels over a socket and allows a separate executable access to your app’s internals.

To put it mildly, this is a very non-trivial task. Xojo is never going to work in the way that you’ve been using .NET and you will need to recalibrate your approach and your expectations to match how Xojo is architected.

I’m only using buttons and menus as an example. This capability goes beyond that.

“To put it mildly, this is a very non-trivial task. Xojo is never going to work in the way that you’ve been using .NET and you will need to recalibrate your approach and your expectations to match how Xojo is architected.” — this is a fair statement. it helps me to understand what’s possible vs not.

This is why I keep both development platforms in my toolbox (Xojo and .NET).