mirror of
https://github.com/mas-cli/mas
synced 2024-11-25 04:50:24 +00:00
Merge pull request #272 from mas-cli/purchase-cleanup
🧹 Purchase cleanup
This commit is contained in:
commit
38f20a8607
39 changed files with 141 additions and 136 deletions
|
@ -8,4 +8,3 @@
|
|||
excluded:
|
||||
- Carthage
|
||||
- docs
|
||||
- MasKitTests
|
||||
|
|
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- ✨ New `purchase` command for purchasing free apps #264 (resolves #2, #145)
|
||||
thanks, [@blochberger](https://github.com/blochberger)!
|
||||
- 🐟 Seriously more interactive fish completions #242
|
||||
thanks, [@lwolfsonkin](https://github.com/lwolfsonkin)!
|
||||
- 🧹 Purchase cleanup #272
|
||||
- ♻️ SoftwareMap Protocol #271
|
||||
- 🕊 Swift 5 #255
|
||||
- ⚒️ Xcode 10.2 and macOS 10.14 required to build
|
||||
|
@ -14,10 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- ⬆️ Nimble (8.0.4) #255
|
||||
- ⬆️ Quick (2.2.0) #255
|
||||
- ➖ Result #255
|
||||
- Seriously more interactive fish completions #242
|
||||
thanks, [@lwolfsonkin](https://github.com/lwolfsonkin)!
|
||||
- 💡 Update readme with simpler tap usage #241
|
||||
- Added support for purchasing apps (#2, #145)
|
||||
|
||||
## [v1.6.4] 🔎 Search Fix - 2020-05-11
|
||||
|
||||
|
|
|
@ -12,15 +12,17 @@ import StoreFoundation
|
|||
/// Monitors app download progress.
|
||||
///
|
||||
/// - Parameter adamId: An app ID?
|
||||
/// - Parameter purchase: Flag indicating whether the app needs to be purchased.
|
||||
/// Only works for free apps. Defaults to false.
|
||||
/// - Returns: An error, if one occurred.
|
||||
func download(_ adamId: UInt64, isPurchase: Bool) -> MASError? {
|
||||
func download(_ adamId: UInt64, purchase: Bool = false) -> MASError? {
|
||||
guard let account = ISStoreAccount.primaryAccount else {
|
||||
return .notSignedIn
|
||||
}
|
||||
|
||||
guard let storeAccount = account as? ISStoreAccount
|
||||
else { fatalError("Unable to cast StoreAccount to ISStoreAccount") }
|
||||
let purchase = SSPurchase(adamId: adamId, account: storeAccount, isPurchase: isPurchase)
|
||||
let purchase = SSPurchase(adamId: adamId, account: storeAccount, purchase: purchase)
|
||||
|
||||
var purchaseError: MASError?
|
||||
var observerIdentifier: CKDownloadQueueObserver?
|
||||
|
|
|
@ -13,7 +13,7 @@ typealias SSPurchaseCompletion =
|
|||
(_ purchase: SSPurchase?, _ completed: Bool, _ error: Error?, _ response: SSPurchaseResponse?) -> Void
|
||||
|
||||
extension SSPurchase {
|
||||
convenience init(adamId: UInt64, account: ISStoreAccount, isPurchase: Bool) {
|
||||
convenience init(adamId: UInt64, account: ISStoreAccount, purchase: Bool = false) {
|
||||
self.init()
|
||||
|
||||
var parameters: [String: Any] = [
|
||||
|
@ -24,7 +24,7 @@ extension SSPurchase {
|
|||
"appExtVrsId": 0
|
||||
]
|
||||
|
||||
if isPurchase {
|
||||
if purchase {
|
||||
parameters["macappinstalledconfirmed"] = 1
|
||||
parameters["pricingParameters"] = "STDQ"
|
||||
|
||||
|
@ -42,7 +42,7 @@ extension SSPurchase {
|
|||
appleID = account.identifier
|
||||
|
||||
// Not sure if this is needed, but lets use it here.
|
||||
if isPurchase {
|
||||
if purchase {
|
||||
isRedownload = false
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,7 @@ public struct HomeCommand: CommandProtocol {
|
|||
/// Runs the command.
|
||||
public func run(_ options: HomeOptions) -> Result<(), MASError> {
|
||||
do {
|
||||
guard let result = try storeSearch.lookup(app: options.appId)
|
||||
else {
|
||||
guard let result = try storeSearch.lookup(app: options.appId) else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ public struct InfoCommand: CommandProtocol {
|
|||
/// Runs the command.
|
||||
public func run(_ options: InfoOptions) -> Result<(), MASError> {
|
||||
do {
|
||||
guard let result = try storeSearch.lookup(app: options.appId)
|
||||
else {
|
||||
guard let result = try storeSearch.lookup(app: options.appId) else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ public struct InstallCommand: CommandProtocol {
|
|||
private let appLibrary: AppLibrary
|
||||
|
||||
/// Public initializer.
|
||||
/// - Parameter appLibrary: AppLibrary manager.
|
||||
public init() {
|
||||
self.init(appLibrary: MasAppLibrary())
|
||||
}
|
||||
|
@ -38,7 +37,7 @@ public struct InstallCommand: CommandProtocol {
|
|||
return nil
|
||||
}
|
||||
|
||||
return download(appId, isPurchase: false)
|
||||
return download(appId)
|
||||
}
|
||||
|
||||
switch downloadResults.count {
|
||||
|
|
|
@ -73,7 +73,7 @@ public struct LuckyCommand: CommandProtocol {
|
|||
return nil
|
||||
}
|
||||
|
||||
return download(appId, isPurchase: false)
|
||||
return download(appId)
|
||||
}
|
||||
|
||||
switch downloadResults.count {
|
||||
|
|
|
@ -14,20 +14,29 @@ public struct PurchaseCommand: CommandProtocol {
|
|||
public let verb = "purchase"
|
||||
public let function = "Purchase and download free apps from the Mac App Store"
|
||||
|
||||
/// Designated initializer.
|
||||
private let appLibrary: AppLibrary
|
||||
|
||||
/// Public initializer.
|
||||
public init() {
|
||||
self.init(appLibrary: MasAppLibrary())
|
||||
}
|
||||
|
||||
/// Internal initializer.
|
||||
/// - Parameter appLibrary: AppLibrary manager.
|
||||
init(appLibrary: AppLibrary = MasAppLibrary()) {
|
||||
self.appLibrary = appLibrary
|
||||
}
|
||||
|
||||
/// Runs the command.
|
||||
public func run(_ options: Options) -> Result<(), MASError> {
|
||||
// Try to download applications with given identifiers and collect results
|
||||
let downloadResults = options.appIds.compactMap { (appId) -> MASError? in
|
||||
if let product = installedApp(appId) {
|
||||
if let product = appLibrary.installedApp(forId: appId) {
|
||||
printWarning("\(product.appName) has already been purchased.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return download(appId, isPurchase: true)
|
||||
return download(appId, purchase: true)
|
||||
}
|
||||
|
||||
switch downloadResults.count {
|
||||
|
@ -39,13 +48,6 @@ public struct PurchaseCommand: CommandProtocol {
|
|||
return .failure(.downloadFailed(error: nil))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func installedApp(_ appId: UInt64) -> CKSoftwareProduct? {
|
||||
let appId = NSNumber(value: appId)
|
||||
|
||||
let softwareMap = CKSoftwareMap.shared()
|
||||
return softwareMap.allProducts()?.first { $0.itemIdentifier == appId }
|
||||
}
|
||||
}
|
||||
|
||||
public struct PurchaseOptions: OptionsProtocol {
|
||||
|
|
|
@ -71,7 +71,7 @@ public struct UpgradeCommand: CommandProtocol {
|
|||
print(updates.map({ "\($0.title) (\($0.bundleVersion))" }).joined(separator: ", "))
|
||||
|
||||
let updateResults = updates.compactMap {
|
||||
download($0.itemIdentifier.uint64Value, isPurchase: false)
|
||||
download($0.itemIdentifier.uint64Value)
|
||||
}
|
||||
|
||||
switch updateResults.count {
|
||||
|
|
|
@ -20,9 +20,6 @@ extension NetworkResult: Equatable {
|
|||
case let (.failure(error1), .failure(error2)):
|
||||
return error1.localizedDescription == error2.localizedDescription
|
||||
|
||||
// case (.none, .none):
|
||||
// return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
|
10
MasKitTests/.swiftlint.yml
Normal file
10
MasKitTests/.swiftlint.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# .swiftlint.yml
|
||||
# MasKitTests
|
||||
#
|
||||
# https://github.com/realm/SwiftLint#configuration
|
||||
#
|
||||
---
|
||||
disabled_rules:
|
||||
- force_cast
|
||||
- function_body_length
|
|
@ -48,15 +48,9 @@ struct SoftwareMapMock: SoftwareMap {
|
|||
}
|
||||
|
||||
func product(for bundleIdentifier: String) -> SoftwareProduct? {
|
||||
for product in products {
|
||||
if product.bundleIdentifier == bundleIdentifier {
|
||||
for product in products where product.bundleIdentifier == bundleIdentifier {
|
||||
return product
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//public func == (lhs: Expectation<[SoftwareProduct]>, rhs: [SoftwareProduct]) {
|
||||
// lhs.to(beCloseTo(rhs))
|
||||
//}
|
||||
|
|
|
@ -14,6 +14,7 @@ class TestURLSessionDelegate: NSObject, URLSessionDelegate {
|
|||
didReceive challenge: URLAuthenticationChallenge,
|
||||
completionHandler: (URLSession.AuthChallengeDisposition,
|
||||
URLCredential?) -> Void) {
|
||||
|
||||
// For example, you may want to override this to accept some self-signed certs here.
|
||||
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
|
||||
Constants.selfSignedHosts.contains(challenge.protectionSpace.host) {
|
||||
|
|
|
@ -19,6 +19,7 @@ fi
|
|||
# : command: usage: command [-pVv] command [arg ...]
|
||||
|
||||
if command -v swiftlint > /dev/null; then
|
||||
swiftlint autocorrect --format
|
||||
swiftlint lint --quiet
|
||||
else
|
||||
echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
|
||||
|
|
Loading…
Reference in a new issue