Dynamic loading of Swift plug-ins for extensible architectures

Overview

Ubuntu macOS

PluginManager

Support for dynamic loading and management of plugins to extend hosting application functionality.

Overview

A multi-platform server-side Swift Infrastructure to support a plugin architecture that allows for dynamically extended functionality of hosting applications.

The PluginManager is implemented as an actor type which takes a plugin type factory as a generic parameter allowing for multiple simultaneous PluginManagers each supporting a specific plugin type.

A plugin type is defined in terms of the protocol it implements, a sample is available in swift-plugin-example-api.

A concrete implementation of a Plugin of that plugin type (there can be several concrete implementations for a given type) is available as a sample in swift-plugin-example.

A hosting application can provide an API that allows the plugin to access functionality from the hosting application, an example of such an API is defined as a protocol in swift-plugin-manager-example-api.

A given hosting application can load plugins and should implement the API that plugins can use, a sample hosting application is available as swift-plugin-manager-example which also can load the sample plugin.

The fundamental plugin protocol is available as a plugin package dependency swift-plugin.

Sample usage

Load all plugins from a given directory, create the factory for each plugin type to create an instance and call a function that uses the host application API:

  let pluginManager = try await PluginManager<PluginExampleAPIFactory>(withPath: validPath)

  for (_, plugin) in await pluginManager.plugins {
    var myInstance = plugin.factory.create()
    myInstance.setPluginManagerExampleAPI(PluginManagerExampleAPIProvider())
    print(myInstance.callFunctionThatUsesHostingApplicationAPI())
  }

Supported Platforms

PluginManager currently supports macOS and Linux with a Swift toolchain versin of at least 5.5 (as the PluginManager is implemented as an actor)

PluginManager uses and supports swift-log.

Getting Started

You just need to do a few things to add plugin capabilities to your application:

  1. Create an API protocol for the plugin (usually as a separate package, as multiple concrete plugins will depend on that)
  2. Create a concrete plugin implementation that implements that API and that includes a trivial factory class to create instances
  3. Add the Plugin Manager dependency to your hosting application and add code to load instances
  4. (optionally) Add an API protocol for the hostsing application so the plugin can use specific features there.

For point 1 and 2, see the sample projects linked above.

For point 3, to add PluginManager as dependency in your own project to add plugin capabilities, it's as simple as adding a dependencies clause to your Package.swift:

dependencies: [
    .package(url: "https://github.com/hassile/swift-plugin-manager.git")
]

and then add the dependency to your target:

        .executableTarget(
            name: "PluginManagerExample",
            dependencies: [
              .product(name: "PluginManager", package: "swift-plugin-manager")
            ]),

The easiest approach to learn is probably to play with the samples published above that are minimal in scope, so download and run the example:

mkdir plugin-test
cd plugin-test
git clone https://github.com/hassila/swift-plugin-manager-example
git clone https://github.com/hassila/swift-plugin-example
cd swift-plugin-example
swift build
cd ../swift-plugin-manager-example
swift run

Runtime warnings

A runtime warning will be issued when a plugin is loaded as the factory class will be implemented both in the hosting application and in the plugin that is loaded - the trivial transport class should be identical in both and the warning can be disregarded.

objc[21884]: Class _TtC16PluginExampleAPI23PluginExampleAPIFactory is implemented in both /Users/jocke/Library/Developer/Xcode/DerivedData/swift-plugin-manager-example-gpipkszbaeyszjgfyfslngejclgt/Build/Products/Debug/PluginManagerExample (0x100060e90) and /Users/jocke/GitHub/swift-plugin-example/.build/arm64-apple-macosx/debug/libPluginExample.dylib (0x1007cc108). One of the two will be used. Which one is undefined.

Related projects and usage notes

Loading of plugins is fundamentally unsafe from a security perspective as it allows for arbitrary code execution and no sandboxing is performed. This makes use of this plugin infrastructur suitable for environments and use cases where the user/operator installing plugins have full control of what's loaded.

This package was primarily put together with server-swide Swift usage in mind. Similar functionality is available in Foundation in Bundle with some caveats - it seems to require using Objective-C bridging headers for a "pure Swift" version and for e.g. Linux some functionality like principalClass isn't yet implemented. That being said, if you are building something only on Apple platforms and can accept a dependency on Foundation, it is a very reasonable alternative solution to consider and has a lot of additional features like co-packaging of resources.

This package does not depend on neither Foundation nor Objective-C facilities and works on both macOS and Linux.

Documentation is supplied in docc format, easiest is to open the package in xcode and build documentation, alternatively run docc from command line.

Future directions

Add a proper github pipeline with autogenerating of html for GH pages in the future to make docs available without downloading package.

Autogeneration of plugin and hosting API:s semvers when doinga a release using Swift Package Managers diagnose-api-breaking-changes (previously named experimental-api-diff) feature. Then we can expose the semver as another known entry point in the module and check that it is compatible during loading.

Feedback and PR:s are welcome.

You might also like...
Asynchronous image loading framework.
Asynchronous image loading framework.

YYWebImage YYWebImage is an asynchronous image loading framework (a component of YYKit). It was created as an improved replacement for SDWebImage, PIN

Twitter Image Pipeline is a robust and performant image loading and caching framework for iOS clients

