Creating an app
Plug
to load and create plugins.Create a new projectin page link
First things first, let’s create a new project. We’ll be using the Swift Package Manager for this. Create a new folder for your project and run the following commands:
mkdir MyApp
cd MyApp
swift package init --type executable
Add the Plug dependencyin page link
Next, add Plug
as a dependency to your Package
file:
.package(url: "https://github.com/lyricalsoul/Plug.git", from: "0.1.0")
Then add Plug
as a dependency to your target:
.target(name: "MyApp", dependencies: [
.product(name: "Plug", package: "plug")
])
Set up the appin page link
Now that we have a project, we can start writing some code. What we’ll be going is pretty simple: we’ll create a Plugin
instance and load a plugin with it. This class is the basis for all plugin management in Plug
. It handles loading plugins, creating instances of them, and sending events to them.
let manager = PluginManager()
try await manager.loadPlugin(pathWithoutExtension: ".build/debug/libExamplePlugin") { plugin in
plugin.on(name: "pong") { (text: String) in
print("Received pong event from plugin with text: \(text)")
}
}
await manager.send(event: "ping", data: "hello world", to: { $0.name == "ExamplePlugin" })
Let’s break this down a bit.
First, we create a
Plugin
instance. This is the class that handles the plugin management.Manager Next, we load a plugin. This is done using the
load
method. The method takes a path to our plugin, a dynamic library, and a closure that will be called when the plugin is loaded. The closure takes aPlugin(path Without Extension: closure:) Loaded
instance as its only argument. This instance is a wrapper around the plugin interface. It allows us to listen to events more conveniently.Plugin Builder
Next, we send an event to the plugin. This is done using the
send(event:
method. This method takes the name of the event, the data to send, and a closure that will be used to determine which plugins to send the event to. This closure takes adata: to:) Plugin
instance as its only argument. This instance contains information about the plugin, such as its name and version. In this case, we’re sending the event to the plugin with the name “ExamplePlugin”.Details
Create the pluginin page link
Now that we have an app, we need a plugin to load. For simplicity’s sake, we’ll put the plugin in the same project as the app. Create a new folder called Example
on the same level as the Sources
folder. Inside this folder, create a new Plugin
file with the following contents (and don’t forget to import Plug
!):
struct ExamplePlugin : PluginInterface {
var version = "1.0.0"
var author = "John Doe"
func on(event: PluginLifecycleEvent) -> Void {
switch event {
case .load:
// plugin loaded
break
case .unload:
// plugin unloaded
break
}
}
func on(event: String, data: Any?) async -> Void {
if event == "ping" {
send(name: "pong", data: data)
}
}
}
Again, let’s dissect this a bit.
First, we apply the
@Plugin
macro to our plugin structure. This macro does a few things:It adds missing methods so that our plugin conforms to the
Plugin
protocol, such as theInterface name
property, which is the name of the struct and is used to identify the plugin. It also adds thesend(name:
method, thedata:) builder
property, etc.It adds the
Exported
class to the file. This class is the interface between the plugin and the host application. It controls the event system, manages the plugin’s lifecycle, frees memory when needed (reloading). It inherits from thePlugin Builder Plugin
class.Builder It adds the
create
method to the file. This method is what allows the host application to create an instance of the plugin. It returns an instance of thePlugin() Exported
class.Plugin Builder
Next, we actually create the plugin. We do this by creating a struct that conforms to the
Plugin
protocol. This protocol defines the basic requirements for a plugin, such as the name, version, and author. In our implementation, we have 2 methods:Interface on(event:)
andon(event:
. The first method is called when the plugin is loaded or unloaded. The second method is called when an event is sent to the plugin. In this case, we’re sending the “pong” event back to the host application with the same data that was sent to us.data:)
Add the plugin to Package.swiftin page link
Now that we have a plugin, we need to add it to our Package
file. This is done by adding a new target to the targets
array:
.target(name: "ExamplePlugin", dependencies: [
.product(name: "Plug", package: "plug")
])
We also need to add the plugin to the products
array:
.product(name: "ExamplePlugin", type: .dynamic, targets: ["ExamplePlugin"])
Build and run the appin page link
We have everything we need to run the app. Let’s do that now:
swift run
If everything went well, you should see the following output:
Received pong event from plugin with text: hello world
You are ready to go now! You can use this as a starting point for your own projects. Have fun!