Plug is a multi-platform plugin architecture micro-framework and plugin manager written in pure Swift. It allows to define rules that has to be met to activate plugins at given plugin point.
Requirements
Development
Project uses following tools for development
Installation
To get started with the Plug you first have to decide how you will integrate it with your project. Plug supports following tools:
Cocoapods
To install Plug using Cocoapods go through following steps:
- Add the following entry in your Podfile:
pod 'Plug'
- Then run
pod install
.
Carthage
To install Plug using Carthage go through following steps:
- Add the following entry to your Cartfile
github "lewandowskit93/Plug"
- Then run
carthage update
Swift Package Manager
To install Plug using Swift Package Manager go through following steps:
- Add following package dependency in you Package.swift
.package(url: "https://github.com/lewandowskit93/Plug.git", from: "0.4.0")
- Add following target dependency in your Package.swift
dependencies: ["Plug"])
For instance this is how it might look like:
import PackageDescription
let package = Package(
name: "YourLibrary",
products: [
.library(
name: "YourLibrary",
targets: ["YourLibrary"])
],
dependencies: [
.package(url: "https://github.com/lewandowskit93/Plug.git", from: "0.4.0")
],
targets: [
.target(
name: "YourLibrary",
dependencies: ["Plug"])
]
)
Overview
Here is a quick overview of functionalities and concepts used in Plug.
Plugin
Plugin is anything that implements PPlugin protocol. You can define what your plugins are and what they do.
As an example here you can see ViewPlugin which can return single SwiftUI view:
public final class ViewPlugin<V: View>: PPlugin {
private let builder: () -> V
public init(builder: @escaping () -> V) {
self.builder = builder
}
public var view: some View {
return builder()
}
}
RuleResolvingContext
RuleResolvingContext is anything that implements PRuleResolvingContext. Everything in the Plug is generic over PRuleResolvingContext. It can provide additional information to decide if the plugins are enabled.
Rule
Rule decides whether plugins should be returned or not depending on the context. You can define your own rules by implementing PRule protocol. There are a few rules available for you: AtomRule, EnabledRule, DisabledRule, InvertedRule, AllOfRule, AnyOfRule, NoneOfRule, AnyRule.
PluginPoint
PluginPoint defines a single slot to which plugins can be attached. Single plugin point can have multiple plugins and rules that describes them. It has a hierarchical structure meaning that a plugin point can have children plugin points. The rules applied to a plugin point are also applied to it's children. Plugin points can be built with PluginPointBuilder
DSL
Plug defines some operators and DSL to shorten building of plugin points. Available operators are:
- Adding plugin with operator: Builder <+ Plugin
- Removign plugin with operator: Builder <- Plugin
- Adding rule with operator: Builder §+ Rule
- Removing rule with operator: Builder §- Rule
- Adding child with operator: Builder |+ PluginPoint
- Removing child with operator: Builder |- PluginPoint
- Finalize building with operator: Builder^
There are also operators available for rules building:
- Inverting a rule: !AnyRule
- AllOfRule: &&[AnyRule]
- NoneOfRule: ~~[AnyRule]
- AnyOfRule: ||[AnyRule]
Example
This is an example definition of plugin point which allows two plugin features to exist if they are enabled in FooContext.
var pluginPoint = PluginPointBuilder()
.add(child: PluginPointBuilder()
.add(plugin: pluginFactory.feature1Plugin())
.add(rule: FeatureEnabledRule(id: "feature_1").any())
.build()
)
.add(child: PluginPointBuilder()
.add(plugin: pluginFactory.feature2Plugin())
.add(rule: FeatureEnabledRule(id: "feature_2").any())
.build()
).build()
var availablePlugins = pluginPoint.getAvailablePlugins(context: FooContext())
The same plugin point could be defined using operators as follows:
var pluginPoint = (
PluginPointBuilder()
|+ (
PluginPointBuilder()
<+ pluginFactory.feature1Plugin()
§+ FeatureEnabledRule(id: "feature_1").any()
)^
|+ (
PluginPointBuilder()
<+ pluginFactory.feature2Plugin()
§+ FeatureEnabledRule(id: "feature_2").any()
)^
)^
var availablePlugins = pluginPoint.getAvailablePlugins(context: FooContext())
or using DSL as follows:
var pluginPoint = PluginPoint {
child {
PluginPoint {
plugin(contextType: Context.self) { pluginFactory.feature1Plugin() }
rule(pluginType: Plugin.self) { FeatureEnabledRule(id: "feature_1").any() }
}
}
child {
PluginPoint {
plugin(contextType: Context.self) { pluginFactory.feature2Plugin() }
rule(pluginType: Plugin.self) { FeatureEnabledRule(id: "feature_2").any() }
}
}
}
For more detailed example please see the source code.
Contribution
Project is created and maintained by Tomasz Lewandowski.
If you created some new feature or fixed a bug you can create a pull request. Please feel free to submit your feature requests if you have any.
License
Plug is released under an MIT license. See License.md for more information.