Sylvester πΌ
A type-safe, XPC-available SourceKitten (SourceKit) interface with some sugar.
Looking for...
- A Floating Action Button for macOS? Check out Fab.
ποΈ . - An Expanding Bubble Text Field for macOS? Check out BubbleTextField
π¬ . - An integrated spotlight-based onboarding and help library for macOS? Check out Enlighten
π‘ .
Features
- Type-safe, no more dictionaries and
SourceKitRepresentables. - Optional XPC service, sandbox-friendly.
- Subclassable interface.
- Comprehensive test suite.
Requirements
- macOS 10.12+
Modules
The Sylvester framework has two build configurations that differ in their method of communicating with SourceKit:
Sylvesterβ Communicates directly from within the embedding application or process. This module is not sandbox-friendly.SylvesterXPCβ Communicates through a XPC service. This module provides privilege separation, enhanced stability, and is sandbox-friendly.
π Note: The XPC service itself cannot be sandboxed (due to inherent dependencies: xcrun, xcodebuild, sourcekitd), and requires an additional code signing step.
Installation
Sylvester is available for installation using Carthage or CocoaPods.
Using CocoaPods
π£ Important: The XPC service (and/or theSylvesterXPCmodule) is currently unavailable for CocoaPods installations.
pod "Sylvester"Using Carthage
github "chriszielinski/Sylvester"Dependencies
Sylvester/SylvesterXPC depends on the following frameworks/libraries, so ensure they are also embedded in the Embed Frameworks phase:
SylvesterCommon.frameworkSourceKittenFramework.frameworkSWXMLHash.frameworkYams.framework
Code Signing
If you decide to use the SylvesterXPC module, you will need to add a Run Script phase before embedding the SylvesterXPC.framework (i.e. before the Embed Frameworks phase). Ensure the shell launch path is /bin/sh (default). Then for Carthage installations, execute the code_sign_carthage.sh shell script in the repository's Scripts directory.
"$SRCROOT/Carthage/Checkouts/Sylvester/Scripts/code_sign_carthage.sh"For other installations, modify the script's paths as neccessary.
Supported Requests
| Request | Class |
|---|---|
| Code Completion | SKCodeCompletion |
| Code Completion Session | SKCodeCompletionSession |
| Documentation Info | SKDocInfo |
| Editor Open | SKEditorOpen |
| Module Info | SKModule |
| Swift Documentation | SKSwiftDocs |
| Syntax Map | SKSyntaxMap |
| Custom YAML | SKYAMLRequest |
Other Fun Things
| Type | Method |
|---|---|
| XCRun | SylvesterInterface.shared.xcRun(arguments:) |
| XcodeBuild | SylvesterInterface.shared.xcodeBuild(arguments:currentDirectoryURL:) |
| Bash Command | SylvesterInterface.shared.executeBash(command:currentDirectoryURL:) |
| Launch Subprocess | SylvesterInterface.shared.launch(subprocess:) |
Subclassing
Most of the standard requests are concrete subclasses of beautiful generic classes. Fancy your own subclass? No problem, it might be possible.
SKSubstructure, SKEntity
Also known as SKBaseSubstructure (or SKBaseEntity), a common culprit.
π Note: SubclassingSKBaseEntityuses similar syntax.
final class BetterSubstructureSubclass: SKBaseSubstructure, SKFinalSubclass {
var iAmAnImportantProperty: String = "πΆββοΈ"
public override func decodeChildren(from container: DecodingContainer) throws -> [SKBaseSubstructure]? {
return try decodeChildren(BetterSubstructureSubclass.self, from: container)
}
/// The default iterator for `SKChildren` does a pre-order (NLR) depth-first search (DFS) traversal; however, if you want something else, for instance:
class FunctionSubstructureIterator<Substructure: BetterSubstructureSubclass>: SKPreOrderDFSIterator<Substructure> {
override func next() -> Substructure? {
guard let nextSubstructure = super.next()
else { return nil }
if nextSubstructure.isFunction {
return nextSubstructure
} else {
return next()
}
}
}
override class func iteratorClass<Substructure: BetterSubstructureSubclass>() -> SKPreOrderDFSIterator<Substructure>.Type {
return FunctionSubstructureIterator.self
}
}SKEditorOpen, SKSwiftDocs
An example of a SKSwiftDocs subclass utilizing the BetterSubstructureSubclass declared above:
π Note: SubclassingSKEditorOpenuses identical syntax, except it inherits fromSKGenericEditorOpen.
class BetterSwiftDocs: SKGenericSwiftDocs<BetterSubstructureSubclass> {
var mySuperCoolProperty: String = "π"
}SKModule
An example of a SKModule subclass utilizing the BetterSwiftDocs and BetterSubstructureSubclass classes declared above:
class BetterModule: SKGenericModule<BetterSubstructureSubclass, BetterSwiftDocs> {}Documentation
You can explore the docs here.
// ToDo:
- Add support for other requests.
Community
- Found a bug? Open an issue.
- Feature idea?
Open an issue.Do it yourself & PR when doneπ (or you can open an issueπ ). - Want to contribute? Submit a pull request.
Contributors
- Chris Zielinski β Original author.
Frameworks & Libraries
Sylvester depends on the wonderful contributions of the Swift community, namely:
- jpsim/SourceKitten β An adorable little framework and command line tool for interacting with SourceKit.
- macmade/AtomicKit β Concurrency made simple in Swift.
- groue/GRMustache.swift β Flexible Mustache templates for Swift.
- realm/jazzy β Soulful docs for Swift & Objective-C.
- realm/SwiftLint β A tool to enforce Swift style and conventions.
License
Sylvester is available under the MIT license, see the LICENSE file for more information.


