diff --git a/Sources/mas/Commands/Home.swift b/Sources/mas/Commands/Home.swift index be04111..18a7a53 100644 --- a/Sources/mas/Commands/Home.swift +++ b/Sources/mas/Commands/Home.swift @@ -10,7 +10,7 @@ import ArgumentParser extension Mas { /// Opens app page on MAS Preview. Uses the iTunes Lookup API: - /// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup + /// https://performance-partners.apple.com/search-api struct Home: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Opens MAS Preview app page in a browser" diff --git a/Sources/mas/Commands/Info.swift b/Sources/mas/Commands/Info.swift index 0e6b71d..ca862d2 100644 --- a/Sources/mas/Commands/Info.swift +++ b/Sources/mas/Commands/Info.swift @@ -11,7 +11,7 @@ import Foundation extension Mas { /// Displays app details. Uses the iTunes Lookup API: - /// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup + /// https://performance-partners.apple.com/search-api struct Info: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Display app information from the Mac App Store" diff --git a/Sources/mas/Commands/Open.swift b/Sources/mas/Commands/Open.swift index e2a143f..4cc0c99 100644 --- a/Sources/mas/Commands/Open.swift +++ b/Sources/mas/Commands/Open.swift @@ -13,7 +13,7 @@ private let masScheme = "macappstore" extension Mas { /// Opens app page in MAS app. Uses the iTunes Lookup API: - /// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup + /// https://performance-partners.apple.com/search-api struct Open: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Opens app page in AppStore.app" diff --git a/Sources/mas/Commands/Search.swift b/Sources/mas/Commands/Search.swift index ec9e344..036c61c 100644 --- a/Sources/mas/Commands/Search.swift +++ b/Sources/mas/Commands/Search.swift @@ -10,7 +10,7 @@ import ArgumentParser extension Mas { /// Search the Mac App Store using the iTunes Search API: - /// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/ + /// https://performance-partners.apple.com/search-api struct Search: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Search for apps from the Mac App Store" diff --git a/Sources/mas/Commands/Vendor.swift b/Sources/mas/Commands/Vendor.swift index 546865e..bdf13b1 100644 --- a/Sources/mas/Commands/Vendor.swift +++ b/Sources/mas/Commands/Vendor.swift @@ -10,7 +10,7 @@ import ArgumentParser extension Mas { /// Opens vendor's app page in a browser. Uses the iTunes Lookup API: - /// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup + /// https://performance-partners.apple.com/search-api struct Vendor: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Opens vendor's app page in a browser" diff --git a/Sources/mas/Controllers/MasStoreSearch.swift b/Sources/mas/Controllers/MasStoreSearch.swift index b921d1e..9cf2405 100644 --- a/Sources/mas/Controllers/MasStoreSearch.swift +++ b/Sources/mas/Controllers/MasStoreSearch.swift @@ -19,7 +19,7 @@ class MasStoreSearch: StoreSearch { // into the App Store. Instead, we'll make an educated guess that it matches the currently // selected locale in macOS. This obviously isn't always going to match, but it's probably // better than passing no "country" at all to the iTunes Search API. - // https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/ + // https://performance-partners.apple.com/search-api private let country: String? private let networkManager: NetworkManager @@ -40,7 +40,7 @@ class MasStoreSearch: StoreSearch { func search(for appName: String) -> Promise<[SearchResult]> { // Search for apps for compatible platforms, in order of preference. // Macs with Apple Silicon can run iPad and iPhone apps. - var entities = [Entity.macSoftware] + var entities = [Entity.desktopSoftware] if SysCtlSystemCommand.isAppleSilicon { entities += [.iPadSoftware, .iPhoneSoftware] } diff --git a/Sources/mas/Controllers/StoreSearch.swift b/Sources/mas/Controllers/StoreSearch.swift index b04ef13..e906cd9 100644 --- a/Sources/mas/Controllers/StoreSearch.swift +++ b/Sources/mas/Controllers/StoreSearch.swift @@ -16,53 +16,73 @@ protocol StoreSearch { } enum Entity: String { + case desktopSoftware case macSoftware case iPadSoftware case iPhoneSoftware = "software" } +private enum URLAction { + case lookup + case search + + var queryItemName: String { + switch self { + case .lookup: + return "id" + case .search: + return "term" + } + } +} + // MARK: - Common methods extension StoreSearch { /// 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. - func searchURL(for appName: String, inCountry country: String?, ofEntity entity: Entity = .macSoftware) -> URL? { - guard var components = URLComponents(string: "https://itunes.apple.com/search") else { - return nil - } - - components.queryItems = [ - URLQueryItem(name: "media", value: "software"), - URLQueryItem(name: "entity", value: entity.rawValue), - URLQueryItem(name: "term", value: appName), - ] - - if let country { - components.queryItems!.append(URLQueryItem(name: "country", value: country)) - } - - return components.url + /// - Parameter searchTerm: term for which to search in MAS. + /// - Returns: URL for the search service or nil if searchTerm can't be encoded. + func searchURL( + for searchTerm: String, + inCountry country: String?, + ofEntity entity: Entity = .desktopSoftware + ) -> URL? { + url(.search, searchTerm, inCountry: country, ofEntity: entity) } /// Builds the lookup URL for an app. /// /// - Parameter appID: MAS app identifier. /// - Returns: URL for the lookup service or nil if appID can't be encoded. - func lookupURL(forAppID appID: AppID, inCountry country: String?) -> URL? { - guard var components = URLComponents(string: "https://itunes.apple.com/lookup") else { + func lookupURL( + forAppID appID: AppID, + inCountry country: String?, + ofEntity entity: Entity = .desktopSoftware + ) -> URL? { + url(.lookup, String(appID), inCountry: country, ofEntity: entity) + } + + private func url( + _ action: URLAction, + _ queryItemValue: String, + inCountry country: String?, + ofEntity entity: Entity = .desktopSoftware + ) -> URL? { + guard var components = URLComponents(string: "https://itunes.apple.com/\(action)") else { return nil } components.queryItems = [ - URLQueryItem(name: "id", value: "\(appID)"), - URLQueryItem(name: "entity", value: "desktopSoftware"), + URLQueryItem(name: "media", value: "software"), + URLQueryItem(name: "entity", value: entity.rawValue), ] if let country { components.queryItems!.append(URLQueryItem(name: "country", value: country)) } + components.queryItems!.append(URLQueryItem(name: action.queryItemName, value: queryItemValue)) + return components.url } } diff --git a/Tests/masTests/Controllers/MasStoreSearchSpec.swift b/Tests/masTests/Controllers/MasStoreSearchSpec.swift index 8d9c6f7..9a3b7ff 100644 --- a/Tests/masTests/Controllers/MasStoreSearchSpec.swift +++ b/Tests/masTests/Controllers/MasStoreSearchSpec.swift @@ -18,17 +18,16 @@ public class MasStoreSearchSpec: QuickSpec { } describe("url string") { it("contains the app name") { - let appName = "myapp" expect { - MasStoreSearch().searchURL(for: appName, inCountry: "US")?.absoluteString + MasStoreSearch().searchURL(for: "myapp", inCountry: "US")?.absoluteString } - == "https://itunes.apple.com/search?media=software&entity=macSoftware&term=\(appName)&country=US" + == "https://itunes.apple.com/search?media=software&entity=desktopSoftware&country=US&term=myapp" } it("contains the encoded app name") { expect { MasStoreSearch().searchURL(for: "My App", inCountry: "US")?.absoluteString } - == "https://itunes.apple.com/search?media=software&entity=macSoftware&term=My%20App&country=US" + == "https://itunes.apple.com/search?media=software&entity=desktopSoftware&country=US&term=My%20App" } } describe("store") {