Twitter Image Pipeline (a.k.a. TIP) Background The Twitter Image Pipeline is a streamlined framework for fetching and storing images in an application

EbImagesSwiftUI - SDWebImageSwiftUI - a SwiftUI image loading framework, which based on SDWebImage
EbImagesSwiftUI - SDWebImageSwiftUI - a SwiftUI image loading framework, which based on SDWebImage

SDWebImageSwiftUI What's for SDWebImageSwiftUI is a SwiftUI image loading framew

SwiftUI  project to show ActivityIndicator above Image while loading
SwiftUI project to show ActivityIndicator above Image while loading

ImageWithActivityIndicatorDemo SwiftUI project to show ActivityIndicator above Image while loading ImageWithActivityIndicatorDemo is a demo app that s

SwiftUI view that download and display image from URL and displaying Activity Indicator while loading .

ViewWithActivityIndicator ViewWithActivityIndicator is a SwiftUI view that download and display image from URL and displaying Activity Indicator while

DGLoading - A loading view that is shown at center of the current view
DGLoading - A loading view that is shown at center of the current view

DGLoading A loading view that is shown at center of the current view. Requiremen

ImageView - Component for loading and displaying different images aka SVG/PNG/JPG/JPEG

ImageView Component that loads and displays images(.svg/.png/.jpg/.jpeg) form as

LCWebImage - An asynchronous image loading framework based on AFNetworking.

LCWebImage is an asynchronous image loading framework based on AFNetworking, which supports memory and disk caching, and provides functions such as custom caching, custom image decoding, and custom network configuration.

Contentful.swift : Swift Delivery SDK for Contentful
Contentful.swift : Swift Delivery SDK for Contentful

contentful.swift - Swift Delivery SDK for Contentful Swift SDK for the Contentfu

Comments
  • use `directoryEntry` as an  `UnsafeMutableRawPointer` instead of `withUnsafePointer(to:)`

    use `directoryEntry` as an `UnsafeMutableRawPointer` instead of `withUnsafePointer(to:)`

    https://github.com/hassila/swift-plugin-manager/blob/a2902135638834912aebb81613d7b33aab71f529/Sources/PluginManager/FilePathDirectoryView.swift#L63

    the documentation for readdir(_:) says not to take d_name as an lvalue. it also says not to rely on static type information about dirent, because the string may overrun those bounds.

    this probably isn’t an issue here, since the OS controls the layout of the dirent structure, so FilePath.init(platformString:) is unlikely to read anything it shouldn’t. but this line should probably be rewritten as a cast to an UnsafeMutableRawPointer and then manual memory bind so that the compiler is aware of this.

    the way it is written, the compiler could materialize an instance of dirent somewhere it is unsafe to read outside the bounds of, so that it can write it back to its intended destination later.

    opened by kelvin13 1
Releases(0.1.0)
Owner
Joakim Hassila
Joakim Hassila
CocoaPods dynamic framework issue

CocoaPods dynamic framework issue This small PoC should show an issue I experien

Alexander Weiß 0 Dec 29, 2021
A pure Swift high-performance asynchronous image loading framework. SwiftUI supported.

Longinus Longinus is a pure-Swift high-performance asynchronous web image loading,caching,editing framework. It was learned from Objective-C web image

Qitao Yang 290 Dec 17, 2022
A simple mesh viewer for MacOS based on Swift and Metal and using Assimp for loading meshes

Metal Mesh Viewer A simple triangle mesh viewer for MacOS This application is a simple (triangle) mesh viewer that should be capable of rendering even

J. Andreas Bærentzen 0 Dec 13, 2021
APNGKit is a high performance framework for loading and displaying APNG images in iOS and macOS.

APNGKit is a high performance framework for loading and displaying APNG images in iOS and macOS. It's built on top of a modified version of libpng wit

Wei Wang 2.1k Dec 30, 2022
Image loading system

Image Loading System Nuke ILS provides an efficient way to download and display images in your app. It's easy to learn and use thanks to a clear and c

Alexander Grebenyuk 7k Dec 31, 2022
Lazy image loading for SwiftUI

A missing piece in SwiftUI that provides lazy image loading.

Alexander Grebenyuk 9 Dec 26, 2022
AsyncImageExample An example project for AsyncImage. Loading images in SwiftUI article.

AsyncImageExample An example project for AsyncImage. Loading images in SwiftUI article. Note: The project works in Xcode 13.0 beta (13A5154h).

Artem Novichkov 4 Dec 31, 2021
SwiftUI Image loading and Animation framework powered by SDWebImage

SDWebImageSwiftUI What's for SDWebImageSwiftUI is a SwiftUI image loading framework, which based on SDWebImage. It brings all your favorite features f

null 1.6k Jan 6, 2023
Lightweight and customisable async image loading in SwiftUI. Supports on-disk storage, placeholders and more!

Asyncrounously download and display images in Swift UI. Supports progress indicators, placeholders and image transitions. RemoteImageView Asyncrounous

Callum Trounce 192 Dec 7, 2022
Advanced framework for loading, caching, processing, displaying and preheating images.

Advanced framework for loading, caching, processing, displaying and preheating images. This framework is no longer maintained. Programming in Swift? C

Alexander Grebenyuk 1.2k Dec 23, 2022