London Escorts sunderland escorts 1v1.lol unblocked yohoho 76 https://www.symbaloo.com/mix/yohoho?lang=EN yohoho https://www.symbaloo.com/mix/agariounblockedpvp https://yohoho-io.app/ https://www.symbaloo.com/mix/agariounblockedschool1?lang=EN
-7.3 C
New York
Sunday, February 2, 2025

Constructing and loading dynamic libraries at runtime in Swift


Why ought to we make a plugin system?

Within the modules and hooks article I used to be writing about how modules (plugins) can work collectively through the use of numerous invocation factors and hooks. The one downside with that method is which you can’t actually activate or off modules on-the-fly, since we normally construct our apps in a static means.

A great plugin system ought to allow us to alter the habits of our code at runtime. WordPress plugins are extraordinarily profitable, as a result of you possibly can add further performance to the CMS with out recompiling or altering the core. Outdoors the Apple ecosystem, there’s a enormous world that would benefit from this idea. Sure, I’m speaking about Swift on the server and backend purposes.

My thought right here is to construct an open-source modular CMS that may be quick, secure and extensible by way of plugins. Luckily now we’ve this wonderful type-safe programming language that we will use. Swift is quick and dependable, it’s the good alternative for constructing backend apps on the long run. ✅

On this article I wish to present you a the way to construct a dynamic plugin system. The entire idea relies on Lopdo‘s GitHub repositories, he did fairly an incredible job implementing it. Thanks very a lot for exhibiting me the way to use dlopen and different comparable capabilities. 🙏

The magic of dynamic linking

Handmade iOS frameworks are normally bundled with the appliance itself, you possibly can be taught just about all the things a few framework if you realize some command line instruments. This time we’re solely going to give attention to static and dynamic linking. By default Swift package deal dependencies are linked statically into your software, however you possibly can change this in case you outline a dynamic library product.

First we’re going to create a shared plugin interface containing the plugin API as a protocol.


import PackageDescription

let package deal = Package deal(
    title: "PluginInterface",
    merchandise: [
        .library(name: "PluginInterface", type: .dynamic, targets: ["PluginInterface"]),
    ],
    targets: [
        .target(name: "PluginInterface", dependencies: []),
    ]
)

This dynamic PluginInterface package deal can produce a .dylib or .so file, quickly there shall be a .dll model as effectively, based mostly on the working system. All of the code bundled into this dynamic library might be shared between different purposes. Let’s make a easy protocol.

public protocol PluginInterface {

    func foo() -> String
}

Since we’re going to load the plugin dynamically we are going to want one thing like a builder to assemble the specified object. We will use a brand new summary class for this goal.

open class PluginBuilder {
    
    public init() {}

    open func construct() -> PluginInterface {
        fatalError("It's a must to override this methodology.")
    }
}

That is our dynamic plugin interface library, be at liberty to push this to a distant repository.

Constructing a dynamic plugin

For the sake of simplicity we’ll construct a module known as PluginA, that is the manifest file:


import PackageDescription

let package deal = Package deal(
    title: "PluginA",
    merchandise: [
        .library(name: "PluginA", type: .dynamic, targets: ["PluginA"]),
    ],
    dependencies: [
        .package(url: "path/to/the/PluginInterface/repository", from: "1.0.0"),
    ],
    targets: [
        .target(name: "PluginA", dependencies: [
            .product(name: "PluginInterface", package: "PluginInterface")
        ]),
    ]
)

The plugin implementation will in fact implement the PluginInterface protocol. You possibly can lengthen this protocol based mostly in your wants, you too can use different frameworks as dependencies.

import PluginInterface

struct PluginA: PluginInterface {

    func foo() -> String {
        return "A"
    }
}

We’ve got to subclass the PluginBuilder class and return our plugin implementation. We’re going to use the @_cdecl attributed create perform to entry our plugin builder from the core app. This Swift attribute tells the compiler to avoid wasting our perform below the “createPlugin” image title.

import PluginInterface

@_cdecl("createPlugin")
public func createPlugin() -> UnsafeMutableRawPointer {
    return Unmanaged.passRetained(PluginABuilder()).toOpaque()
}

ultimate class PluginABuilder: PluginBuilder {

    override func construct() -> PluginInterface {
        PluginA()
    }
}

We will construct the plugin utilizing the command line, simply run swift construct within the undertaking folder. Now yow will discover the dylib file below the binary path, be at liberty to run swift construct --show-bin-path, this can output the required folder. We are going to want each .dylib recordsdata for later use.

Loading the plugin at runtime

The core software will even use the plugin interface as a dependency.


import PackageDescription

let package deal = Package deal(
    title: "CoreApp",
    dependencies: [
        .package(url: "path/to/the/PluginInterface/repository", from: "1.0.0"),
    ],
    targets: [
        .target(name: "CoreApp", dependencies: [
            .product(name: "PluginInterface", package: "PluginInterface")
        ]),
    ]
)

That is an executable goal, so we will place the loading logic to the important.swift file.

import Basis
import PluginInterface

typealias InitFunction = @conference(c) () -> UnsafeMutableRawPointer

func plugin(at path: String) -> PluginInterface {
    let openRes = dlopen(path, RTLD_NOW|RTLD_LOCAL)
    if openRes != nil {
        defer {
            dlclose(openRes)
        }

        let symbolName = "createPlugin"
        let sym = dlsym(openRes, symbolName)

        if sym != nil {
            let f: InitFunction = unsafeBitCast(sym, to: InitFunction.self)
            let pluginPointer = f()
            let builder = Unmanaged<PluginBuilder>.fromOpaque(pluginPointer).takeRetainedValue()
            return builder.construct()
        }
        else {
            fatalError("error loading lib: image (symbolName) not discovered, path: (path)")
        }
    }
    else {
        if let err = dlerror() {
            fatalError("error opening lib: (String(format: "%s", err)), path: (path)")
        }
        else {
            fatalError("error opening lib: unknown error, path: (path)")
        }
    }
}

let myPlugin = plugin(at: "path/to/my/plugin/libPluginA.dylib")
let a = myPlugin.foo()
print(a)

We will use the dlopen perform to open the dynamic library file, then we are attempting to get the createPlugin image utilizing the dlsym methodology. If we’ve a pointer we nonetheless must solid that into a legitimate PluginBuilder object, then we will name the construct methodology and return the plugin interface.

Operating the app

Now in case you attempt to run this software utilizing Xcode you will get a warning like this:

Class _TtC15PluginInterface13PluginBuilder is carried out in each… One of many two shall be used. Which one is undefined.

That is associated to an outdated bug, however luckily that’s already resolved. This time Xcode is the dangerous man, since it’s attempting to hyperlink all the things as a static dependency. Now in case you construct the appliance by way of the command line (swift construct) and place the next recordsdata in the identical folder:

  • CoreApp
  • libPluginA.dylib
  • libPluginInterface.dylib

You possibly can run the appliance ./CoreApp with out additional points. The app will print out A with out the warning message, for the reason that Swift package deal supervisor is recognizing that you simply wish to hyperlink the libPluginInterface framework as a dynamic framework, so it will not be embedded into the appliance binary. After all it’s a must to arrange the precise plugin path within the core software.

Related Articles

Social Media Auto Publish Powered By : XYZScripts.com