Swiftpack.co - jamf/Subprocess as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by jamf.
jamf/Subprocess 3.0.2
Swift library for macOS providing interfaces for both synchronous and asynchronous process execution
⭐️ 26
🕓 9 weeks ago
macOS
.package(url: "https://github.com/jamf/Subprocess.git", from: "3.0.2")

Subprocess

License Build CocoaPods Platform Language Carthage compatible SwiftPM compatible Documentation

Subprocess is a Swift library for macOS providing interfaces for both synchronous and asynchronous process execution. SubprocessMocks can be used in unit tests for quick and highly customizable mocking and verification of Subprocess usage.

Full Documentation

Usage

Subprocess Class

The Subprocess class can be used for command execution.

Command Input

Input for data
let inputData = Data("hello world".utf8)
let data = try await Subprocess.data(for: ["/usr/bin/grep", "hello"], standardInput: inputData)
Input for text
let data = try await Subprocess.data(for: ["/usr/bin/grep", "hello"], standardInput: "hello world")
Input for file URL
let data = try await Subprocess.data(for: ["/usr/bin/grep", "foo"], standardInput: URL(filePath: "/path/to/input/file"))

Command Output

Output as Data
let data = try await Subprocess.data(for: ["/usr/bin/sw_vers"])
Output as String
let string = try await Subprocess.string(for: ["/usr/bin/sw_vers"])
Output as decodable object from JSON
struct LogMessage: Codable {
    var subsystem: String
    var category: String
    var machTimestamp: UInt64
}

let result: [LogMessage] = try await Subprocess.value(for: ["/usr/bin/log", "show", "--style", "json", "--last", "30s"], decoder: JSONDecoder())
Output as decodable object from Property List
struct SystemVersion: Codable {
    enum CodingKeys: String, CodingKey {
        case version = "ProductVersion"
    }
    var version: String
}

let result: SystemVersion = try await Subprocess.value(for: ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"], decoder: PropertyListDecoder())
Output mapped to other type
let enabled = try await Subprocess(["/usr/bin/csrutil", "status"]).run().standardOutput.lines.first(where: { $0.contains("enabled") } ) != nil
Output options
let errorText = try await Subprocess.string(for: ["/usr/bin/cat", "/non/existent/file.txt"], options: .returnStandardError)
let outputText = try await Subprocess.string(for: ["/usr/bin/sw_vers"])

async let (standardOutput, standardError, _) = try Subprocess(["/usr/bin/csrutil", "status"]).run()
let combinedOutput = try await [standardOutput.string(), standardError.string()]
Handling output as it is read
let (stream, input) = {
    var input: AsyncStream<UInt8>.Continuation!
    let stream: AsyncStream<UInt8> = AsyncStream { continuation in
        input = continuation
    }

    return (stream, input!)
}()

let subprocess = Subprocess(["/bin/cat"])
let (standardOutput, _, waitForExit) = try subprocess.run(standardInput: stream)

input.yield("hello\n")

Task {
    for await line in standardOutput.lines {
        switch line {
        case "hello":
            input.yield("world\n")
        case "world":
            input.yield("and\nuniverse")
            input.finish()
        case "universe":
            await waitForExit()
            break
        default:
            continue
        }
    }
}
Handling output on termination
let process = Subprocess(["/usr/bin/csrutil", "status"])
let (standardOutput, standardError, waitForExit) = try process.run()
async let (stdout, stderr) = (standardOutput, standardError)
let combinedOutput = await [stdout.data(), stderr.data()]

await waitForExit()

if process.exitCode == 0 {
    // Do something with output data
} else {
    // Handle failure
}
Closure based callbacks
let command: [String] = ...
let process = Subprocess(command)

// The outputHandler and errorHandler are invoked serially
try process.launch(outputHandler: { data in
    // Handle new data read from stdout
}, errorHandler: { data in
    // Handle new data read from stderr
}, terminationHandler: { process in
    // Handle process termination, all scheduled calls to
    // the outputHandler and errorHandler are guaranteed to
    // have completed.
})
Handing output on termination with a closure
let command: [String] = ...
let process = Subprocess(command)

try process.launch { (process, outputData, errorData) in
    if process.exitCode == 0 {
        // Do something with output data
    } else {
        // Handle failure
    }

Installation

SwiftPM

let package = Package(
    // name, platforms, products, etc.
    dependencies: [
        // other dependencies
        .package(url: "https://github.com/jamf/Subprocess.git", .upToNextMajor(from: "3.0.0")),
    ],
    targets: [
        .target(name: "<target>",
        dependencies: [
            // other dependencies
            .product(name: "Subprocess"),
        ]),
        // other targets
    ]
)

Cocoapods

pod 'Subprocess'

Carthage

github 'jamf/Subprocess'

GitHub

link
Stars: 26
Last commit: 2 days ago
Advertisement: IndiePitcher.com - Cold Email Software for Startups

Related Packages

Release Notes

Update exitedWithNonZeroStatus error
2 years ago

Changed

  • Breaking: added the output of the command to Shell's exception exitedWithNonZeroStatus error to better conform to objc interop and NSError
  • Updated minimum deployment target to macOS 10.13.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics