This Swift Package contains code generation macros and plugins to build a communication channel between Raycast's React extensions and Swift native code. Basically, it lets you import Swift code into your Raycast extension in order to:
Xcode.
Xcode needs to be installed in your system. We are hoping to stop requiring Xcode in the future and just require the existence of a Swift toolchain, alas we are not there yet. Please notice, you don't need to write the swift code in Xcode, you can use any other editor such as VSCode, Sublime, or Nova.
We built a sample extension using Swift here. Check it out to quickly get a feeling how things should be laid out.
To use Swift within Raycast:
Create (or fork) a Raycast extension.
If you don't know how, check out this guide.
Create a Swift executable target in the folder of your Raycast extension.
You can create the target in any of the following ways:
I like to put it in a This file is reserved for the Raycast plugins generating the TypeScript interface.using Xcode, or
File > New > Package...
to create a new Swift packageExecutable
swift
folder next to the existing src
folder.main.swift
file autogenerated by Xcode.
swift package
command in the terminal or VSCode.swift package init --type executable --name CustomName
in the Raycast extension folder.
In the previous command, CustomName
references the name of the Swift Package. You can name this whatever you want.
main.swift
file autogenerated by swift package
.
This file is reserved for the Raycast plugins generating the TypeScript interface.
You shouldn't have a
main.swift
file in your project nor a structure marked with@main
. These are reserved for the Swift-to-TypeScript plugins.
Modify the Package.swift
file to include the necessary macros and build plugins.
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "CustomName",
+ platforms: [
+ .macOS(.v12)
+ ],
+ dependencies: [
+ .package(url: "https://github.com/raycast/extensions-swift-tools", from: "1.0.4")
+ ],
targets: [
.executableTarget(
name: "CustomName",
+ dependencies: [
+ .product(name: "RaycastSwiftMacros", package: "extensions-swift-tools"),
+ .product(name: "RaycastSwiftPlugin", package: "extensions-swift-tools"),
+ .product(name: "RaycastTypeScriptPlugin", package: "extensions-swift-tools"),
+ ]
),
]
)
Import RaycastSwiftMacros
in your Swift file.
import RaycastSwiftMacros
Write global Swift functions and mark them with the @raycast
attribute.
Global functions marked with @raycast
are exported to TypeScript. These functions can have any number of parameters, and one or no return type. Exported functions can also be asynchronous (async
) or throw errors (throws
). The only restrictions are:
Decodable
.Encodable
(or be Void
or ()
).struct
s, class
es, or enum
s won't be exported.@raycast func greet(name: String, isFormal: Bool) -> String {
let address = isFormal ? "Mr/Ms" : ""
return "Hello \(address) \(name)! How are you?"
}
Custom types can be received as parameters or returned by the function. You just need to be sure the type conforms to Decodable
(for arguments) and Encodable
(for return types).
@raycast func pickColor(name: String) throws -> Color {
switch name {
case "red": Color(red: 1, green: 0, blue: 0)
case "green": Color(red: 0, green: 1, blue: 0)
case "blue": Color(red: 0, green: 0, blue: 1)
default: throw Color.Error.unsupportedColor
}
}
struct Color: Encodable {
let red: Float
let green: Float
let blue: Float
enum Error: Swift.Error {
case unsupportedColor
}
}