Swift-OPA is a Swift package for evaluating OPA IR Plans compiled from Rego policies.
Rego is a declarative language for expressing policy over structured data. A common use of Rego is for defining authorization policy. Swift-OPA allows for in-process evaluation of compiled Rego within a Swift-based service or application.
let package = Package(
// name, platforms, products, etc.
dependencies: [
// other dependencies
.package(url: "https://github.com/open-policy-agent/swift-opa", branch: "main"),
],
targets: [
.[executable|library]Target(name: "<target-name>", dependencies: [
// other dependencies
.product(name:"SwiftOPA", package: "swift-opa"),
]),
// other targets
]
)
The main entry point for policy evaluation is the OPA.Engine
. An engine can evaluate policies packaged in one or more Bundles
.
An OPA.Engine
can be initialized with an on-disk bundle using its constructor: init(bundlePaths:)
:
import Rego
import Foundation
let path = "path/to/bundle"
let bundlePath = OPA.Engine.BundlePath(name: "policyBundle", url: URL(fileURLWithPath: path))
var regoEngine = OPA.Engine(bundlePaths: [bundlePath])
// Prepare does as much pre-processing as possible to get ready to evaluate queries.
// This only needs to be done once when loading the engine and after updating it.
// PreparedQuery's can be re-used.
let rawQuery = "data.policy.main.is_valid"
let preparedQuery = try await regoEngine.prepareForEvaluation(query: rawQuery)
Policies often expect an input
document to be passed in. This can be parsed from JSON data, for example:
import AST
// ...
let rawInput = #"{"favorite_fruit": "apple"}"#.data(using: .utf8)!
let inputDocument = try AST.RegoValue(jsonData: rawInput)
Evaluation is performed with the prepared query. We used data.policy.main.is_valid
above, which matches this simple policy:
package policy.main
default is_valid := false
is_valid := true if {
input.favorite_fruit == "apple"
}
Putting it all together, we can evaluate our query and interpret the results, in this case just printing them:
let resultSet = try await preparedQuery.evaluate(
input: inputDocument
)
print(try resultSet.jsonString)
- Builtins are the standard and custom functions available to Rego (e.g.
count
,concat
, etc.). The engine comes with the default OPA builtins enabled. - You can optionally supply an OPA
capabilities.json
(from an OPA release) upon theOPA.Engine
init viacapabilities: .path(...)
. DuringprepareForEvaluation
, the engine checks that all builtins required by your compiled policies are present in the capabilities file (including matching signatures). If no capabilities are specified, validation is skipped and execution proceeds normally. - The engine also checks that every required builtin by the compiled policy is present in the Swift implementation, matched by its name (default or custom builtin). This check runs independently of capabilities validation. Signature correctness is enforced by OPA’s capabilities (if specified); Swift builtin closures validate arguments only at runtime.
One can also register custom builtins when creating the OPA.Engine
, in addition to the default OPA builtins provided by swift-opa
.
Conflicts with default builtin names are validated during prepareForEvaluation(query:)
.
If capabilities are provided, the custom builtins are validated against the specified capabilities file.
import AST
let customBuiltins: [String: Builtin] = [
"my.slugify": { ctx, args in
// Example: expect a single string argument and return a slug
guard args.count == 1, case let .string(s) = args[0] else {
throw BuiltinError.argumentCountMismatch(got: args.count, want: 1)
}
let slug = s.lowercased().replacingOccurrences(of: " ", with: "-")
return .string(slug)
}
]
var engine = OPA.Engine(
bundlePaths: [bundlePath],
// To enable builtin validation, provide a `capabilities.json`. Without it, builtins are not checked against capabilities.
// capabilities: .path(capabilitiesURL),
customBuiltins: customBuiltins
)
// Throws if a custom builtin name conflicts with a default or a builtin required by the compiled policy is not present.
// If capabilities are specified, this throws if a capabilities validation error against the builtins occurs.
let preparedQuery = try await engine.prepareForEvaluation(query: "<some_query>")