Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Xcodes/AppleAPI/Sources/AppleAPI/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public class Client {
case 401:
return Fail(error: AuthenticationError.invalidUsernameOrPassword(username: accountName))
.eraseToAnyPublisher()
case 403:
let errorMessage = responseBody.serviceErrors?.first?.description.replacingOccurrences(of: "-20209: ", with: "") ?? ""
return Fail(error: AuthenticationError.accountLocked(errorMessage))
.eraseToAnyPublisher()
case 409:
return self.handleTwoStepOrFactor(data: data, response: response, serviceKey: serviceKey)
case 412 where Client.authTypes.contains(responseBody.authType ?? ""):
Expand Down Expand Up @@ -180,6 +184,7 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
case appleIDAndPrivacyAcknowledgementRequired
case accountUsesTwoStepAuthentication
case accountUsesUnknownAuthenticationKind(String?)
case accountLocked(String)
case badStatusCode(statusCode: Int, data: Data, response: HTTPURLResponse)

public var errorDescription: String? {
Expand All @@ -203,6 +208,8 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
return "Received a response from Apple that indicates this account has two-step authentication enabled. xcodes currently only supports the newer two-factor authentication, though. Please consider upgrading to two-factor authentication, or explain why this isn't an option for you by making a new feature request in the Help menu."
case .accountUsesUnknownAuthenticationKind:
return "Received a response from Apple that indicates this account has two-step or two-factor authentication enabled, but xcodes is unsure how to handle this response. If you continue to have problems, please submit a bug report in the Help menu."
case let .accountLocked(message):
return message
case let .badStatusCode(statusCode, _, _):
return "Received an unexpected status code: \(statusCode). If you continue to have problems, please submit a bug report in the Help menu."
}
Expand Down
1 change: 1 addition & 0 deletions Xcodes/Backend/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class AppState: ObservableObject {
}

func signIn(username: String, password: String) {
authError = nil
signIn(username: username, password: password)
.sink(
receiveCompletion: { _ in },
Expand Down
1 change: 1 addition & 0 deletions Xcodes/Frontend/MainWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct MainWindow: View {
.padding()
} else {
SignInCredentialsView()
.frame(width: 400)
}
}
}
Expand Down
29 changes: 21 additions & 8 deletions Xcodes/Frontend/SignIn/SignInCredentialsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,49 @@ struct SignInCredentialsView: View {
Text("Apple ID:")
.frame(minWidth: 100, alignment: .trailing)
TextField("[email protected]", text: $username)
.frame(width: 250)
}
HStack {
Text("Password:")
.frame(minWidth: 100, alignment: .trailing)
SecureField("Required", text: $password)
.frame(width: 250)
}
if appState.authError != nil {
HStack {
Text("")
.frame(minWidth: 100)
Text(appState.authError?.legibleLocalizedDescription ?? "")
.fixedSize(horizontal: false, vertical: true)
.foregroundColor(.red)
}
}

HStack {
Spacer()
Button("Cancel") { appState.presentedSheet = nil }
.keyboardShortcut(.cancelAction)
ProgressButton(isInProgress: appState.isProcessingAuthRequest,
action: { appState.signIn(username: username, password: password) }) {
Text("Next")
Button("Cancel") {
appState.authError = nil
appState.presentedSheet = nil
}
.keyboardShortcut(.cancelAction)
ProgressButton(
isInProgress: appState.isProcessingAuthRequest,
action: { appState.signIn(username: username, password: password) },
label: {
Text("Next")
}
)
.disabled(username.isEmpty || password.isEmpty)
.keyboardShortcut(.defaultAction)
}
.frame(height: 25)
}
.padding()
.emittingError($appState.authError, recoveryHandler: { _ in })
}
}

struct SignInCredentialsView_Previews: PreviewProvider {
static var previews: some View {
SignInCredentialsView()
.environmentObject(AppState())
.previewLayout(.sizeThatFits)
}
}