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 blankLinesAroundMark
--disable consecutiveSpaces --disable consecutiveSpaces
--disable hoistPatternLet --disable hoistPatternLet
--disable hoistTry
--disable indent --disable indent
--disable trailingCommas --disable trailingCommas

View file

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

View file

@ -24,11 +24,11 @@ public struct AccountCommand: CommandProtocol {
return .failure(.notSupported) return .failure(.notSupported)
} }
if let account = ISStoreAccount.primaryAccount { do {
print(account.identifier) print(try ISStoreAccount.primaryAccount.wait().identifier)
} else { return .success(())
return .failure(.notSignedIn) } 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, password: options.password,
systemDialog: options.dialog systemDialog: options.dialog
) )
.wait()
return .success(()) return .success(())
} catch { } catch {
return .failure(error as? MASError ?? .signInFailed(error: error as NSError)) return .failure(error as? MASError ?? .signInFailed(error: error as NSError))

View file

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