diff --git a/.swift-format b/.swift-format index d9da89a8a..9c12fd605 100644 --- a/.swift-format +++ b/.swift-format @@ -7,7 +7,7 @@ "AllPublicDeclarationsHaveDocumentation" : false, "AlwaysUseLowerCamelCase" : true, "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, "DoNotUseSemicolons" : true, "DontRepeatTypeInStaticProperties" : true, "FileScopedDeclarationPrivacy" : true, diff --git a/MasKit/AppStore/Downloader.swift b/MasKit/AppStore/Downloader.swift index 1d8ae020c..75b0ef83d 100644 --- a/MasKit/AppStore/Downloader.swift +++ b/MasKit/AppStore/Downloader.swift @@ -57,7 +57,7 @@ func download(_ adamId: UInt64, purchase: Bool = false) -> MASError? { } } - _ = group.wait(timeout: .distantFuture) + group.wait() if let observerIdentifier = observerIdentifier { CKDownloadQueue.shared().remove(observerIdentifier) diff --git a/MasKit/Commands/Home.swift b/MasKit/Commands/Home.swift index 47abe2805..2caf200b2 100644 --- a/MasKit/Commands/Home.swift +++ b/MasKit/Commands/Home.swift @@ -19,8 +19,15 @@ public struct HomeCommand: CommandProtocol { private let storeSearch: StoreSearch private var openCommand: ExternalCommand + public init() { + self.init( + storeSearch: MasStoreSearch(), + openCommand: OpenSystemCommand() + ) + } + /// Designated initializer. - public init( + init( storeSearch: StoreSearch = MasStoreSearch(), openCommand: ExternalCommand = OpenSystemCommand() ) { diff --git a/MasKit/Commands/Info.swift b/MasKit/Commands/Info.swift index 61a31852d..26f3221fc 100644 --- a/MasKit/Commands/Info.swift +++ b/MasKit/Commands/Info.swift @@ -17,8 +17,12 @@ public struct InfoCommand: CommandProtocol { private let storeSearch: StoreSearch + public init() { + self.init(storeSearch: MasStoreSearch()) + } + /// Designated initializer. - public init(storeSearch: StoreSearch = MasStoreSearch()) { + init(storeSearch: StoreSearch = MasStoreSearch()) { self.storeSearch = storeSearch } diff --git a/MasKit/Commands/Install.swift b/MasKit/Commands/Install.swift index 92e696824..cbfcfaa99 100644 --- a/MasKit/Commands/Install.swift +++ b/MasKit/Commands/Install.swift @@ -31,7 +31,7 @@ public struct InstallCommand: CommandProtocol { /// Runs the command. public func run(_ options: Options) -> Result { // Try to download applications with given identifiers and collect results - let downloadResults = options.appIds.compactMap { (appId) -> MASError? in + let downloadResults = options.appIds.compactMap { appId -> MASError? in if let product = appLibrary.installedApp(forId: appId), !options.forceInstall { printWarning("\(product.appName) is already installed") return nil diff --git a/MasKit/Commands/Lucky.swift b/MasKit/Commands/Lucky.swift index 520ac86df..97f30f831 100644 --- a/MasKit/Commands/Lucky.swift +++ b/MasKit/Commands/Lucky.swift @@ -19,9 +19,13 @@ public struct LuckyCommand: CommandProtocol { private let appLibrary: AppLibrary private let storeSearch: StoreSearch - /// Public initializer. + public init() { + self.init(storeSearch: MasStoreSearch()) + } + + /// Designated initializer. /// - Parameter storeSearch: Search manager. - public init(storeSearch: StoreSearch = MasStoreSearch()) { + init(storeSearch: StoreSearch = MasStoreSearch()) { self.init(appLibrary: MasAppLibrary(), storeSearch: storeSearch) } @@ -42,7 +46,7 @@ public struct LuckyCommand: CommandProtocol { do { let results = try storeSearch.search(for: options.appName) - guard let result = results.results.first else { + guard let result = results.first else { print("No results found") return .failure(.noSearchResultsFound) } @@ -70,7 +74,7 @@ public struct LuckyCommand: CommandProtocol { fileprivate func install(_ appId: UInt64, options: Options) -> Result { // Try to download applications with given identifiers and collect results let downloadResults = [appId] - .compactMap { (appId) -> MASError? in + .compactMap { appId -> MASError? in if let product = appLibrary.installedApp(forId: appId), !options.forceInstall { printWarning("\(product.appName) is already installed") return nil diff --git a/MasKit/Commands/Open.swift b/MasKit/Commands/Open.swift index 5c00ac84d..732c1448a 100644 --- a/MasKit/Commands/Open.swift +++ b/MasKit/Commands/Open.swift @@ -22,8 +22,15 @@ public struct OpenCommand: CommandProtocol { private let storeSearch: StoreSearch private var systemOpen: ExternalCommand + public init() { + self.init( + storeSearch: MasStoreSearch(), + openCommand: OpenSystemCommand() + ) + } + /// Designated initializer. - public init( + init( storeSearch: StoreSearch = MasStoreSearch(), openCommand: ExternalCommand = OpenSystemCommand() ) { diff --git a/MasKit/Commands/Outdated.swift b/MasKit/Commands/Outdated.swift index 320db1536..dbba1e7cb 100644 --- a/MasKit/Commands/Outdated.swift +++ b/MasKit/Commands/Outdated.swift @@ -33,28 +33,44 @@ public struct OutdatedCommand: CommandProtocol { /// Runs the command. public func run(_: Options) -> Result { + var failure: MASError? + let group = DispatchGroup() for installedApp in appLibrary.installedApps { - do { - if let storeApp = try storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { - if installedApp.bundleVersion != storeApp.version { - print( - """ - \(installedApp.itemIdentifier) \(installedApp.appName) \ - (\(installedApp.bundleVersion) -> \(storeApp.version)) - """) - } - } else { + group.enter() + storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { storeApp, error in + defer { group.leave() } + + guard error == nil else { + // Bubble up MASErrors + failure = error as? MASError ?? .searchFailed + return + } + + guard let storeApp = storeApp else { printWarning( """ Identifier \(installedApp.itemIdentifier) not found in store. \ Was expected to identify \(installedApp.appName). """) + return + } + + if installedApp.isOutdatedWhenComparedTo(storeApp) { + print( + """ + \(installedApp.itemIdentifier) \(installedApp.appName) \ + (\(installedApp.bundleVersion) -> \(storeApp.version)) + """) } - } catch { - // Bubble up MASErrors - return .failure(error as? MASError ?? .searchFailed) } } + + group.wait() + + if let failure = failure { + return .failure(failure) + } + return .success(()) } } diff --git a/MasKit/Commands/Purchase.swift b/MasKit/Commands/Purchase.swift index 40b80ae8d..f9ea5dc03 100644 --- a/MasKit/Commands/Purchase.swift +++ b/MasKit/Commands/Purchase.swift @@ -30,7 +30,7 @@ public struct PurchaseCommand: CommandProtocol { /// Runs the command. public func run(_ options: Options) -> Result { // Try to download applications with given identifiers and collect results - let downloadResults = options.appIds.compactMap { (appId) -> MASError? in + let downloadResults = options.appIds.compactMap { appId -> MASError? in if let product = appLibrary.installedApp(forId: appId) { printWarning("\(product.appName) has already been purchased.") return nil diff --git a/MasKit/Commands/Search.swift b/MasKit/Commands/Search.swift index f3bd86f49..627211840 100644 --- a/MasKit/Commands/Search.swift +++ b/MasKit/Commands/Search.swift @@ -17,22 +17,26 @@ public struct SearchCommand: CommandProtocol { private let storeSearch: StoreSearch + public init() { + self.init(storeSearch: MasStoreSearch()) + } + /// Designated initializer. /// /// - Parameter storeSearch: Search manager. - public init(storeSearch: StoreSearch = MasStoreSearch()) { + init(storeSearch: StoreSearch = MasStoreSearch()) { self.storeSearch = storeSearch } public func run(_ options: Options) -> Result { do { - let resultList = try storeSearch.search(for: options.appName) - if resultList.resultCount <= 0 || resultList.results.isEmpty { + let results = try storeSearch.search(for: options.appName) + if results.isEmpty { print("No results found") return .failure(.noSearchResultsFound) } - let output = SearchResultFormatter.format(results: resultList.results, includePrice: options.price) + let output = SearchResultFormatter.format(results: results, includePrice: options.price) print(output) return .success(()) diff --git a/MasKit/Commands/Upgrade.swift b/MasKit/Commands/Upgrade.swift index b1d5770ea..baf09c259 100644 --- a/MasKit/Commands/Upgrade.swift +++ b/MasKit/Commands/Upgrade.swift @@ -32,64 +32,82 @@ public struct UpgradeCommand: CommandProtocol { /// Runs the command. public func run(_ options: Options) -> Result { + let apps: [SoftwareProduct] do { - let apps = - try - (options.apps.count == 0 - ? appLibrary.installedApps - : options.apps.compactMap { - if let appId = UInt64($0) { - // if argument a UInt64, lookup app by id using argument - return appLibrary.installedApp(forId: appId) - } else { - // if argument not a UInt64, lookup app by name using argument - return appLibrary.installedApp(named: $0) - } - }) - .compactMap { (installedApp: SoftwareProduct) -> SoftwareProduct? in - // only upgrade apps whose local version differs from the store version - if let storeApp = try storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { - return storeApp.version != installedApp.bundleVersion - ? installedApp - : nil - } else { - return nil - } - } + apps = try findOutdatedApps(options) + } catch { + // Bubble up MASErrors + return .failure(error as? MASError ?? .searchFailed) + } - guard apps.count > 0 else { - printWarning("Nothing found to upgrade") - return .success(()) + guard apps.count > 0 else { + printWarning("Nothing found to upgrade") + return .success(()) + } + + print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):") + print(apps.map { "\($0.appName) (\($0.bundleVersion))" }.joined(separator: ", ")) + + var updatedAppCount = 0 + var failedUpgradeResults = [MASError]() + for app in apps { + if let upgradeResult = download(app.itemIdentifier.uint64Value) { + failedUpgradeResults.append(upgradeResult) + } else { + updatedAppCount += 1 } + } - print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):") - print(apps.map { "\($0.appName) (\($0.bundleVersion))" }.joined(separator: ", ")) + switch failedUpgradeResults.count { + case 0: + if updatedAppCount == 0 { + print("Everything is up-to-date") + } + return .success(()) + case 1: + return .failure(failedUpgradeResults[0]) + default: + return .failure(.downloadFailed(error: nil)) + } + } - var updatedAppCount = 0 - var failedUpgradeResults = [MASError]() - for app in apps { - if let upgradeResult = download(app.itemIdentifier.uint64Value) { - failedUpgradeResults.append(upgradeResult) + private func findOutdatedApps(_ options: Options) throws -> [SoftwareProduct] { + var apps: [SoftwareProduct] + if options.apps.isEmpty { + apps = appLibrary.installedApps + } else { + apps = options.apps.compactMap { + if let appId = UInt64($0) { + // if argument a UInt64, lookup app by id using argument + return appLibrary.installedApp(forId: appId) } else { - updatedAppCount += 1 + // if argument not a UInt64, lookup app by name using argument + return appLibrary.installedApp(named: $0) } } + } + + var outdated = [SoftwareProduct]() + let group = DispatchGroup() + let semaphore = DispatchSemaphore(value: 1) + for installedApp in apps { + // only upgrade apps whose local version differs from the store version + group.enter() + storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { result, _ in + defer { group.leave() } + + if let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) { + semaphore.wait() + defer { semaphore.signal() } - switch failedUpgradeResults.count { - case 0: - if updatedAppCount == 0 { - print("Everything is up-to-date") + outdated.append(installedApp) } - return .success(()) - case 1: - return .failure(failedUpgradeResults[0]) - default: - return .failure(.downloadFailed(error: nil)) } - } catch { - // Bubble up MASErrors - return .failure(error as? MASError ?? .searchFailed) } + + group.wait() + + return outdated } } diff --git a/MasKit/Commands/Vendor.swift b/MasKit/Commands/Vendor.swift index 8fa0ce51b..38bb56db6 100644 --- a/MasKit/Commands/Vendor.swift +++ b/MasKit/Commands/Vendor.swift @@ -19,8 +19,15 @@ public struct VendorCommand: CommandProtocol { private let storeSearch: StoreSearch private var openCommand: ExternalCommand + public init() { + self.init( + storeSearch: MasStoreSearch(), + openCommand: OpenSystemCommand() + ) + } + /// Designated initializer. - public init( + init( storeSearch: StoreSearch = MasStoreSearch(), openCommand: ExternalCommand = OpenSystemCommand() ) { diff --git a/MasKit/Controllers/AppLibrary.swift b/MasKit/Controllers/AppLibrary.swift index ab6d28e46..c02bf40d4 100644 --- a/MasKit/Controllers/AppLibrary.swift +++ b/MasKit/Controllers/AppLibrary.swift @@ -6,14 +6,13 @@ // Copyright © 2018 mas-cli. All rights reserved. // +import Foundation + /// Utility for managing installed apps. -public protocol AppLibrary { +protocol AppLibrary { /// Entire set of installed apps. var installedApps: [SoftwareProduct] { get } - /// Map of app name to ID. - var appIdsByName: [String: UInt64] { get } - /// Finds an app by ID. /// /// - Parameter forId: MAS ID for app. @@ -41,20 +40,11 @@ public protocol AppLibrary { /// Common logic extension AppLibrary { - /// Map of app name to ID. - public var appIdsByName: [String: UInt64] { - var destMap = [String: UInt64]() - for product in installedApps { - destMap[product.appName] = product.itemIdentifier.uint64Value - } - return destMap - } - /// Finds an app by name. /// /// - Parameter id: MAS ID for app. /// - Returns: Software Product of app if found; nil otherwise. - public func installedApp(forId identifier: UInt64) -> SoftwareProduct? { + func installedApp(forId identifier: UInt64) -> SoftwareProduct? { let appId = NSNumber(value: identifier) return installedApps.first { $0.itemIdentifier == appId } } @@ -63,7 +53,7 @@ extension AppLibrary { /// /// - Parameter appName: Full title of an app. /// - Returns: Software Product of app if found; nil otherwise. - public func installedApp(named appName: String) -> SoftwareProduct? { + func installedApp(named appName: String) -> SoftwareProduct? { installedApps.first { $0.appName == appName } } } diff --git a/MasKit/Controllers/MasAppLibrary.swift b/MasKit/Controllers/MasAppLibrary.swift index c1bc8fc1d..c87befaa7 100644 --- a/MasKit/Controllers/MasAppLibrary.swift +++ b/MasKit/Controllers/MasAppLibrary.swift @@ -9,12 +9,12 @@ import CommerceKit /// Utility for managing installed apps. -public class MasAppLibrary: AppLibrary { +class MasAppLibrary: AppLibrary { /// CommerceKit's singleton manager of installed software. private let softwareMap: SoftwareMap /// Array of installed software products. - public lazy var installedApps: [SoftwareProduct] = { + lazy var installedApps: [SoftwareProduct] = { softwareMap.allSoftwareProducts() }() @@ -28,7 +28,7 @@ public class MasAppLibrary: AppLibrary { /// /// - Parameter bundleId: Bundle identifier of app. /// - Returns: Software Product of app if found; nil otherwise. - public func installedApp(forBundleId bundleId: String) -> SoftwareProduct? { + func installedApp(forBundleId bundleId: String) -> SoftwareProduct? { softwareMap.product(for: bundleId) } @@ -36,7 +36,7 @@ public class MasAppLibrary: AppLibrary { /// /// - Parameter app: App to be removed. /// - Throws: Error if there is a problem. - public func uninstallApp(app: SoftwareProduct) throws { + func uninstallApp(app: SoftwareProduct) throws { if !userIsRoot() { printWarning("Apps installed from the Mac App Store require root permission to remove.") } diff --git a/MasKit/Controllers/MasStoreSearch.swift b/MasKit/Controllers/MasStoreSearch.swift index b4f677860..79d533412 100644 --- a/MasKit/Controllers/MasStoreSearch.swift +++ b/MasKit/Controllers/MasStoreSearch.swift @@ -6,72 +6,134 @@ // Copyright © 2018 mas-cli. All rights reserved. // +import Foundation +import Version + /// Manages searching the MAS catalog through the iTunes Search and Lookup APIs. -public class MasStoreSearch: StoreSearch { +class MasStoreSearch: StoreSearch { private let networkManager: NetworkManager + private static let versionExpression: NSRegularExpression = { + do { + return try NSRegularExpression(pattern: #"\"versionDisplay\"\:\"([^\"]+)\""#) + } catch { + fatalError("Unexpected error initializing NSRegularExpression: \(error.localizedDescription)") + } + }() /// Designated initializer. - public init(networkManager: NetworkManager = NetworkManager()) { + init(networkManager: NetworkManager = NetworkManager()) { self.networkManager = networkManager } /// Searches for an app. /// /// - Parameter appName: MAS ID of app - /// - Returns: Search results list of app. List will have no records if there were no matches. Never nil. - /// - Throws: Error if there is a problem with the network request. - public func search(for appName: String) throws -> SearchResultList { + /// - Parameter completion: A closure that receives the search results or an Error if there is a + /// problem with the network request. Results array will be empty if there were no matches. + func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) { guard let url = searchURL(for: appName) - else { throw MASError.urlEncoding } - - let result = networkManager.loadDataSync(from: url) - - // Unwrap network result - guard case let .success(data) = result else { - if case let .failure(error) = result { - throw error - } - throw MASError.noData + completion(nil, MASError.urlEncoding) + return } - do { - let results = try JSONDecoder().decode(SearchResultList.self, from: data) - return results - } catch { - throw MASError.jsonParsing(error: error as NSError) + loadSearchResults(url) { results, error in + if let error = error { + completion(nil, error) + return + } + + completion(results, nil) } } /// Looks up app details. /// /// - Parameter appId: MAS ID of app - /// - Returns: Search result record of app or nil if no apps match the ID. - /// - Throws: Error if there is a problem with the network request. - public func lookup(app appId: Int) throws -> SearchResult? { + /// - Parameter completion: A closure that receives the search result record of app, or nil if no apps match the ID, + /// or an Error if there is a problem with the network request. + func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) { guard let url = lookupURL(forApp: appId) - else { throw MASError.urlEncoding } + else { + completion(nil, MASError.urlEncoding) + return + } - let result = networkManager.loadDataSync(from: url) + loadSearchResults(url) { results, error in + if let error = error { + completion(nil, error) + return + } - // Unwrap network result - guard case let .success(data) = result - else { - if case let .failure(error) = result { - throw error + completion(results?.first, nil) + } + } + + private func loadSearchResults(_ url: URL, _ completion: @escaping ([SearchResult]?, Error?) -> Void) { + networkManager.loadData(from: url) { data, error in + guard let data = data else { + if let error = error { + completion(nil, error) + } else { + completion(nil, MASError.noData) + } + + return + } + + var results: SearchResultList + do { + results = try JSONDecoder().decode(SearchResultList.self, from: data) + } catch { + completion(nil, MASError.jsonParsing(error: error as NSError)) + return + } + + let group = DispatchGroup() + for index in results.results.indices { + let result = results.results[index] + guard let searchVersion = Version(tolerant: result.version), + let pageUrl = URL(string: result.trackViewUrl) + else { + continue + } + + group.enter() + self.scrapeVersionFromPage(pageUrl) { pageVersion in + if let pageVersion = pageVersion, pageVersion > searchVersion { + results.results[index].version = pageVersion.description + } + + group.leave() + } + } + + group.notify(queue: DispatchQueue.global()) { + completion(results.results, nil) } - throw MASError.noData } + } - do { - let results = try JSONDecoder().decode(SearchResultList.self, from: data) + // The App Store often lists a newer version available in an app's page than in + // the search results. We attempt to scrape it here. + private func scrapeVersionFromPage(_ pageUrl: URL, _ completion: @escaping (Version?) -> Void) { + networkManager.loadData(from: pageUrl) { data, _ in + guard let data = data else { + completion(nil) + return + } - guard let searchResult = results.results.first - else { return nil } + let html = String(decoding: data, as: UTF8.self) + let fullRange = NSRange(html.startIndex.. SearchResult? - func search(for appName: String) throws -> SearchResultList +protocol StoreSearch { + func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) + func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) } // MARK: - Common methods extension StoreSearch { + /// Looks up app details. + /// + /// - Parameter appId: MAS ID of app + /// - Returns: Search result record of app or nil if no apps match the ID. + /// - Throws: Error if there is a problem with the network request. + func lookup(app appId: Int) throws -> SearchResult? { + var result: SearchResult? + var error: Error? + + let group = DispatchGroup() + group.enter() + lookup(app: appId) { + result = $0 + error = $1 + group.leave() + } + + group.wait() + + if let error = error { + throw error + } + + return result + } + + /// Searches for an app. + /// + /// - Parameter appName: MAS ID of app + /// - Returns: Search results. Empty if there were no matches. + /// - Throws: Error if there is a problem with the network request. + func search(for appName: String) throws -> [SearchResult] { + var results: [SearchResult]? + var error: Error? + + let group = DispatchGroup() + group.enter() + search(for: appName) { + results = $0 + error = $1 + group.leave() + } + + group.wait() + + if let error = error { + throw error + } + + return results! + } + /// Builds the search URL for an app. /// /// - Parameter appName: MAS app identifier. /// - Returns: URL for the search service or nil if appName can't be encoded. - public func searchURL(for appName: String) -> URL? { + func searchURL(for appName: String) -> URL? { guard let urlString = searchURLString(forApp: appName) else { return nil } return URL(string: urlString) } @@ -38,7 +92,7 @@ extension StoreSearch { /// /// - Parameter appId: MAS app identifier. /// - Returns: URL for the lookup service or nil if appId can't be encoded. - public func lookupURL(forApp appId: Int) -> URL? { + func lookupURL(forApp appId: Int) -> URL? { guard let urlString = lookupURLString(forApp: appId) else { return nil } return URL(string: urlString) } diff --git a/MasKit/Extensions/Dictionary+StringOrEmpty.swift b/MasKit/Extensions/Dictionary+StringOrEmpty.swift deleted file mode 100644 index b7b658728..000000000 --- a/MasKit/Extensions/Dictionary+StringOrEmpty.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Dictionary+StringOrEmpty.swift -// MasKit -// -// Created by Ben Chatelain on 1/7/19. -// Copyright © 2019 mas-cli. All rights reserved. -// - -import Foundation - -extension Dictionary { - func stringOrEmpty(key: Key) -> String { - self[key] as? String ?? "" - } -} diff --git a/MasKit/ExternalCommands/ExternalCommand.swift b/MasKit/ExternalCommands/ExternalCommand.swift index 04eec21e5..785bc842c 100644 --- a/MasKit/ExternalCommands/ExternalCommand.swift +++ b/MasKit/ExternalCommands/ExternalCommand.swift @@ -6,8 +6,10 @@ // Copyright © 2019 mas-cli. All rights reserved. // +import Foundation + /// CLI command -public protocol ExternalCommand { +protocol ExternalCommand { var binaryPath: String { get set } var process: Process { get } @@ -17,7 +19,7 @@ public protocol ExternalCommand { var stdoutPipe: Pipe { get } var stderrPipe: Pipe { get } - var exitCode: Int? { get } + var exitCode: Int32 { get } var succeeded: Bool { get } var failed: Bool { get } @@ -27,42 +29,37 @@ public protocol ExternalCommand { /// Common implementation extension ExternalCommand { - public var stdout: String { + var stdout: String { let data = stdoutPipe.fileHandleForReading.readDataToEndOfFile() return String(data: data, encoding: .utf8) ?? "" } - public var stderr: String { + var stderr: String { let data = stderrPipe.fileHandleForReading.readDataToEndOfFile() return String(data: data, encoding: .utf8) ?? "" } - public var exitCode: Int? { - Int(process.terminationStatus) + var exitCode: Int32 { + process.terminationStatus } - public var succeeded: Bool { - exitCode == 0 + var succeeded: Bool { + process.terminationReason == .exit && exitCode == 0 } - public var failed: Bool { + var failed: Bool { !succeeded } /// Runs the command. - public func run(arguments: String...) throws { + func run(arguments: String...) throws { process.standardOutput = stdoutPipe process.standardError = stderrPipe process.arguments = arguments - if #available(OSX 10.13, *) { + if #available(macOS 10.13, *) { process.executableURL = URL(fileURLWithPath: binaryPath) - do { - try process.run() - } catch { - printError("Unable to launch command") - // return throw Error() - } + try process.run() } else { process.launchPath = binaryPath process.launch() diff --git a/MasKit/ExternalCommands/OpenSystemCommand.swift b/MasKit/ExternalCommands/OpenSystemCommand.swift index a82723f1c..eddb2d639 100644 --- a/MasKit/ExternalCommands/OpenSystemCommand.swift +++ b/MasKit/ExternalCommands/OpenSystemCommand.swift @@ -6,19 +6,19 @@ // Copyright © 2019 mas-cli. All rights reserved. // +import Foundation + /// Wrapper for the external open system command. /// https://ss64.com/osx/open.html -public struct OpenSystemCommand: ExternalCommand { - public var binaryPath: String +struct OpenSystemCommand: ExternalCommand { + var binaryPath: String - public let process = Process() + let process = Process() - public let stdoutPipe = Pipe() - public let stderrPipe = Pipe() + let stdoutPipe = Pipe() + let stderrPipe = Pipe() - public init( - binaryPath: String = "/usr/bin/open" - ) { + init(binaryPath: String = "/usr/bin/open") { self.binaryPath = binaryPath } } diff --git a/MasKit/Formatters/Utilities.swift b/MasKit/Formatters/Utilities.swift index 2b069f286..97ef68a74 100644 --- a/MasKit/Formatters/Utilities.swift +++ b/MasKit/Formatters/Utilities.swift @@ -6,6 +6,8 @@ // Copyright © 2016 Andrew Naylor. All rights reserved. // +import Foundation + /// A collection of output formatting helpers /// Terminal Control Sequence Indicator diff --git a/MasKit/Models/SearchResult.swift b/MasKit/Models/SearchResult.swift index 21fd43c9c..303475df0 100644 --- a/MasKit/Models/SearchResult.swift +++ b/MasKit/Models/SearchResult.swift @@ -6,20 +6,20 @@ // Copyright © 2018 mas-cli. All rights reserved. // -public struct SearchResult: Decodable { - public var bundleId: String - public var currentVersionReleaseDate: String - public var fileSizeBytes: String? - public var formattedPrice: String? - public var minimumOsVersion: String - public var price: Double? - public var sellerName: String - public var sellerUrl: String? - public var trackId: Int - public var trackCensoredName: String - public var trackName: String - public var trackViewUrl: String - public var version: String +struct SearchResult: Decodable { + var bundleId: String + var currentVersionReleaseDate: String + var fileSizeBytes: String? + var formattedPrice: String? + var minimumOsVersion: String + var price: Double? + var sellerName: String + var sellerUrl: String? + var trackId: Int + var trackCensoredName: String + var trackName: String + var trackViewUrl: String + var version: String init( bundleId: String = "", diff --git a/MasKit/Models/SearchResultList.swift b/MasKit/Models/SearchResultList.swift index d714aeb26..cd29756e4 100644 --- a/MasKit/Models/SearchResultList.swift +++ b/MasKit/Models/SearchResultList.swift @@ -6,7 +6,7 @@ // Copyright © 2018 mas-cli. All rights reserved. // -public struct SearchResultList: Decodable { +struct SearchResultList: Decodable { var resultCount: Int var results: [SearchResult] } diff --git a/MasKit/Models/SoftwareProduct.swift b/MasKit/Models/SoftwareProduct.swift index 230697b56..de232d3a3 100644 --- a/MasKit/Models/SoftwareProduct.swift +++ b/MasKit/Models/SoftwareProduct.swift @@ -6,8 +6,11 @@ // Copyright © 2018 mas-cli. All rights reserved. // +import Foundation +import Version + /// Protocol describing the members of CKSoftwareProduct used throughout MasKit. -public protocol SoftwareProduct { +protocol SoftwareProduct { var appName: String { get } var bundleIdentifier: String { get set } var bundlePath: String { get set } @@ -29,4 +32,19 @@ extension SoftwareProduct { var appNameOrBbundleIdentifier: String { appName == "" ? bundleIdentifier : appName } + + func isOutdatedWhenComparedTo(_ storeApp: SearchResult) -> Bool { + // The App Store does not enforce semantic versioning, but we assume most apps follow versioning + // schemes that increase numerically over time. + guard let semanticBundleVersion = Version(tolerant: bundleVersion), + let semanticAppStoreVersion = Version(tolerant: storeApp.version) + else { + // If a version string can't be parsed as a Semantic Version, our best effort is to check for + // equality. The only version that matters is the one in the App Store. + // https://semver.org + return bundleVersion != storeApp.version + } + + return semanticBundleVersion < semanticAppStoreVersion + } } diff --git a/MasKit/Network/NetworkManager.swift b/MasKit/Network/NetworkManager.swift index a76612fdb..cb4235467 100644 --- a/MasKit/Network/NetworkManager.swift +++ b/MasKit/Network/NetworkManager.swift @@ -9,17 +9,13 @@ import Foundation /// Network abstraction -public class NetworkManager { - enum NetworkError: Error { - case timeout - } - +class NetworkManager { private let session: NetworkSession /// Designated initializer /// /// - Parameter session: A networking session. - public init(session: NetworkSession = URLSession(configuration: .ephemeral)) { + init(session: NetworkSession = URLSession(configuration: .ephemeral)) { self.session = session // Older releases allowed URLSession to write a cache. We clean it up here. @@ -34,34 +30,35 @@ public class NetworkManager { /// - Parameters: /// - url: URL to load data from. /// - completionHandler: Closure where result is delivered. - func loadData(from url: URL, completionHandler: @escaping (NetworkResult) -> Void) { - session.loadData(from: url) { (data: Data?, error: Error?) in - let result: NetworkResult = - data != nil - ? .success(data!) - : .failure(error!) - completionHandler(result) - } + func loadData(from url: URL, completionHandler: @escaping (Data?, Error?) -> Void) { + session.loadData(from: url, completionHandler: completionHandler) } /// Loads data synchronously. /// /// - Parameter url: URL to load data from. - /// - Returns: Network result containing either Data or an Error. - func loadDataSync(from url: URL) -> NetworkResult { - var syncResult: NetworkResult? - let semaphore = DispatchSemaphore(value: 0) + /// - Returns: The Data of the response. + func loadDataSync(from url: URL) throws -> Data { + var data: Data? + var error: Error? + let group = DispatchGroup() + group.enter() + session.loadData(from: url) { + data = $0 + error = $1 + group.leave() + } + + group.wait() - loadData(from: url) { asyncResult in - syncResult = asyncResult - semaphore.signal() + guard error == nil else { + throw error! } - _ = semaphore.wait(timeout: .distantFuture) - guard let result = syncResult else { - return .failure(NetworkError.timeout) + guard data != nil else { + throw MASError.noData } - return result + return data! } } diff --git a/MasKit/Network/NetworkResult.swift b/MasKit/Network/NetworkResult.swift deleted file mode 100644 index 8724a4f34..000000000 --- a/MasKit/Network/NetworkResult.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// NetworkResult.swift -// MasKit -// -// Created by Ben Chatelain on 1/5/19. -// Copyright © 2019 mas-cli. All rights reserved. -// - -enum NetworkResult { - case success(Data) - case failure(Error) -} - -extension NetworkResult: Equatable { - static func == (lhs: NetworkResult, rhs: NetworkResult) -> Bool { - switch (lhs, rhs) { - case (.success(let data1), .success(let data2)): - return data1 == data2 - - case (.failure(let error1), .failure(let error2)): - return error1.localizedDescription == error2.localizedDescription - - default: - return false - } - } -} diff --git a/MasKit/Network/NetworkSession.swift b/MasKit/Network/NetworkSession.swift index 64ad4044a..a3ef2cff6 100644 --- a/MasKit/Network/NetworkSession.swift +++ b/MasKit/Network/NetworkSession.swift @@ -6,6 +6,8 @@ // Copyright © 2019 mas-cli. All rights reserved. // -@objc public protocol NetworkSession { - @objc func loadData(from url: URL, completionHandler: @escaping (Data?, Error?) -> Void) +import Foundation + +protocol NetworkSession { + func loadData(from url: URL, completionHandler: @escaping (Data?, Error?) -> Void) } diff --git a/MasKit/Network/URLSession+NetworkSession.swift b/MasKit/Network/URLSession+NetworkSession.swift index 2fce2672b..66c08f5ff 100644 --- a/MasKit/Network/URLSession+NetworkSession.swift +++ b/MasKit/Network/URLSession+NetworkSession.swift @@ -9,7 +9,7 @@ import Foundation extension URLSession: NetworkSession { - @objc open func loadData(from url: URL, completionHandler: @escaping (Data?, Error?) -> Void) { + open func loadData(from url: URL, completionHandler: @escaping (Data?, Error?) -> Void) { let task = dataTask(with: url) { data, _, error in completionHandler(data, error) } diff --git a/MasKit/SupportingFiles/MasKit.h b/MasKit/SupportingFiles/MasKit.h index 3b4b909e4..154841cfc 100644 --- a/MasKit/SupportingFiles/MasKit.h +++ b/MasKit/SupportingFiles/MasKit.h @@ -6,7 +6,7 @@ // Copyright © 2018 Andrew Naylor. All rights reserved. // -@import Cocoa; +@import Foundation; //! Project version number for MasKit. FOUNDATION_EXPORT double MasKitVersionNumber; diff --git a/MasKitTests/Controllers/AppLibraryMock.swift b/MasKitTests/Controllers/AppLibraryMock.swift index 61d70c470..2290ad994 100644 --- a/MasKitTests/Controllers/AppLibraryMock.swift +++ b/MasKitTests/Controllers/AppLibraryMock.swift @@ -15,12 +15,12 @@ class AppLibraryMock: AppLibrary { /// /// - Parameter bundleId: Bundle identifier of app. /// - Returns: Software Product of app if found; nil otherwise. - public func installedApp(forBundleId _: String) -> SoftwareProduct? { + func installedApp(forBundleId _: String) -> SoftwareProduct? { nil } func uninstallApp(app: SoftwareProduct) throws { - if !installedApps.contains(where: { (product) -> Bool in + if !installedApps.contains(where: { product -> Bool in app.itemIdentifier == product.itemIdentifier }) { throw MASError.notInstalled diff --git a/MasKitTests/Controllers/MasStoreSearchSpec.swift b/MasKitTests/Controllers/MasStoreSearchSpec.swift index 176182b84..a1e5ccc3b 100644 --- a/MasKitTests/Controllers/MasStoreSearchSpec.swift +++ b/MasKitTests/Controllers/MasStoreSearchSpec.swift @@ -19,11 +19,10 @@ class MasStoreSearchSpec: QuickSpec { let networkSession = NetworkSessionMockFromFile(responseFile: "search/slack.json") let storeSearch = MasStoreSearch(networkManager: NetworkManager(session: networkSession)) - var searchList: SearchResultList + var results: [SearchResult] do { - searchList = try storeSearch.search(for: "slack") - expect(searchList.resultCount) == 39 - expect(searchList.results.count) == 39 + results = try storeSearch.search(for: "slack") + expect(results.count) == 39 } catch { let maserror = error as! MASError if case let .jsonParsing(nserror) = maserror { diff --git a/MasKitTests/Controllers/StoreSearchMock.swift b/MasKitTests/Controllers/StoreSearchMock.swift index e899e65e5..20a479211 100644 --- a/MasKitTests/Controllers/StoreSearchMock.swift +++ b/MasKitTests/Controllers/StoreSearchMock.swift @@ -11,21 +11,26 @@ class StoreSearchMock: StoreSearch { var apps: [Int: SearchResult] = [:] - func search(for appName: String) throws -> SearchResultList { + func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) { let filtered = apps.filter { $1.trackName.contains(appName) } - return SearchResultList(resultCount: filtered.count, results: filtered.map { $1 }) + let results = filtered.map { $1 } + completion(results, nil) } - func lookup(app appId: Int) throws -> SearchResult? { + func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) { // Negative numbers are invalid - if appId <= 0 { - throw MASError.searchFailed + guard appId > 0 else { + completion(nil, MASError.searchFailed) + return } guard let result = apps[appId] - else { throw MASError.noSearchResultsFound } + else { + completion(nil, MASError.noSearchResultsFound) + return + } - return result + completion(result, nil) } func reset() { diff --git a/MasKitTests/Controllers/StoreSearchSpec.swift b/MasKitTests/Controllers/StoreSearchSpec.swift index 87b219e62..dd90ef323 100644 --- a/MasKitTests/Controllers/StoreSearchSpec.swift +++ b/MasKitTests/Controllers/StoreSearchSpec.swift @@ -12,8 +12,13 @@ import Quick /// Protocol minimal implementation struct StoreSearchForTesting: StoreSearch { - func lookup(app _: Int) throws -> SearchResult? { nil } - func search(for _: String) throws -> SearchResultList { SearchResultList(resultCount: 0, results: []) } + func lookup(app _: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) { + completion(nil, nil) + } + + func search(for _: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) { + completion([], nil) + } } class StoreSearchSpec: QuickSpec { diff --git a/MasKitTests/ExternalCommands/OpenSystemCommandMock.swift b/MasKitTests/ExternalCommands/OpenSystemCommandMock.swift index 3abedd746..13352e061 100644 --- a/MasKitTests/ExternalCommands/OpenSystemCommandMock.swift +++ b/MasKitTests/ExternalCommands/OpenSystemCommandMock.swift @@ -6,6 +6,7 @@ // Copyright © 2019 mas-cli. All rights reserved. // +import Foundation @testable import MasKit class OpenSystemCommandMock: ExternalCommand { diff --git a/MasKitTests/Models/SoftwareProductMock.swift b/MasKitTests/Models/SoftwareProductMock.swift index 2502d1281..ee8203e51 100644 --- a/MasKitTests/Models/SoftwareProductMock.swift +++ b/MasKitTests/Models/SoftwareProductMock.swift @@ -6,6 +6,7 @@ // Copyright © 2018 mas-cli. All rights reserved. // +import Foundation @testable import MasKit struct SoftwareProductMock: SoftwareProduct { diff --git a/MasKitTests/Network/NetworkManagerTests.swift b/MasKitTests/Network/NetworkManagerTests.swift index 9f5c4fcb8..c78690c8a 100644 --- a/MasKitTests/Network/NetworkManagerTests.swift +++ b/MasKitTests/Network/NetworkManagerTests.swift @@ -24,12 +24,18 @@ class NetworkManagerTests: XCTestCase { let url = URL(fileURLWithPath: "url") // Perform the request and verify the result - var result: NetworkResult! - manager.loadData(from: url) { result = $0 } - XCTAssertEqual(result, NetworkResult.success(data)) + var response: Data? + var error: Error? + manager.loadData(from: url) { + response = $0 + error = $1 + } + + XCTAssertEqual(response, data) + XCTAssertNil(error) } - func testSuccessfulSyncResponse() { + func testSuccessfulSyncResponse() throws { // Setup our objects let session = NetworkSessionMock() let manager = NetworkManager(session: session) @@ -42,8 +48,8 @@ class NetworkManagerTests: XCTestCase { let url = URL(fileURLWithPath: "url") // Perform the request and verify the result - let result = manager.loadDataSync(from: url) - XCTAssertEqual(result, NetworkResult.success(data)) + let result = try manager.loadDataSync(from: url) + XCTAssertEqual(result, data) } func testFailureAsyncResponse() { @@ -51,15 +57,20 @@ class NetworkManagerTests: XCTestCase { let session = NetworkSessionMock() let manager = NetworkManager(session: session) - session.error = NetworkManager.NetworkError.timeout + session.error = MASError.noData // Create a URL (using the file path API to avoid optionals) let url = URL(fileURLWithPath: "url") // Perform the request and verify the result - var result: NetworkResult! - manager.loadData(from: url) { result = $0 } - XCTAssertEqual(result, NetworkResult.failure(NetworkManager.NetworkError.timeout)) + var error: Error! + manager.loadData(from: url) { error = $1 } + guard let masError = error as? MASError else { + XCTFail("Error is of unexpected type.") + return + } + + XCTAssertEqual(masError, MASError.noData) } func testFailureSyncResponse() { @@ -67,13 +78,19 @@ class NetworkManagerTests: XCTestCase { let session = NetworkSessionMock() let manager = NetworkManager(session: session) - session.error = NetworkManager.NetworkError.timeout + session.error = MASError.noData // Create a URL (using the file path API to avoid optionals) let url = URL(fileURLWithPath: "url") // Perform the request and verify the result - let result = manager.loadDataSync(from: url) - XCTAssertEqual(result, NetworkResult.failure(NetworkManager.NetworkError.timeout)) + XCTAssertThrowsError(try manager.loadDataSync(from: url)) { error in + guard let error = error as? MASError else { + XCTFail("Error is of unexpected type.") + return + } + + XCTAssertEqual(error, MASError.noData) + } } } diff --git a/MasKitTests/Network/NetworkSessionMock.swift b/MasKitTests/Network/NetworkSessionMock.swift index 81b15f98b..a3e2f526a 100644 --- a/MasKitTests/Network/NetworkSessionMock.swift +++ b/MasKitTests/Network/NetworkSessionMock.swift @@ -6,7 +6,8 @@ // Copyright © 2018 mas-cli. All rights reserved. // -import MasKit +import Foundation +@testable import MasKit /// Mock NetworkSession for testing. class NetworkSessionMock: NetworkSession { @@ -37,7 +38,7 @@ class NetworkSessionMock: NetworkSession { /// - Parameters: /// - url: unused /// - completionHandler: Closure which is delivered either data or an error. - @objc func loadData(from _: URL, completionHandler: @escaping (Data?, Error?) -> Void) { + func loadData(from _: URL, completionHandler: @escaping (Data?, Error?) -> Void) { completionHandler(data, error) } } diff --git a/MasKitTests/Network/NetworkSessionMockFromFile.swift b/MasKitTests/Network/NetworkSessionMockFromFile.swift index 0835c15ef..efe5d129d 100644 --- a/MasKitTests/Network/NetworkSessionMockFromFile.swift +++ b/MasKitTests/Network/NetworkSessionMockFromFile.swift @@ -6,6 +6,7 @@ // Copyright © 2019 mas-cli. All rights reserved. // +import Foundation import MasKit /// Mock NetworkSession for testing with saved JSON response payload files. @@ -25,7 +26,7 @@ class NetworkSessionMockFromFile: NetworkSessionMock { /// - Parameters: /// - url: unused /// - completionHandler: Closure which is delivered either data or an error. - @objc override func loadData(from _: URL, completionHandler: @escaping (Data?, Error?) -> Void) { + override func loadData(from _: URL, completionHandler: @escaping (Data?, Error?) -> Void) { guard let fileURL = Bundle.url(for: responseFile) else { fatalError("Unable to load file \(responseFile)") } diff --git a/PrivateFrameworks/CommerceKit/CKUpdateController.h b/PrivateFrameworks/CommerceKit/CKUpdateController.h deleted file mode 100644 index 6b00b3547..000000000 --- a/PrivateFrameworks/CommerceKit/CKUpdateController.h +++ /dev/null @@ -1,98 +0,0 @@ -// -// Generated by class-dump 3.5 (64 bit). -// -// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. -// - -#import "CKServiceInterface.h" - -@class CKUpdateControllerClient, NSMutableDictionary; - -NS_ASSUME_NONNULL_BEGIN - -@interface CKUpdateController : CKServiceInterface -{ - BOOL _shouldNotAttemptInstallationAfterFailureDialog; - - // CDUnknownBlockType _dialogHandler; - - NSMutableDictionary *_availableUpdatesObservers; - NSMutableDictionary *_updateScanObservers; - NSMutableDictionary *_updateProgressObservers; - CKUpdateControllerClient *_sharedObserver; -} - -+ (CKUpdateController * _Nullable)sharedUpdateController; - -@property(retain, nonatomic) CKUpdateControllerClient *sharedObserver; // @synthesize sharedObserver=_sharedObserver; -@property(retain, nonatomic) NSMutableDictionary *updateProgressObservers; // @synthesize updateProgressObservers=_updateProgressObservers; -@property(retain, nonatomic) NSMutableDictionary *updateScanObservers; // @synthesize updateScanObservers=_updateScanObservers; -@property(retain, nonatomic) NSMutableDictionary *availableUpdatesObservers; // @synthesize availableUpdatesObservers=_availableUpdatesObservers; -@property BOOL shouldNotAttemptInstallationAfterFailureDialog; // @synthesize shouldNotAttemptInstallationAfterFailureDialog=_shouldNotAttemptInstallationAfterFailureDialog; - -//@property(copy) CDUnknownBlockType dialogHandler; // @synthesize dialogHandler=_dialogHandler; -//- (void).cxx_destruct; - -- (void)didInteractivelyPurchaseItemIdentifier:(unsigned long long)arg1 success:(BOOL)arg2; -- (BOOL)willInteractivelyPurchaseItemIdentifier:(unsigned long long)arg1; -- (void)promptUserToOptInForAutoUpdateWithShowNotification:(BOOL)arg1; -- (BOOL)shouldPromptForAutoUpdateOptIn; -- (BOOL)isAutoUpdatedEnabled; -- (id)installedUpdatesJournal; -- (BOOL)softwareUpdateCatalogIsSeedCatalog; -- (long long)softwareUpdateCatalogTrustLevel; -- (int)catalogTrustLevel; -- (id)catalogHostName; -- (void)stopObservingOSUpdateProgressWithCallback:(id)arg1; - -//- (id)observeOSUpdateProgressWithProgressHandler:(CDUnknownBlockType)arg1; - -- (void)stopObservingOSUpdateScansWithCallback:(id)arg1; - -//- (id)observeOSUpdateScansWithProgressHandler:(CDUnknownBlockType)arg1; - -- (void)startOSUpdateScanWithForceFullScan:(BOOL)arg1 reportProgressImmediately:(BOOL)arg2 launchedFromNotification:(BOOL)arg3 userHasSeenAllUpdates:(BOOL)arg4 checkForOtherUpdates:(BOOL)arg5; -- (void)unhideAllOSUpdates; -- (void)hideOSUpdatesWithProductKeys:(id)arg1; -- (BOOL)hasHiddenOSUpdates; -- (BOOL)osUpdateScanInProgress; -- (id)_updateFailureDialogWithAuditInfo:(id)arg1; -- (BOOL)_otherUsersAreLoggedIn; -- (void)showUpdateFailureWithAuditToken:(id)arg1; -- (void)removeUpdateFromInstallLaterWithBundleID:(id)arg1; -- (id)appUpdatesToBeInstalledLater; -- (id)osUpdatesToBeInstalledLater; -- (id)osUpdatesToBeInstalledAfterLogout; -- (void)cancelUpdatesToBeInstalledLater; - -//- (void)queueOSUpdatesForLaterInstall:(id)arg1 withMode:(long long)arg2 completionHandler:(CDUnknownBlockType)arg3; - -- (void)installAvailableUpdatesLaterWithMode:(long long)arg1; -- (BOOL)shouldOfferDoItLater; - -//- (void)updatesWithTags:(id)arg1 completionHandler:(CDUnknownBlockType)arg2; - -- (void)installAllAvailableUpdates; - -//- (void)startAppInstallWithTags:(id)arg1 fallbackPurchase:(id)arg2 completionHandler:(CDUnknownBlockType)arg3; -//- (void)startAppUpdates:(id)arg1 andOSUpdates:(id)arg2 withDelegate:(id)arg3 completionHandler:(CDUnknownBlockType)arg4; -//- (void)_checkForBookUpdatesWithCompletionHandler:(CDUnknownBlockType)arg1; -//- (void)checkForUpdatesWithUserHasSeenUpdates:(BOOL)arg1 completionHandler:(CDUnknownBlockType)arg2; - -- (void)removeAvailableUpdatesObserver:(id)arg1; - -//- (id)addAvailableUpdatesObserverWithBlock:(CDUnknownBlockType)arg1; - -- (unsigned long long)availableUpdatesBadgeCount; -- (id)incompatibleUpdates; - -- (nullable CKUpdate *)availableUpdateWithItemIdentifier:(unsigned long long)arg1; -- (NSArray*)availableUpdates; - -- (void)connectionWasInterrupted; -- (id)initWithStoreClient:(id)arg1; -- (id)init; - -@end - -NS_ASSUME_NONNULL_END diff --git a/PrivateFrameworks/CommerceKit/ISStoreURLOperationDelegate.h b/PrivateFrameworks/CommerceKit/ISStoreURLOperationDelegate.h deleted file mode 100644 index cb15080f7..000000000 --- a/PrivateFrameworks/CommerceKit/ISStoreURLOperationDelegate.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Generated by class-dump 3.5 (64 bit). -// -// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. -// - -@import StoreFoundation; - -#import "ISURLOperationDelegate.h" - -@class ISStoreURLOperation, NSNumber, NSString; - -NS_ASSUME_NONNULL_BEGIN - -@protocol ISStoreURLOperationDelegate - -@optional -- (BOOL)operation:(ISStoreURLOperation *)arg1 shouldSetStoreFrontID:(NSString *)arg2; -- (void)operation:(ISStoreURLOperation *)arg1 didAuthenticateWithDSID:(NSNumber *)arg2; -@end - -NS_ASSUME_NONNULL_END diff --git a/PrivateFrameworks/CommerceKit/module.modulemap b/PrivateFrameworks/CommerceKit/module.modulemap index ef2067c7e..2ff442f92 100644 --- a/PrivateFrameworks/CommerceKit/module.modulemap +++ b/PrivateFrameworks/CommerceKit/module.modulemap @@ -6,13 +6,9 @@ module CommerceKit { header "CKAccountStore.h" header "CKDownloadDirectory.h" - header "CKDownloadQueue.h" header "CKDownloadQueueObserver.h" header "CKPurchaseController.h" - header "CKServiceInterface.h" header "CKSoftwareMap.h" - header "CKUpdateController.h" - header "ISStoreURLOperationDelegate.h" export * } diff --git a/PrivateFrameworks/StoreFoundation/ISOperationDelegate.h b/PrivateFrameworks/StoreFoundation/ISOperationDelegate.h deleted file mode 100644 index a686addb7..000000000 --- a/PrivateFrameworks/StoreFoundation/ISOperationDelegate.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated by class-dump 3.5 (64 bit). -// -// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. -// - -@class ISOperation, NSError, SSOperationProgress; - -NS_ASSUME_NONNULL_BEGIN - -@protocol ISOperationDelegate - -@optional -- (void)operationFinished:(ISOperation *)arg1; -- (void)operation:(ISOperation *)arg1 updatedProgress:(SSOperationProgress *)arg2; -- (void)operation:(ISOperation *)arg1 failedWithError:(NSError *)arg2; -@end - -NS_ASSUME_NONNULL_END diff --git a/PrivateFrameworks/StoreFoundation/ISURLOperationDelegate.h b/PrivateFrameworks/StoreFoundation/ISURLOperationDelegate.h deleted file mode 100644 index 9a8d7b92b..000000000 --- a/PrivateFrameworks/StoreFoundation/ISURLOperationDelegate.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Generated by class-dump 3.5 (64 bit). -// -// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. -// - - #import "ISOperationDelegate.h" - -@class ISURLOperation, NSMutableURLRequest, NSURLResponse; - -NS_ASSUME_NONNULL_BEGIN - -@protocol ISURLOperationDelegate - -@optional -- (void)operation:(ISURLOperation *)arg1 willSendRequest:(NSMutableURLRequest *)arg2; -- (void)operation:(ISURLOperation *)arg1 didReceiveResponse:(NSURLResponse *)arg2; -- (void)operation:(ISURLOperation *)arg1 finishedWithOutput:(id)arg2; -@end - -NS_ASSUME_NONNULL_END diff --git a/PrivateFrameworks/StoreFoundation/module.modulemap b/PrivateFrameworks/StoreFoundation/module.modulemap index 197ea3ea3..96344c688 100644 --- a/PrivateFrameworks/StoreFoundation/module.modulemap +++ b/PrivateFrameworks/StoreFoundation/module.modulemap @@ -8,12 +8,8 @@ module StoreFoundation { header "CKUpdate.h" header "ISAccountService.h" header "ISAuthenticationContext.h" - header "ISOperationDelegate.h" - header "ISServiceProxy.h" - header "ISServiceRemoteObject.h" header "ISStoreAccount.h" header "ISStoreClient.h" - header "ISURLOperationDelegate.h" header "SSDownload.h" header "SSDownloadMetadata.h" header "SSDownloadPhase.h" diff --git a/mas-cli.xcodeproj/project.pbxproj b/mas-cli.xcodeproj/project.pbxproj index 18511b040..ea3e8b041 100644 --- a/mas-cli.xcodeproj/project.pbxproj +++ b/mas-cli.xcodeproj/project.pbxproj @@ -28,7 +28,6 @@ B576FE0021E113610016B39D /* NetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FDFF21E113610016B39D /* NetworkSession.swift */; }; B576FE0221E1139E0016B39D /* URLSession+NetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE0121E1139E0016B39D /* URLSession+NetworkSession.swift */; }; B576FE0421E113E90016B39D /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE0321E113E90016B39D /* NetworkManager.swift */; }; - B576FE0821E114A80016B39D /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE0721E114A80016B39D /* NetworkResult.swift */; }; B576FE0C21E116590016B39D /* NetworkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE0B21E116590016B39D /* NetworkManagerTests.swift */; }; B576FE0E21E1D6310016B39D /* String+PercentEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE0D21E1D6310016B39D /* String+PercentEncoding.swift */; }; B576FE1221E1D82D0016B39D /* NetworkSessionMockFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE1121E1D82D0016B39D /* NetworkSessionMockFromFile.swift */; }; @@ -36,7 +35,6 @@ B576FE1621E1D8CB0016B39D /* String+FileExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE1521E1D8CB0016B39D /* String+FileExtension.swift */; }; B576FE1B21E28E8A0016B39D /* NetworkSessionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE1A21E28E8A0016B39D /* NetworkSessionMock.swift */; }; B576FE1D21E28EF70016B39D /* URLSessionDataTaskMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE1C21E28EF70016B39D /* URLSessionDataTaskMock.swift */; }; - B576FE2821E423E60016B39D /* Dictionary+StringOrEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE2721E423E60016B39D /* Dictionary+StringOrEmpty.swift */; }; B576FE2A21E4240B0016B39D /* AppInfoFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE2921E4240B0016B39D /* AppInfoFormatter.swift */; }; B576FE2C21E42A230016B39D /* OutputListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE2B21E42A230016B39D /* OutputListener.swift */; }; B576FE2E21E5A8010016B39D /* Strongify.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576FE2D21E5A8010016B39D /* Strongify.swift */; }; @@ -77,6 +75,7 @@ B5DBF81321DEEC7C00F3B151 /* OpenCommandSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBF81221DEEC7C00F3B151 /* OpenCommandSpec.swift */; }; B5DBF81521E02BA900F3B151 /* StoreSearchMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBF81421E02BA900F3B151 /* StoreSearchMock.swift */; }; B5DBF81721E02E3400F3B151 /* OpenSystemCommandMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBF81621E02E3400F3B151 /* OpenSystemCommandMock.swift */; }; + C56C4FF5262A50F5004F37EB /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = C56C4FF4262A50F5004F37EB /* Version */; }; ED031A7C1B5127C00097692E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED031A7B1B5127C00097692E /* main.swift */; }; F80B27B62611116A00A285C9 /* AppListFormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80B27B52611116A00A285C9 /* AppListFormatterSpec.swift */; }; F80B27BA2611118E00A285C9 /* AppListFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80B27B92611118E00A285C9 /* AppListFormatter.swift */; }; @@ -86,19 +85,15 @@ F832138C2173D3E1008BA8A0 /* CKPurchaseController.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB719D20F2EC4500F56FDC /* CKPurchaseController.h */; }; F832138D2173D3E1008BA8A0 /* CKServiceInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB719E20F2EC4500F56FDC /* CKServiceInterface.h */; }; F832138E2173D3E1008BA8A0 /* CKSoftwareMap.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB719F20F2EC4500F56FDC /* CKSoftwareMap.h */; }; - F832138F2173D3E1008BA8A0 /* CKUpdateController.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A020F2EC4500F56FDC /* CKUpdateController.h */; }; - F83213902173D3E1008BA8A0 /* ISStoreURLOperationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A120F2EC4500F56FDC /* ISStoreURLOperationDelegate.h */; }; F83213912173D3E1008BA8A0 /* CKDownloadDirectory.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71B620F2F87300F56FDC /* CKDownloadDirectory.h */; }; F83213922173D5AB008BA8A0 /* CKSoftwareProduct.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A320F2EC4500F56FDC /* CKSoftwareProduct.h */; }; F83213932173D5AB008BA8A0 /* CKUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A420F2EC4500F56FDC /* CKUpdate.h */; }; F83213942173D5AB008BA8A0 /* ISAccountService.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A520F2EC4500F56FDC /* ISAccountService.h */; }; F83213952173D5AB008BA8A0 /* ISAuthenticationContext.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A620F2EC4500F56FDC /* ISAuthenticationContext.h */; }; - F83213962173D5AB008BA8A0 /* ISOperationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A720F2EC4500F56FDC /* ISOperationDelegate.h */; }; F83213972173D5AB008BA8A0 /* ISServiceProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A820F2EC4500F56FDC /* ISServiceProxy.h */; }; F83213982173D5AB008BA8A0 /* ISServiceRemoteObject.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71A920F2EC4500F56FDC /* ISServiceRemoteObject.h */; }; F83213992173D5AB008BA8A0 /* ISStoreAccount.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AA20F2EC4500F56FDC /* ISStoreAccount.h */; }; F832139A2173D5AB008BA8A0 /* ISStoreClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AB20F2EC4500F56FDC /* ISStoreClient.h */; }; - F832139B2173D5AB008BA8A0 /* ISURLOperationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AC20F2EC4500F56FDC /* ISURLOperationDelegate.h */; }; F832139C2173D5B2008BA8A0 /* SSDownload.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AD20F2EC4500F56FDC /* SSDownload.h */; }; F832139D2173D5B2008BA8A0 /* SSDownloadMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AE20F2EC4500F56FDC /* SSDownloadMetadata.h */; }; F832139E2173D5B2008BA8A0 /* SSDownloadPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = F8FB71AF20F2EC4500F56FDC /* SSDownloadPhase.h */; }; @@ -219,7 +214,6 @@ B576FDFF21E113610016B39D /* NetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSession.swift; sourceTree = ""; }; B576FE0121E1139E0016B39D /* URLSession+NetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+NetworkSession.swift"; sourceTree = ""; }; B576FE0321E113E90016B39D /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; - B576FE0721E114A80016B39D /* NetworkResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkResult.swift; sourceTree = ""; }; B576FE0B21E116590016B39D /* NetworkManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManagerTests.swift; sourceTree = ""; }; B576FE0D21E1D6310016B39D /* String+PercentEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+PercentEncoding.swift"; sourceTree = ""; }; B576FE1121E1D82D0016B39D /* NetworkSessionMockFromFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkSessionMockFromFile.swift; sourceTree = ""; }; @@ -227,7 +221,6 @@ B576FE1521E1D8CB0016B39D /* String+FileExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+FileExtension.swift"; sourceTree = ""; }; B576FE1A21E28E8A0016B39D /* NetworkSessionMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkSessionMock.swift; sourceTree = ""; }; B576FE1C21E28EF70016B39D /* URLSessionDataTaskMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDataTaskMock.swift; sourceTree = ""; }; - B576FE2721E423E60016B39D /* Dictionary+StringOrEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+StringOrEmpty.swift"; sourceTree = ""; }; B576FE2921E4240B0016B39D /* AppInfoFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoFormatter.swift; sourceTree = ""; }; B576FE2B21E42A230016B39D /* OutputListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputListener.swift; sourceTree = ""; }; B576FE2D21E5A8010016B39D /* Strongify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strongify.swift; sourceTree = ""; }; @@ -290,7 +283,6 @@ F80B27B52611116A00A285C9 /* AppListFormatterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppListFormatterSpec.swift; sourceTree = ""; }; F80B27B92611118E00A285C9 /* AppListFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppListFormatter.swift; sourceTree = ""; }; F8242D8020746A510026DF35 /* StoreAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreAccount.swift; sourceTree = ""; }; - F83213A42173EF75008BA8A0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; F83213A52173EF75008BA8A0 /* StoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreFoundation.framework; path = /System/Library/PrivateFrameworks/StoreFoundation.framework; sourceTree = ""; }; F83213A62173EF75008BA8A0 /* CommerceKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CommerceKit.framework; path = /System/Library/PrivateFrameworks/CommerceKit.framework; sourceTree = ""; }; F85DA8AD240C313900FE5650 /* MasAppLibrarySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasAppLibrarySpec.swift; sourceTree = ""; }; @@ -309,18 +301,14 @@ F8FB719D20F2EC4500F56FDC /* CKPurchaseController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKPurchaseController.h; sourceTree = ""; }; F8FB719E20F2EC4500F56FDC /* CKServiceInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKServiceInterface.h; sourceTree = ""; }; F8FB719F20F2EC4500F56FDC /* CKSoftwareMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKSoftwareMap.h; sourceTree = ""; }; - F8FB71A020F2EC4500F56FDC /* CKUpdateController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKUpdateController.h; sourceTree = ""; }; - F8FB71A120F2EC4500F56FDC /* ISStoreURLOperationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISStoreURLOperationDelegate.h; sourceTree = ""; }; F8FB71A320F2EC4500F56FDC /* CKSoftwareProduct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKSoftwareProduct.h; sourceTree = ""; }; F8FB71A420F2EC4500F56FDC /* CKUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKUpdate.h; sourceTree = ""; }; F8FB71A520F2EC4500F56FDC /* ISAccountService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISAccountService.h; sourceTree = ""; }; F8FB71A620F2EC4500F56FDC /* ISAuthenticationContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISAuthenticationContext.h; sourceTree = ""; }; - F8FB71A720F2EC4500F56FDC /* ISOperationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISOperationDelegate.h; sourceTree = ""; }; F8FB71A820F2EC4500F56FDC /* ISServiceProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISServiceProxy.h; sourceTree = ""; }; F8FB71A920F2EC4500F56FDC /* ISServiceRemoteObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISServiceRemoteObject.h; sourceTree = ""; }; F8FB71AA20F2EC4500F56FDC /* ISStoreAccount.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISStoreAccount.h; sourceTree = ""; }; F8FB71AB20F2EC4500F56FDC /* ISStoreClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISStoreClient.h; sourceTree = ""; }; - F8FB71AC20F2EC4500F56FDC /* ISURLOperationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ISURLOperationDelegate.h; sourceTree = ""; }; F8FB71AD20F2EC4500F56FDC /* SSDownload.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSDownload.h; sourceTree = ""; }; F8FB71AE20F2EC4500F56FDC /* SSDownloadMetadata.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSDownloadMetadata.h; sourceTree = ""; }; F8FB71AF20F2EC4500F56FDC /* SSDownloadPhase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSDownloadPhase.h; sourceTree = ""; }; @@ -347,6 +335,7 @@ buildActionMask = 2147483647; files = ( F83213A22173DC13008BA8A0 /* Commandant.framework in Frameworks */, + C56C4FF5262A50F5004F37EB /* Version in Frameworks */, B5552928219A1BB900ACB4CA /* CommerceKit.framework in Frameworks */, B5552929219A1BC700ACB4CA /* StoreFoundation.framework in Frameworks */, ); @@ -390,7 +379,6 @@ isa = PBXGroup; children = ( B576FE0321E113E90016B39D /* NetworkManager.swift */, - B576FE0721E114A80016B39D /* NetworkResult.swift */, B576FDFF21E113610016B39D /* NetworkSession.swift */, B576FE0121E1139E0016B39D /* URLSession+NetworkSession.swift */, ); @@ -449,7 +437,6 @@ B576FE1F21E290720016B39D /* Extensions */ = { isa = PBXGroup; children = ( - B576FE2721E423E60016B39D /* Dictionary+StringOrEmpty.swift */, B576FE0D21E1D6310016B39D /* String+PercentEncoding.swift */, ); path = Extensions; @@ -639,7 +626,6 @@ EDFC76381B642A2E00D0DBD7 /* Frameworks */ = { isa = PBXGroup; children = ( - F83213A42173EF75008BA8A0 /* Cocoa.framework */, F83213A62173EF75008BA8A0 /* CommerceKit.framework */, F83213A52173EF75008BA8A0 /* StoreFoundation.framework */, ); @@ -703,8 +689,6 @@ F8FB719D20F2EC4500F56FDC /* CKPurchaseController.h */, F8FB719E20F2EC4500F56FDC /* CKServiceInterface.h */, F8FB719F20F2EC4500F56FDC /* CKSoftwareMap.h */, - F8FB71A020F2EC4500F56FDC /* CKUpdateController.h */, - F8FB71A120F2EC4500F56FDC /* ISStoreURLOperationDelegate.h */, F8FB71B320F2EC7900F56FDC /* module.modulemap */, ); path = CommerceKit; @@ -718,12 +702,10 @@ F8FB71A520F2EC4500F56FDC /* ISAccountService.h */, F8FB71A620F2EC4500F56FDC /* ISAuthenticationContext.h */, B578F060224FB5BD00D2086A /* ISAuthenticationResponse.h */, - F8FB71A720F2EC4500F56FDC /* ISOperationDelegate.h */, F8FB71A820F2EC4500F56FDC /* ISServiceProxy.h */, F8FB71A920F2EC4500F56FDC /* ISServiceRemoteObject.h */, F8FB71AA20F2EC4500F56FDC /* ISStoreAccount.h */, F8FB71AB20F2EC4500F56FDC /* ISStoreClient.h */, - F8FB71AC20F2EC4500F56FDC /* ISURLOperationDelegate.h */, F8FB71B420F2EC8800F56FDC /* module.modulemap */, F8FB71AD20F2EC4500F56FDC /* SSDownload.h */, F8FB71AE20F2EC4500F56FDC /* SSDownloadMetadata.h */, @@ -751,17 +733,13 @@ F832138E2173D3E1008BA8A0 /* CKSoftwareMap.h in Headers */, F83213922173D5AB008BA8A0 /* CKSoftwareProduct.h in Headers */, F83213932173D5AB008BA8A0 /* CKUpdate.h in Headers */, - F832138F2173D3E1008BA8A0 /* CKUpdateController.h in Headers */, F83213942173D5AB008BA8A0 /* ISAccountService.h in Headers */, F83213952173D5AB008BA8A0 /* ISAuthenticationContext.h in Headers */, B578F061224FB5BD00D2086A /* ISAuthenticationResponse.h in Headers */, - F83213962173D5AB008BA8A0 /* ISOperationDelegate.h in Headers */, F83213972173D5AB008BA8A0 /* ISServiceProxy.h in Headers */, F83213982173D5AB008BA8A0 /* ISServiceRemoteObject.h in Headers */, F83213992173D5AB008BA8A0 /* ISStoreAccount.h in Headers */, F832139A2173D5AB008BA8A0 /* ISStoreClient.h in Headers */, - F83213902173D3E1008BA8A0 /* ISStoreURLOperationDelegate.h in Headers */, - F832139B2173D5AB008BA8A0 /* ISURLOperationDelegate.h in Headers */, F8FB716220F2B41400F56FDC /* MasKit.h in Headers */, F832139C2173D5B2008BA8A0 /* SSDownload.h in Headers */, F832139D2173D5B2008BA8A0 /* SSDownloadMetadata.h in Headers */, @@ -811,6 +789,9 @@ dependencies = ( ); name = MasKit; + packageProductDependencies = ( + C56C4FF4262A50F5004F37EB /* Version */, + ); productName = MasKit; productReference = F8FB715220F2B41400F56FDC /* MasKit.framework */; productType = "com.apple.product-type.framework"; @@ -871,6 +852,9 @@ Base, ); mainGroup = ED031A6F1B5127C00097692E; + packageReferences = ( + C56C4FF0262A4ED0004F37EB /* XCRemoteSwiftPackageReference "Version" */, + ); productRefGroup = ED031A791B5127C00097692E /* Products */; projectDirPath = ""; projectRoot = ""; @@ -946,7 +930,6 @@ B594B12721D5825800F3AC59 /* AppLibrary.swift in Sources */, F85DA8B2240CBAFE00FE5650 /* CKSoftwareMap+SoftwareMap.swift in Sources */, B594B12B21D5837200F3AC59 /* CKSoftwareProduct+SoftwareProduct.swift in Sources */, - B576FE2821E423E60016B39D /* Dictionary+StringOrEmpty.swift in Sources */, F8FB716A20F2B4DD00F56FDC /* Downloader.swift in Sources */, B588CE0221DC89490047D305 /* ExternalCommand.swift in Sources */, B594B14C21D8983700F3AC59 /* Home.swift in Sources */, @@ -959,7 +942,6 @@ F8FB717B20F2B4DD00F56FDC /* MASError.swift in Sources */, B594B15221D89A8B00F3AC59 /* MasStoreSearch.swift in Sources */, B576FE0421E113E90016B39D /* NetworkManager.swift in Sources */, - B576FE0821E114A80016B39D /* NetworkResult.swift in Sources */, B576FE0021E113610016B39D /* NetworkSession.swift in Sources */, B5DBF80D21DEE4E600F3B151 /* Open.swift in Sources */, B576FDF721E107AA0016B39D /* OpenSystemCommand.swift in Sources */, @@ -1420,6 +1402,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + C56C4FF0262A4ED0004F37EB /* XCRemoteSwiftPackageReference "Version" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/mxcl/Version.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C56C4FF4262A50F5004F37EB /* Version */ = { + isa = XCSwiftPackageProductDependency; + package = C56C4FF0262A4ED0004F37EB /* XCRemoteSwiftPackageReference "Version" */; + productName = Version; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = ED031A701B5127C00097692E /* Project object */; } diff --git a/mas.xcworkspace/xcshareddata/swiftpm/Package.resolved b/mas.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..5c4f7c3d6 --- /dev/null +++ b/mas.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "Version", + "repositoryURL": "https://github.com/mxcl/Version.git", + "state": { + "branch": null, + "revision": "a94b48f36763c05629fc102837398505032dead9", + "version": "2.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/mas/main.swift b/mas/main.swift index a477332f0..a91e01088 100644 --- a/mas/main.swift +++ b/mas/main.swift @@ -10,8 +10,8 @@ import Commandant import Foundation import MasKit -public struct StderrOutputStream: TextOutputStream { - public mutating func write(_ string: String) { +struct StderrOutputStream: TextOutputStream { + mutating func write(_ string: String) { fputs(string, stderr) } }