2018-12-27 22:24:20 +00:00
|
|
|
//
|
|
|
|
// MasAppLibrary.swift
|
|
|
|
// MasKit
|
|
|
|
//
|
|
|
|
// Created by Ben Chatelain on 12/27/18.
|
|
|
|
// Copyright © 2018 mas-cli. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import CommerceKit
|
|
|
|
|
|
|
|
/// Utility for managing installed apps.
|
|
|
|
public class MasAppLibrary: AppLibrary {
|
|
|
|
/// CommerceKit's singleton manager of installed software.
|
|
|
|
private let softwareMap = CKSoftwareMap.shared()
|
|
|
|
|
2018-12-28 22:08:31 +00:00
|
|
|
/// Array of installed software products.
|
|
|
|
public lazy var installedApps: [SoftwareProduct] = {
|
|
|
|
var appList = [SoftwareProduct]()
|
|
|
|
guard let products = softwareMap.allProducts()
|
|
|
|
else { return appList }
|
|
|
|
appList.append(contentsOf: products)
|
|
|
|
return products
|
|
|
|
}()
|
|
|
|
|
2018-12-27 22:24:20 +00:00
|
|
|
public init() {}
|
|
|
|
|
2018-12-28 22:08:31 +00:00
|
|
|
/// Finds an app using a bundle identifier.
|
2018-12-27 22:24:20 +00:00
|
|
|
///
|
2018-12-28 22:08:31 +00:00
|
|
|
/// - Parameter bundleId: Bundle identifier of app.
|
2018-12-27 22:24:20 +00:00
|
|
|
/// - Returns: Software Product of app if found; nil otherwise.
|
2018-12-28 22:08:31 +00:00
|
|
|
public func installedApp(forBundleId bundleId: String) -> SoftwareProduct? {
|
|
|
|
return softwareMap.product(forBundleIdentifier: bundleId)
|
2018-12-27 22:24:20 +00:00
|
|
|
}
|
2018-12-27 23:07:35 +00:00
|
|
|
|
|
|
|
/// Uninstalls an app.
|
|
|
|
///
|
|
|
|
/// - Parameter app: App to be removed.
|
|
|
|
/// - Throws: Error if there is a problem.
|
|
|
|
public func uninstallApp(app: SoftwareProduct) throws {
|
|
|
|
let status = trash(path: app.bundlePath)
|
|
|
|
if !status {
|
|
|
|
throw MASError.uninstallFailed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Runs the trash command in another process. Relies on the "trash" command
|
|
|
|
/// from Homebrew. Trash requires el_capitan or higher for core bottles:
|
|
|
|
/// https://github.com/Homebrew/homebrew-core/blob/master/Formula/trash.rb
|
|
|
|
///
|
|
|
|
/// - Parameter path: Absolute path to the application bundle to uninstall.
|
|
|
|
/// - Returns: true on success; fail on error
|
|
|
|
func trash(path: String) -> Bool {
|
|
|
|
let binaryPath = "/usr/local/bin/trash"
|
|
|
|
let process = Process()
|
|
|
|
let stdout = Pipe()
|
|
|
|
let stderr = Pipe()
|
|
|
|
|
|
|
|
process.standardOutput = stdout
|
|
|
|
process.standardError = stderr
|
|
|
|
process.arguments = [path]
|
|
|
|
|
|
|
|
if #available(OSX 10.13, *) {
|
|
|
|
process.executableURL = URL(fileURLWithPath: binaryPath)
|
|
|
|
do {
|
|
|
|
try process.run()
|
|
|
|
} catch {
|
|
|
|
printError("Unable to launch trash command")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
process.launchPath = binaryPath
|
|
|
|
process.launch()
|
|
|
|
}
|
|
|
|
|
|
|
|
process.waitUntilExit()
|
|
|
|
|
|
|
|
if process.terminationStatus == 0 {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
let reason = process.terminationReason
|
|
|
|
let output = stderr.fileHandleForReading.readDataToEndOfFile()
|
|
|
|
printError("Uninstall failed: \(reason)\n\(String(data: output, encoding: .utf8)!)")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 22:24:20 +00:00
|
|
|
}
|