Skip to content

Commit f0c7e02

Browse files
authored
Implement --purchase flag in download command (#57)
1 parent cd54b3b commit f0c7e02

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ To download a copy of the ipa file, use the `download` command.
107107
```
108108
OVERVIEW: Download (encrypted) iOS app packages from the App Store.
109109
110-
USAGE: ipatool download --bundle-identifier <bundle-identifier> [--country <country>] [--device-family <device-family>] [--output <output>] [--log-level <log-level>]
110+
USAGE: ipatool download --bundle-identifier <bundle-identifier> [--country <country>] [--device-family <device-family>] [--output <output>] [--log-level <log-level>] [--purchase]
111111
112112
OPTIONS:
113113
-b, --bundle-identifier <bundle-identifier>
@@ -119,6 +119,7 @@ OPTIONS:
119119
(default: iPhone)
120120
-o, --output <output> The destination path of the downloaded app package.
121121
--log-level <log-level> The log level. (default: info)
122+
--purchase Obtain a license for the app if needed.
122123
--version Show the version.
123124
-h, --help Show help information.
124125
```

Sources/CLI/Commands/Download.swift

+54-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ struct Download: AsyncParsableCommand {
3434
@Option(name: [.long], help: "The log level.")
3535
private var logLevel: LogLevel = .info
3636

37+
@Flag(name: .long, help: "Obtain a license for the app if needed.")
38+
private var purchase: Bool = false
39+
3740
lazy var logger = ConsoleLogger(level: logLevel)
3841
}
3942

@@ -66,7 +69,47 @@ extension Download {
6669
}
6770
}
6871

69-
private mutating func item(from app: iTunesResponse.Result, account: Account) async -> StoreResponse.Item {
72+
73+
private mutating func purchase(app: iTunesResponse.Result, account: Account) async {
74+
logger.log("Creating HTTP client...", level: .debug)
75+
let httpClient = HTTPClient(session: URLSession.shared)
76+
77+
logger.log("Creating App Store client...", level: .debug)
78+
let storeClient = StoreClient(httpClient: httpClient)
79+
80+
do {
81+
logger.log("Obtaining a license for '\(app.identifier)' from the App Store...", level: .info)
82+
try await storeClient.purchase(
83+
identifier: "\(app.identifier)",
84+
directoryServicesIdentifier: account.directoryServicesIdentifier,
85+
passwordToken: account.passwordToken,
86+
countryCode: countryCode
87+
)
88+
} catch {
89+
logger.log("\(error)", level: .debug)
90+
91+
switch error {
92+
case StoreClient.Error.purchaseFailed:
93+
logger.log("Purchase failed.", level: .error)
94+
case StoreClient.Error.duplicateLicense:
95+
logger.log("A license already exists for this item.", level: .error)
96+
case StoreResponse.Error.invalidCountry:
97+
logger.log("The country provided does not match with the account you are using. Supply a valid country using the \"--country\" flag.", level: .error)
98+
case StoreResponse.Error.passwordTokenExpired:
99+
logger.log("Token expired. Login again using the \"auth\" command.", level: .error)
100+
default:
101+
logger.log("An unknown error has occurred.", level: .error)
102+
}
103+
104+
_exit(1)
105+
}
106+
}
107+
108+
private mutating func item(
109+
from app: iTunesResponse.Result,
110+
account: Account,
111+
purchaseAttempted: Bool = false
112+
) async -> StoreResponse.Item {
70113
logger.log("Creating HTTP client...", level: .debug)
71114
let httpClient = HTTPClient(session: URLSession.shared)
72115

@@ -88,7 +131,16 @@ extension Download {
88131
case StoreResponse.Error.invalidItem:
89132
logger.log("Received invalid store item.", level: .error)
90133
case StoreResponse.Error.invalidLicense:
91-
logger.log("Your Apple ID does not have a license for this app. Use the \"purchase\" command to obtain a license.", level: .error)
134+
if !purchaseAttempted, purchase {
135+
logger.log("License is missing.", level: .info)
136+
137+
await purchase(app: app, account: account)
138+
logger.log("Obtained a license for '\(app.identifier)'.", level: .debug)
139+
140+
return await item(from: app, account: account, purchaseAttempted: true)
141+
} else {
142+
logger.log("Your Apple ID does not have a license for this app. Use the \"purchase\" command or the \"--purchase\" to obtain a license.", level: .error)
143+
}
92144
case StoreResponse.Error.invalidCountry:
93145
logger.log("The country provided does not match with the account you are using. Supply a valid country using the \"--country\" flag.", level: .error)
94146
case StoreResponse.Error.passwordTokenExpired:

Sources/CLI/Commands/Purchase.swift

-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ extension Purchase {
9696

9797
_exit(1)
9898
}
99-
10099
}
101100

102101
mutating func run() async throws {

0 commit comments

Comments
 (0)