Skip to content

Commit

Permalink
Merge pull request #570 from rgoldberg/478-app-id
Browse files Browse the repository at this point in the history
Fix app IDs
  • Loading branch information
rgoldberg authored Oct 14, 2024
2 parents 43505db + 006273b commit 751e47b
Show file tree
Hide file tree
Showing 26 changed files with 85 additions and 95 deletions.
6 changes: 3 additions & 3 deletions Sources/mas/AppStore/Downloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import StoreFoundation
/// Only works for free apps. Defaults to false.
/// - Returns: A promise that completes when the downloads are complete. If any fail,
/// the promise is rejected with the first error, after all remaining downloads are attempted.
func downloadAll(_ appIDs: [UInt64], purchase: Bool = false) -> Promise<Void> {
func downloadAll(_ appIDs: [AppID], purchase: Bool = false) -> Promise<Void> {
var firstError: Error?
return appIDs.reduce(Guarantee<Void>.value(())) { previous, appID in
previous.then {
Expand All @@ -34,8 +34,8 @@ func downloadAll(_ appIDs: [UInt64], purchase: Bool = false) -> Promise<Void> {
}
}

private func downloadWithRetries(_ appID: UInt64, purchase: Bool = false, attempts: Int = 3) -> Promise<Void> {
SSPurchase().perform(adamId: appID, purchase: purchase)
private func downloadWithRetries(_ appID: AppID, purchase: Bool = false, attempts: Int = 3) -> Promise<Void> {
SSPurchase().perform(appID: appID, purchase: purchase)
.recover { error -> Promise<Void> in
guard attempts > 1 else {
throw error
Expand Down
8 changes: 4 additions & 4 deletions Sources/mas/AppStore/SSPurchase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import PromiseKit
import StoreFoundation

extension SSPurchase {
func perform(adamId: UInt64, purchase: Bool) -> Promise<Void> {
func perform(appID: AppID, purchase: Bool) -> Promise<Void> {
var parameters: [String: Any] = [
"productType": "C",
"price": 0,
"salableAdamId": adamId,
"salableAdamId": appID,
"pg": "default",
"appExtVrsId": 0,
]
Expand All @@ -34,7 +34,7 @@ extension SSPurchase {
}
.joined(separator: "&")

itemIdentifier = adamId
itemIdentifier = appID

// Not sure if this is needed…
if purchase {
Expand All @@ -43,7 +43,7 @@ extension SSPurchase {

downloadMetadata = SSDownloadMetadata()
downloadMetadata.kind = "software"
downloadMetadata.itemIdentifier = adamId
downloadMetadata.itemIdentifier = appID

// Monterey obscures the user's App Store account, but allows
// redownloads without passing any account IDs to SSPurchase.
Expand Down
4 changes: 2 additions & 2 deletions Sources/mas/Commands/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension Mas {
)

@Argument(help: "ID of app to show on MAS Preview")
var appId: Int
var appID: AppID

/// Runs the command.
func run() throws {
Expand All @@ -26,7 +26,7 @@ extension Mas {

func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do {
guard let result = try storeSearch.lookup(app: appId).wait() else {
guard let result = try storeSearch.lookup(appID: appID).wait() else {
throw MASError.noSearchResultsFound
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/mas/Commands/Info.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension Mas {
)

@Argument(help: "ID of app to show info")
var appId: Int
var appID: AppID

/// Runs the command.
func run() throws {
Expand All @@ -27,7 +27,7 @@ extension Mas {

func run(storeSearch: StoreSearch) throws {
do {
guard let result = try storeSearch.lookup(app: appId).wait() else {
guard let result = try storeSearch.lookup(appID: appID).wait() else {
throw MASError.noSearchResultsFound
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/mas/Commands/Install.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension Mas {
@Flag(help: "force reinstall")
var force = false
@Argument(help: "app ID(s) to install")
var appIds: [UInt64]
var appIDs: [AppID]

/// Runs the command.
func run() throws {
Expand All @@ -28,8 +28,8 @@ extension Mas {

func run(appLibrary: AppLibrary) throws {
// Try to download applications with given identifiers and collect results
let appIds = appIds.filter { appId in
if let product = appLibrary.installedApp(forId: appId), !force {
let appIDs = appIDs.filter { appID in
if let product = appLibrary.installedApp(withAppID: appID), !force {
printWarning("\(product.appName) is already installed")
return false
}
Expand All @@ -38,7 +38,7 @@ extension Mas {
}

do {
try downloadAll(appIds).wait()
try downloadAll(appIDs).wait()
} catch {
throw error as? MASError ?? .downloadFailed(error: error as NSError)
}
Expand Down
18 changes: 10 additions & 8 deletions Sources/mas/Commands/Lucky.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension Mas {
}

func run(appLibrary: AppLibrary, storeSearch: StoreSearch) throws {
var appId: Int?
var appID: AppID?

do {
let results = try storeSearch.search(for: appName).wait()
Expand All @@ -37,28 +37,30 @@ extension Mas {
throw MASError.noSearchResultsFound
}

appId = result.trackId
appID = result.trackId
} catch {
throw error as? MASError ?? .searchFailed
}

guard let identifier = appId else { fatalError() }
guard let appID else {
fatalError()
}

try install(UInt64(identifier), appLibrary: appLibrary)
try install(appID: appID, appLibrary: appLibrary)
}

/// Installs an app.
///
/// - Parameters:
/// - appId: App identifier
/// - appID: App identifier
/// - appLibrary: Library of installed apps
fileprivate func install(_ appId: UInt64, appLibrary: AppLibrary) throws {
fileprivate func install(appID: AppID, appLibrary: AppLibrary) throws {
// Try to download applications with given identifiers and collect results
if let product = appLibrary.installedApp(forId: appId), !force {
if let product = appLibrary.installedApp(withAppID: appID), !force {
printWarning("\(product.appName) is already installed")
} else {
do {
try downloadAll([appId]).wait()
try downloadAll([appID]).wait()
} catch {
throw error as? MASError ?? .downloadFailed(error: error as NSError)
}
Expand Down
13 changes: 3 additions & 10 deletions Sources/mas/Commands/Open.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import ArgumentParser
import Foundation

private let markerValue = "appstore"
private let masScheme = "macappstore"

extension Mas {
Expand All @@ -21,7 +20,7 @@ extension Mas {
)

@Argument(help: "the app ID")
var appId: String = markerValue
var appID: AppID?

/// Runs the command.
func run() throws {
Expand All @@ -30,19 +29,13 @@ extension Mas {

func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do {
if appId == markerValue {
guard let appID else {
// If no app ID is given, just open the MAS GUI app
try openCommand.run(arguments: masScheme + "://")
return
}

guard let appId = Int(appId)
else {
printError("Invalid app ID")
throw MASError.noSearchResultsFound
}

guard let result = try storeSearch.lookup(app: appId).wait()
guard let result = try storeSearch.lookup(appID: appID).wait()
else {
throw MASError.noSearchResultsFound
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/mas/Commands/Outdated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension Mas {
fulfilled:
appLibrary.installedApps.map { installedApp in
firstly {
storeSearch.lookup(app: installedApp.itemIdentifier.intValue)
storeSearch.lookup(appID: installedApp.itemIdentifier.uint64Value)
}.done { storeApp in
guard let storeApp else {
if verbose {
Expand Down
8 changes: 4 additions & 4 deletions Sources/mas/Commands/Purchase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extension Mas {
)

@Argument(help: "app ID(s) to install")
var appIds: [UInt64]
var appIDs: [AppID]

/// Runs the command.
func run() throws {
Expand All @@ -25,8 +25,8 @@ extension Mas {

func run(appLibrary: AppLibrary) throws {
// Try to download applications with given identifiers and collect results
let appIds = appIds.filter { appId in
if let product = appLibrary.installedApp(forId: appId) {
let appIDs = appIDs.filter { appID in
if let product = appLibrary.installedApp(withAppID: appID) {
printWarning("\(product.appName) has already been purchased.")
return false
}
Expand All @@ -35,7 +35,7 @@ extension Mas {
}

do {
try downloadAll(appIds, purchase: true).wait()
try downloadAll(appIDs, purchase: true).wait()
} catch {
throw error as? MASError ?? .downloadFailed(error: error as NSError)
}
Expand Down
6 changes: 2 additions & 4 deletions Sources/mas/Commands/Uninstall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@ extension Mas {
@Flag(help: "dry run")
var dryRun = false
@Argument(help: "ID of app to uninstall")
var appId: Int
var appID: AppID

/// Runs the uninstall command.
func run() throws {
try run(appLibrary: MasAppLibrary())
}

func run(appLibrary: AppLibrary) throws {
let appId = UInt64(appId)

guard let product = appLibrary.installedApp(forId: appId) else {
guard let product = appLibrary.installedApp(withAppID: appID) else {
throw MASError.notInstalled
}

Expand Down
16 changes: 8 additions & 8 deletions Sources/mas/Commands/Upgrade.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension Mas {
)

@Argument(help: "app(s) to upgrade")
var appIds: [String] = []
var appIDs: [String] = []

/// Runs the command.
func run() throws {
Expand Down Expand Up @@ -55,22 +55,22 @@ extension Mas {
storeSearch: StoreSearch
) throws -> [(SoftwareProduct, SearchResult)] {
let apps: [SoftwareProduct] =
appIds.isEmpty
appIDs.isEmpty
? appLibrary.installedApps
: appIds.compactMap {
if let appId = UInt64($0) {
// if argument a UInt64, lookup app by id using argument
return appLibrary.installedApp(forId: appId)
: appIDs.compactMap {
if let appID = AppID($0) {
// if argument an AppID, lookup app by id using argument
return appLibrary.installedApp(withAppID: appID)
} else {
// if argument not a UInt64, lookup app by name using argument
// if argument not an AppID, lookup app by name using argument
return appLibrary.installedApp(named: $0)
}
}

let promises = apps.map { installedApp in
// only upgrade apps whose local version differs from the store version
firstly {
storeSearch.lookup(app: installedApp.itemIdentifier.intValue)
storeSearch.lookup(appID: installedApp.itemIdentifier.uint64Value)
}.map { result -> (SoftwareProduct, SearchResult)? in
guard let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) else {
return nil
Expand Down
4 changes: 2 additions & 2 deletions Sources/mas/Commands/Vendor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension Mas {
)

@Argument(help: "the app ID to show the vendor's website")
var appId: Int
var appID: AppID

/// Runs the command.
func run() throws {
Expand All @@ -26,7 +26,7 @@ extension Mas {

func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do {
guard let result = try storeSearch.lookup(app: appId).wait()
guard let result = try storeSearch.lookup(appID: appID).wait()
else {
throw MASError.noSearchResultsFound
}
Expand Down
12 changes: 6 additions & 6 deletions Sources/mas/Controllers/AppLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ protocol AppLibrary {

/// Finds an app by ID.
///
/// - Parameter forId: MAS ID for app.
/// - Parameter withAppID: MAS ID for app.
/// - Returns: Software Product of app if found; nil otherwise.
func installedApp(forId: UInt64) -> SoftwareProduct?
func installedApp(withAppID appID: AppID) -> SoftwareProduct?

/// Uninstalls an app.
///
Expand All @@ -30,11 +30,11 @@ protocol AppLibrary {
extension AppLibrary {
/// Finds an app by ID.
///
/// - Parameter forId: MAS ID for app.
/// - Parameter withAppID: MAS ID for app.
/// - Returns: Software Product of app if found; nil otherwise.
func installedApp(forId identifier: UInt64) -> SoftwareProduct? {
let appId = NSNumber(value: identifier)
return installedApps.first { $0.itemIdentifier == appId }
func installedApp(withAppID appID: AppID) -> SoftwareProduct? {
let appID = NSNumber(value: appID)
return installedApps.first { $0.itemIdentifier == appID }
}

/// Finds an app by name.
Expand Down
10 changes: 5 additions & 5 deletions Sources/mas/Controllers/MasStoreSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,20 @@ class MasStoreSearch: StoreSearch {
}

// Combine the results, removing any duplicates.
var seenAppIDs = Set<Int>()
var seenAppIDs = Set<AppID>()
return when(fulfilled: results).flatMapValues { $0 }.filterValues { result in
seenAppIDs.insert(result.trackId).inserted
}
}

/// Looks up app details.
///
/// - Parameter appId: MAS ID of app
/// - Parameter appID: MAS ID of app
/// - Returns: A Promise for 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) -> Promise<SearchResult?> {
guard let url = lookupURL(forApp: appId, inCountry: country) else {
fatalError("Failed to build URL for \(appId)")
func lookup(appID: AppID) -> Promise<SearchResult?> {
guard let url = lookupURL(forAppID: appID, inCountry: country) else {
fatalError("Failed to build URL for \(appID)")
}
return firstly {
loadSearchResults(url)
Expand Down
Loading

0 comments on commit 751e47b

Please sign in to comment.