ActionKit contains an editor for visual scripting. The developer can provide functions the user can use as nodes, the input and output parameters and run the function defined by the user. It is also possible to add custom data types that conform to the ActionType
protocol.
- Open your Swift package in Xcode.
- Navigate to
File > Add Packages
. - Paste this URL into the search field:
https://github.com/david-swift/ActionKit-macOS
- Click on
Copy Dependency
. - Navigate to the
Package.swift
file. - In the
Package
initializer, underdependencies
, paste the dependency into the array.
- Open your Xcode project in Xcode.
- Navigate to
File > Add Packages
. - Paste this URL into the search field:
https://github.com/david-swift/ActionKit-macOS
- Click on
Add Package
.
You first need to define the function that the user can edit. You need to define a function identifier, name and description.
@State private var function = Function(
id: "user-function",
name: "User Function",
description: "A function defined by the user"
)
The following snippets show additional parameters for initializing a function.
If you want to provide data the user has access to, add input parameters.
// ...
input: [
.init("Name", type: String.self),
.init("Age", type: Double.self),
.init("Profession", type: Profession.self)
]
// ...
If you want to get output from the user, add output parameters.
// ...
output: [
.init("Adult", type: Bool.self)
]
// ...
If you do not provide functions for the user, function will add a default set with functions for text, numbers, booleans and the control flow. You can provide custom functions. Here is an example of a custom function group „My Functions“ containing a function for printing text into the console.
// ...
functions: [
.init("My Functions", icon: .init("my-functions") {
Function(
id: "print",
name: "Print",
description: "Print text",
input: [.init("Text", type: String.self)]
) {
print(input.first as? String ?? "-")
return []
}
}
]
// ...
You can also access the default set ([Folder<Function>.default
), parts of the default set (.numberFunctions
, .textFunctions
, .booleanFunctions
, .controlFlowFunctions
, .mergeFlowGroup
) or even parts of those sets and add them to your functions.
Now that you have defined the function the user can define, it’s time to use the FunctionEditor
in a SwiftUI view.
var body: some View {
FunctionEditor($function)
}
The following snippets show additional parameters for initializing the function editor.
You can change the size of the function editor.
// ...
zoom: 0.4
// ...
You can define whether the view for grabbing functions is visible or if it is a viewer instead of an editor.
// ...
functionsView: false
// ...
You can define an action that is executed when the user wants to open a node. If it is not defined, the button for opening a node is hidden.
// ...
openNode: { node in
// Open the node
}
// ...
It is also possible to add actions that are always visible in the editor as buttons. For example, you could add a run button or a button for deleting the selected nodes.
// ...
extraActions: {
TaggedView(tag: "run") {
// The run button
}
}
// ...
There are also some modifiers for the function editor.
Provide a Binding
that is always updated with the node’s selection. If you edit the value, the selection will change.
// ...
.observeSelection(value: $selection)
Provide a Binding
that is always updated when the node actions’ visibility changes. If you edit the value, the visibility will change.
// ...
.observeActions(value: $actionsVisibility)
Provide a Binding
that is always updated when the expansion of the functions view changes. If you edit the value, the expansion will change.
// ...
.observeExpandFunctions(value: $expandFunctions)
You can throw an error by providing a Binding
. If the wrapped value changes to another String?
value than nil
, the error is displayed in the editor.
// ...
.throwError(error: $error)
You can run the function defined by the user. The input values should match the parameters defined as the function’s input.
If everything works fine, you get the function’s output as [ActionType]
, else, the function throws an error.
let output = try function.run(input: ["Peter", 23, Profession.developer])
The following snippets show additional parameters for running a function.
Observe the function’s state by always getting the node’s index when the execution of a new node starts.
// ...
startedStep: { index in
// Do something
}
// ...
You might want to save a function so it stays the same even after restarting the app or add a function to a document. You can accomplish that with CodableFunction
, a wrapper around Function
that makes it conform to Codable
.
Information defined by the developer and that cannot be changed as for example the available functions have to be stored as static variables in a separate type conforming to the CodableFunctionInformation
protocol. This type is also used by the function’s encoder and decoder to encoder or decode an action type. As you as the developer know every available type in the functions (do not forget ControlFlow
), you can try to convert the type value to its real type and with that create an encoder and decoder. Here is an example. You can find a working example in the TestApp.
// ...
init(from decoder: Decoder) throws {
let decoder = try decoder.singleValueContainer()
if let value = try? decoder.decode(String.self) {
self.init(type: value)
} else if let value = try? decoder.decode(Double.self) {
self.init(type: value
} else if let value = try? decoder.decode(Bool.self) {
// ...
}
func encode(to encoder: Encoder) throws {
var encoder = encoder.singleValueContainer()
if let string = type as? String {
try encoder.encode(string)
} else if let double = type as? Double {
try encoder.encode(double)
} else if let boolean = type as? Bool {
// ...
}
// ...
- SFSafeSymbols licensed under the MIT license
- SwiftLintPlugin licensed under the MIT license
- ColibriComponents licensed under the MIT license
- The contributors
- SourceDocs used for generating the docs
- SwiftLint for checking whether code style conventions are violated
- AudioKit: File NodeEditor+Drawing.swift in the AudioKit/Flow GitHub repository
- The programming language Swift