Migrate ISStoreAccount from Grand Central Dispatch to PromiseKit.

Partial #562

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-10 23:15:44 -04:00
parent aceb4c3ddb
commit c01f7c541e
No known key found for this signature in database
6 changed files with 72 additions and 66 deletions

View file

@ -11,6 +11,7 @@
--disable blankLinesAroundMark
--disable consecutiveSpaces
--disable hoistPatternLet
--disable hoistTry
--disable indent
--disable trailingCommas

View file

@ -7,85 +7,81 @@
//
import CommerceKit
import PromiseKit
import StoreFoundation
extension ISStoreAccount: StoreAccount {
static var primaryAccount: ISStoreAccount? {
static var primaryAccount: Promise<ISStoreAccount> {
if #available(macOS 10.13, *) {
let group = DispatchGroup()
group.enter()
var account: ISStoreAccount?
ISServiceProxy.genericShared().accountService.primaryAccount { storeAccount in
account = storeAccount
group.leave()
}
_ = group.wait(timeout: .now() + 30)
return account
return race(
Promise<ISStoreAccount> { seal in
ISServiceProxy.genericShared().accountService.primaryAccount { storeAccount in
seal.fulfill(storeAccount)
}
},
after(seconds: 30).then {
Promise(error: MASError.notSignedIn)
}
)
} else {
return CKAccountStore.shared().primaryAccount
return .value(CKAccountStore.shared().primaryAccount)
}
}
static func signIn(username: String, password: String, systemDialog: Bool = false) throws -> ISStoreAccount {
static func signIn(username: String, password: String, systemDialog: Bool) -> Promise<ISStoreAccount> {
if #available(macOS 10.13, *) {
// Signing in is no longer possible as of High Sierra.
// https://github.com/mas-cli/mas/issues/164
throw MASError.notSupported
return Promise(error: MASError.notSupported)
} else {
if let account = primaryAccount, account.isSignedIn {
throw MASError.alreadySignedIn(asAccountId: account.identifier)
}
return
primaryAccount
.then { account -> Promise<ISStoreAccount> in
if account.isSignedIn {
return Promise(error: MASError.alreadySignedIn(asAccountId: account.identifier))
}
let password =
password.isEmpty && !systemDialog
? String(validatingUTF8: getpass("Password: "))!
: password
let password =
password.isEmpty && !systemDialog
? String(validatingUTF8: getpass("Password: "))!
: password
guard !password.isEmpty || systemDialog else {
throw MASError.noPasswordProvided
}
guard !password.isEmpty || systemDialog else {
return Promise(error: MASError.noPasswordProvided)
}
let accountService = ISServiceProxy.genericShared().accountService
accountService.setStoreClient(ISStoreClient(storeClientType: 0))
let context = ISAuthenticationContext(accountID: 0)
context.appleIDOverride = username
let context = ISAuthenticationContext(accountID: 0)
context.appleIDOverride = username
if !systemDialog {
context.demoMode = true
context.demoAccountName = username
context.demoAccountPassword = password
context.demoAutologinMode = true
}
let signInPromise =
Promise<ISStoreAccount> { seal in
let accountService = ISServiceProxy.genericShared().accountService
accountService.setStoreClient(ISStoreClient(storeClientType: 0))
accountService.signIn(with: context) { success, storeAccount, error in
if success, let storeAccount {
seal.fulfill(storeAccount)
} else {
seal.reject(MASError.signInFailed(error: error as NSError?))
}
}
}
let group = DispatchGroup()
group.enter()
if systemDialog {
return signInPromise
} else {
context.demoMode = true
context.demoAccountName = username
context.demoAccountPassword = password
context.demoAutologinMode = true
var storeAccount: ISStoreAccount?
var maserror: MASError?
// Only works on macOS Sierra and below
accountService.signIn(with: context) { success, account, error in
if success, let account {
storeAccount = account
} else {
maserror = .signInFailed(error: error as NSError?)
return race(
signInPromise,
after(seconds: 30).then {
Promise(error: MASError.signInFailed(error: nil))
}
)
}
}
group.leave()
}
if systemDialog {
group.wait()
} else {
_ = group.wait(timeout: .now() + 30)
}
if let storeAccount {
return storeAccount
}
throw maserror ?? MASError.signInFailed(error: nil)
}
}
}

View file

@ -45,7 +45,7 @@ extension SSPurchase {
// Monterey obscures the user's App Store account information, but allows
// redownloads without passing the account to SSPurchase.
// https://github.com/mas-cli/mas/issues/417
if let storeAccount = ISStoreAccount.primaryAccount {
if let storeAccount = try? ISStoreAccount.primaryAccount.wait() {
accountIdentifier = storeAccount.dsID
appleID = storeAccount.identifier
}

View file

@ -24,11 +24,11 @@ public struct AccountCommand: CommandProtocol {
return .failure(.notSupported)
}
if let account = ISStoreAccount.primaryAccount {
print(account.identifier)
} else {
return .failure(.notSignedIn)
do {
print(try ISStoreAccount.primaryAccount.wait().identifier)
return .success(())
} catch {
return .failure(error as? MASError ?? .failed(error: error as NSError))
}
return .success(())
}
}

View file

@ -25,6 +25,7 @@ public struct SignInCommand: CommandProtocol {
password: options.password,
systemDialog: options.dialog
)
.wait()
return .success(())
} catch {
return .failure(error as? MASError ?? .signInFailed(error: error as NSError))

View file

@ -11,6 +11,8 @@ import Foundation
public enum MASError: Error, Equatable {
case notSupported
case failed(error: NSError?)
case notSignedIn
case noPasswordProvided
case signInFailed(error: NSError?)
@ -46,6 +48,12 @@ extension MASError: CustomStringConvertible {
For more information see: \
https://github.com/mas-cli/mas#%EF%B8%8F-known-issues
"""
case .failed(let error):
if let error {
return "Failed: \(error.localizedDescription)"
} else {
return "Failed"
}
case .signInFailed(let error):
if let error {
return "Sign in failed: \(error.localizedDescription)"