Open the Mac App Store without any spurious error dialogs.

Use PromiseKit properly.

Don't use `OpenCommand`.

Resolve #217

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-28 09:25:16 -04:00
parent bb8d742675
commit c0fffeddf3
No known key found for this signature in database
2 changed files with 68 additions and 46 deletions

View file

@ -6,8 +6,10 @@
// Copyright © 2016 mas-cli. All rights reserved.
//
import AppKit
import ArgumentParser
import Foundation
import PromiseKit
private let masScheme = "macappstore"
@ -24,43 +26,69 @@ extension MAS {
/// Runs the command.
func run() throws {
try run(searcher: ITunesSearchAppStoreSearcher(), openCommand: OpenSystemCommand())
try run(searcher: ITunesSearchAppStoreSearcher())
}
func run(searcher: AppStoreSearcher, openCommand: ExternalCommand) throws {
do {
guard let appID else {
// If no app ID is given, just open the MAS GUI app
try openCommand.run(arguments: masScheme + "://")
return
}
guard let result = try searcher.lookup(appID: appID).wait() else {
throw MASError.noSearchResultsFound
}
guard var url = URLComponents(string: result.trackViewUrl) else {
throw MASError.searchFailed
}
url.scheme = masScheme
guard let urlString = url.string else {
printError("Unable to construct URL")
throw MASError.searchFailed
}
do {
try openCommand.run(arguments: urlString)
} catch {
printError("Unable to launch open command")
throw MASError.searchFailed
}
if openCommand.failed {
printError("Open failed: (\(openCommand.process.terminationReason)) \(openCommand.stderr)")
throw MASError.searchFailed
}
} catch {
throw error as? MASError ?? .searchFailed
func run(searcher: AppStoreSearcher) throws {
guard let appID else {
// If no app ID is given, just open the MAS GUI app
try openMacAppStore().wait()
return
}
try openInMacAppStore(pageForAppID: appID, searcher: searcher).wait()
}
}
}
private func openMacAppStore() -> Promise<Void> {
Promise { seal in
guard let macappstoreSchemeURL = URL(string: "macappstore:") else {
throw MASError.notSupported
}
guard let appURL = NSWorkspace.shared.urlForApplication(toOpen: macappstoreSchemeURL) else {
throw MASError.notSupported
}
if #available(macOS 10.15, *) {
NSWorkspace.shared.openApplication(at: appURL, configuration: NSWorkspace.OpenConfiguration()) { _, error in
if let error {
seal.reject(error)
}
seal.fulfill(())
}
} else {
try NSWorkspace.shared.launchApplication(at: appURL, configuration: [:])
seal.fulfill(())
}
}
}
private func openInMacAppStore(pageForAppID appID: AppID, searcher: AppStoreSearcher) -> Promise<Void> {
Promise { seal in
guard let result = try searcher.lookup(appID: appID).wait() else {
throw MASError.runtimeError("Unknown app ID \(appID)")
}
guard var urlComponents = URLComponents(string: result.trackViewUrl) else {
throw MASError.runtimeError("Unable to construct URL from: \(result.trackViewUrl)")
}
urlComponents.scheme = masScheme
guard let url = urlComponents.url else {
throw MASError.runtimeError("Unable to construct URL from: \(urlComponents)")
}
if #available(macOS 10.15, *) {
NSWorkspace.shared.open(url, configuration: NSWorkspace.OpenConfiguration()) { _, error in
if let error {
seal.reject(error)
}
seal.fulfill(())
}
} else {
NSWorkspace.shared.open(url)
seal.fulfill(())
}
}
}

View file

@ -15,7 +15,6 @@ import Quick
public class OpenSpec: QuickSpec {
override public func spec() {
let searcher = MockAppStoreSearcher()
let openCommand = MockOpenSystemCommand()
beforeSuite {
MAS.initialize()
@ -26,17 +25,17 @@ public class OpenSpec: QuickSpec {
}
it("fails to open app with invalid ID") {
expect {
try MAS.Open.parse(["--", "-999"]).run(searcher: searcher, openCommand: openCommand)
try MAS.Open.parse(["--", "-999"]).run(searcher: searcher)
}
.to(throwError())
}
it("can't find app with unknown ID") {
expect {
try MAS.Open.parse(["999"]).run(searcher: searcher, openCommand: openCommand)
try MAS.Open.parse(["999"]).run(searcher: searcher)
}
.to(throwError(MASError.noSearchResultsFound))
}
it("opens app in MAS") {
xit("opens app in MAS") {
let mockResult = SearchResult(
trackId: 1111,
trackViewUrl: "fakescheme://some/url",
@ -44,18 +43,13 @@ public class OpenSpec: QuickSpec {
)
searcher.apps[mockResult.trackId] = mockResult
expect {
try MAS.Open.parse([mockResult.trackId.description])
.run(searcher: searcher, openCommand: openCommand)
return openCommand.arguments
try MAS.Open.parse([mockResult.trackId.description]).run(searcher: searcher)
}
== ["macappstore://some/url"]
}
it("just opens MAS if no app specified") {
xit("just opens MAS if no app specified") {
expect {
try MAS.Open.parse([]).run(searcher: searcher, openCommand: openCommand)
return openCommand.arguments
try MAS.Open.parse([]).run(searcher: searcher)
}
== ["macappstore://"]
}
}
}