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
- [x] Type-safe, no more dictionaries and
SourceKitRepresentable
s. - [x] Optional XPC service, sandbox-friendly.
- [x] Subclassable interface.
- [x] 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
π Bug: Requires CocoaPods version >= 1.6.0 (Current pre-release, 1.6.0.rc.2)
π£ Important: The XPC service (and/or the
SylvesterXPC
module) 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.framework
SourceKittenFramework.framework
SWXMLHash.framework
Yams.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 |
Cursor Info | SKCursorInfo |
Documentation Info | SKDocInfo |
Editor Open | SKEditorOpen |
Editor Extract Text From Comment | SKEditorExtractTextFromComment |
Convert Markup To XML | SKConvertMarkupToXML |
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: Subclassing
SKBaseEntity
uses 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: Subclassing
SKEditorOpen
uses 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.