Begin to refactor the download process

This commit is contained in:
Andrew Naylor 2015-08-25 02:45:54 +08:00
parent 83291f2fc6
commit 41ceab2c3a
7 changed files with 93 additions and 32 deletions

View file

@ -21,10 +21,11 @@
ED0F23831B87533A00AE40CD /* ListInstalled.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23821B87533A00AE40CD /* ListInstalled.swift */; }; ED0F23831B87533A00AE40CD /* ListInstalled.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23821B87533A00AE40CD /* ListInstalled.swift */; };
ED0F23851B87536A00AE40CD /* ListUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23841B87536A00AE40CD /* ListUpdates.swift */; }; ED0F23851B87536A00AE40CD /* ListUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23841B87536A00AE40CD /* ListUpdates.swift */; };
ED0F23871B87537200AE40CD /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23861B87537200AE40CD /* Account.swift */; }; ED0F23871B87537200AE40CD /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23861B87537200AE40CD /* Account.swift */; };
ED0F23891B87543D00AE40CD /* DownloadQueueObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23881B87543D00AE40CD /* DownloadQueueObserver.swift */; }; ED0F23891B87543D00AE40CD /* PurchaseDownloadObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23881B87543D00AE40CD /* PurchaseDownloadObserver.swift */; };
ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238A1B87569C00AE40CD /* Downloader.swift */; }; ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238A1B87569C00AE40CD /* Downloader.swift */; };
ED0F238D1B8756E600AE40CD /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238C1B8756E600AE40CD /* Error.swift */; }; ED0F238D1B8756E600AE40CD /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238C1B8756E600AE40CD /* Error.swift */; };
ED0F23901B87A56F00AE40CD /* ISStoreAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */; }; ED0F23901B87A56F00AE40CD /* ISStoreAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */; };
EDA3BE521B8B84AF00C18D70 /* SSPurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA3BE511B8B84AF00C18D70 /* SSPurchase.swift */; };
EDEAA0C01B51CE6200F2FC3F /* StoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */; }; EDEAA0C01B51CE6200F2FC3F /* StoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */; };
EDEAA17D1B5C579100F2FC3F /* CommerceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */; }; EDEAA17D1B5C579100F2FC3F /* CommerceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */; };
F5F01044EC3065C6EBAB95D7 /* BoxType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E87CFA5E6371893D5B1807 /* BoxType.swift */; }; F5F01044EC3065C6EBAB95D7 /* BoxType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E87CFA5E6371893D5B1807 /* BoxType.swift */; };
@ -57,10 +58,11 @@
ED0F23821B87533A00AE40CD /* ListInstalled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListInstalled.swift; sourceTree = "<group>"; }; ED0F23821B87533A00AE40CD /* ListInstalled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListInstalled.swift; sourceTree = "<group>"; };
ED0F23841B87536A00AE40CD /* ListUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListUpdates.swift; sourceTree = "<group>"; }; ED0F23841B87536A00AE40CD /* ListUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListUpdates.swift; sourceTree = "<group>"; };
ED0F23861B87537200AE40CD /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; }; ED0F23861B87537200AE40CD /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; };
ED0F23881B87543D00AE40CD /* DownloadQueueObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadQueueObserver.swift; sourceTree = "<group>"; }; ED0F23881B87543D00AE40CD /* PurchaseDownloadObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseDownloadObserver.swift; sourceTree = "<group>"; };
ED0F238A1B87569C00AE40CD /* Downloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; }; ED0F238A1B87569C00AE40CD /* Downloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; };
ED0F238C1B8756E600AE40CD /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; }; ED0F238C1B8756E600AE40CD /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISStoreAccount.swift; sourceTree = "<group>"; }; ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISStoreAccount.swift; sourceTree = "<group>"; };
EDA3BE511B8B84AF00C18D70 /* SSPurchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSPurchase.swift; sourceTree = "<group>"; };
EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreFoundation.framework; path = /System/Library/PrivateFrameworks/StoreFoundation.framework; sourceTree = "<absolute>"; }; EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreFoundation.framework; path = /System/Library/PrivateFrameworks/StoreFoundation.framework; sourceTree = "<absolute>"; };
EDEAA0C31B51CEE400F2FC3F /* CDStructures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDStructures.h; sourceTree = "<group>"; }; EDEAA0C31B51CEE400F2FC3F /* CDStructures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDStructures.h; sourceTree = "<group>"; };
EDEAA0C41B51CEE400F2FC3F /* CKBook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKBook.h; sourceTree = "<group>"; }; EDEAA0C41B51CEE400F2FC3F /* CKBook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKBook.h; sourceTree = "<group>"; };
@ -351,8 +353,9 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED0F238A1B87569C00AE40CD /* Downloader.swift */, ED0F238A1B87569C00AE40CD /* Downloader.swift */,
ED0F23881B87543D00AE40CD /* DownloadQueueObserver.swift */,
ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */, ED0F238F1B87A56F00AE40CD /* ISStoreAccount.swift */,
ED0F23881B87543D00AE40CD /* PurchaseDownloadObserver.swift */,
EDA3BE511B8B84AF00C18D70 /* SSPurchase.swift */,
); );
path = AppStore; path = AppStore;
sourceTree = "<group>"; sourceTree = "<group>";
@ -637,7 +640,6 @@
F5F01044EC3065C6EBAB95D7 /* BoxType.swift in Sources */, F5F01044EC3065C6EBAB95D7 /* BoxType.swift in Sources */,
21EC092422A3EDFE33B153B8 /* Command.swift in Sources */, 21EC092422A3EDFE33B153B8 /* Command.swift in Sources */,
ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */, ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */,
ED0F23891B87543D00AE40CD /* DownloadQueueObserver.swift in Sources */,
ED0F238D1B8756E600AE40CD /* Error.swift in Sources */, ED0F238D1B8756E600AE40CD /* Error.swift in Sources */,
B80C5DDD38A8F7EB6F320697 /* Errors.swift in Sources */, B80C5DDD38A8F7EB6F320697 /* Errors.swift in Sources */,
5918483F96256CDAC88FF450 /* HelpCommand.swift in Sources */, 5918483F96256CDAC88FF450 /* HelpCommand.swift in Sources */,
@ -648,7 +650,9 @@
ED031A7C1B5127C00097692E /* main.swift in Sources */, ED031A7C1B5127C00097692E /* main.swift in Sources */,
DE39BCA91D1BC3D876711677 /* MutableBox.swift in Sources */, DE39BCA91D1BC3D876711677 /* MutableBox.swift in Sources */,
92828DCD99CED47F54242776 /* Option.swift in Sources */, 92828DCD99CED47F54242776 /* Option.swift in Sources */,
ED0F23891B87543D00AE40CD /* PurchaseDownloadObserver.swift in Sources */,
1CC607DA6B900AA3FEC3F6D8 /* Result.swift in Sources */, 1CC607DA6B900AA3FEC3F6D8 /* Result.swift in Sources */,
EDA3BE521B8B84AF00C18D70 /* SSPurchase.swift in Sources */,
7858BCFB4D5A4251DE998CE4 /* Switch.swift in Sources */, 7858BCFB4D5A4251DE998CE4 /* Switch.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View file

@ -6,25 +6,34 @@
// Copyright (c) 2015 Andrew Naylor. All rights reserved. // Copyright (c) 2015 Andrew Naylor. All rights reserved.
// //
typealias DownloadCompletion = (purchase: SSPurchase!, completed: Bool, error: NSError?, response: SSPurchaseResponse!) -> () func download(adamId: UInt64) -> MASError? {
func download(adamId: UInt64, completion:DownloadCompletion) { if let account = ISStoreAccount.primaryAccount {
let buyParameters = "productType=C&price=0&salableAdamId=\(adamId)&pricingParameters=STDRDL" let group = dispatch_group_create()
let purchase = SSPurchase() let purchase = SSPurchase(adamId: adamId, account: account)
purchase.buyParameters = buyParameters
purchase.itemIdentifier = adamId
purchase.accountIdentifier = primaryAccount().dsID
purchase.appleID = primaryAccount().identifier
let downloadMetadata = SSDownloadMetadata() var purchaseError: MASError?
downloadMetadata.kind = "software"
downloadMetadata.itemIdentifier = adamId
purchase.downloadMetadata = downloadMetadata purchase.perform { purchase, completed, error, response in
if completed {
let observer = PurchaseDownloadObserver(purchase: purchase)
observer.onCompletion {
dispatch_group_leave(group)
}
let purchaseController = CKPurchaseController.sharedPurchaseController() CKDownloadQueue.sharedDownloadQueue().addObserver(observer)
purchaseController.performPurchase(purchase, withOptions: 0, completionHandler: completion) }
while true { else {
NSRunLoop.mainRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: 10)) purchaseError = MASError(code: .PurchaseError, sourceError: error)
dispatch_group_leave(group)
}
}
dispatch_group_enter(group)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
return purchaseError
}
else {
return MASError(code: .NotSignedIn)
} }
} }

View file

@ -1,5 +1,5 @@
// //
// DownloadQueueObserver.swift // PurchaseDownloadObserver.swift
// mas-cli // mas-cli
// //
// Created by Andrew Naylor on 21/08/2015. // Created by Andrew Naylor on 21/08/2015.
@ -8,9 +8,15 @@
let csi = "\u{001B}[" let csi = "\u{001B}["
@objc class DownloadQueueObserver: CKDownloadQueueObserver { @objc class PurchaseDownloadObserver: CKDownloadQueueObserver {
let purchase: SSPurchase
var completionHandler: (() -> ())?
var started = false var started = false
init(purchase: SSPurchase) {
self.purchase = purchase
}
func downloadQueue(queue: CKDownloadQueue, statusChangedForDownload download: SSDownload!) { func downloadQueue(queue: CKDownloadQueue, statusChangedForDownload download: SSDownload!) {
if !started { if !started {
return return
@ -26,7 +32,13 @@ let csi = "\u{001B}["
func downloadQueue(queue: CKDownloadQueue, changedWithRemoval download: SSDownload!) { func downloadQueue(queue: CKDownloadQueue, changedWithRemoval download: SSDownload!) {
println("") println("")
println("==> Installed " + download.metadata.title) println("==> Installed " + download.metadata.title)
exit(EXIT_SUCCESS) if let complete = self.completionHandler {
complete()
}
}
func onCompletion(complete: () -> ()) {
self.completionHandler = complete
} }
} }

View file

@ -0,0 +1,29 @@
//
// SSPurchase.swift
// mas-cli
//
// Created by Andrew Naylor on 25/08/2015.
// Copyright (c) 2015 Andrew Naylor. All rights reserved.
//
typealias SSPurchaseCompletion = (purchase: SSPurchase!, completed: Bool, error: NSError?, response: SSPurchaseResponse!) -> ()
extension SSPurchase {
convenience init(adamId: UInt64, account: ISStoreAccount) {
self.init()
self.buyParameters = "productType=C&price=0&salableAdamId=\(adamId)&pricingParameters=STDRDL"
self.itemIdentifier = adamId
self.accountIdentifier = account.dsID
self.appleID = account.identifier
let downloadMetadata = SSDownloadMetadata()
downloadMetadata.kind = "software"
downloadMetadata.itemIdentifier = adamId
self.downloadMetadata = downloadMetadata
}
func perform(completion: SSPurchaseCompletion) {
CKPurchaseController.sharedPurchaseController().performPurchase(self, withOptions: 0, completionHandler: completion)
}
}

View file

@ -11,10 +11,18 @@ struct InstallCommand: CommandType {
let function = "Install from the Mac App Store" let function = "Install from the Mac App Store"
func run(mode: CommandMode) -> Result<(), CommandantError<MASError>> { func run(mode: CommandMode) -> Result<(), CommandantError<MASError>> {
return InstallOptions.evaluate(mode).map { options in let optionsResult = InstallOptions.evaluate(mode)
download(options.appId) { (purchase, completed, error, response) in
switch optionsResult {
case let .Failure(error):
return .Failure(error)
case let .Success(options):
if let error = download(options.value.appId) {
return .failure(CommandantError.CommandError(Box(error)))
} }
return .success(())
} }
} }
} }

View file

@ -13,6 +13,7 @@ private let MASErrorSource: String = "MASErrorSource"
public enum MASErrorCode: Int { public enum MASErrorCode: Int {
case NoError case NoError
case NotSignedIn case NotSignedIn
case PurchaseError
var exitCode: Int32 { var exitCode: Int32 {
return Int32(self.rawValue) return Int32(self.rawValue)

View file

@ -8,8 +8,6 @@
import Foundation import Foundation
var downloadQueue = CKDownloadQueue.sharedDownloadQueue()
downloadQueue.addObserver(DownloadQueueObserver())
let registry = CommandRegistry<MASError>() let registry = CommandRegistry<MASError>()
let helpCommand = HelpCommand(registry: registry) let helpCommand = HelpCommand(registry: registry)