Merge pull request #129 from RobotsAndPencils/andrew/signInErrorHandling

Show sign in errors inline on sign in view
This commit is contained in:
Matt Kiazyk 2021-05-02 08:34:42 -05:00 committed by GitHub
commit ffc7223a4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 8 deletions

View file

@ -36,6 +36,10 @@ public class Client {
case 401: case 401:
return Fail(error: AuthenticationError.invalidUsernameOrPassword(username: accountName)) return Fail(error: AuthenticationError.invalidUsernameOrPassword(username: accountName))
.eraseToAnyPublisher() .eraseToAnyPublisher()
case 403:
let errorMessage = responseBody.serviceErrors?.first?.description.replacingOccurrences(of: "-20209: ", with: "") ?? ""
return Fail(error: AuthenticationError.accountLocked(errorMessage))
.eraseToAnyPublisher()
case 409: case 409:
return self.handleTwoStepOrFactor(data: data, response: response, serviceKey: serviceKey) return self.handleTwoStepOrFactor(data: data, response: response, serviceKey: serviceKey)
case 412 where Client.authTypes.contains(responseBody.authType ?? ""): case 412 where Client.authTypes.contains(responseBody.authType ?? ""):
@ -180,6 +184,7 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
case appleIDAndPrivacyAcknowledgementRequired case appleIDAndPrivacyAcknowledgementRequired
case accountUsesTwoStepAuthentication case accountUsesTwoStepAuthentication
case accountUsesUnknownAuthenticationKind(String?) case accountUsesUnknownAuthenticationKind(String?)
case accountLocked(String)
case badStatusCode(statusCode: Int, data: Data, response: HTTPURLResponse) case badStatusCode(statusCode: Int, data: Data, response: HTTPURLResponse)
public var errorDescription: String? { public var errorDescription: String? {
@ -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." 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: 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." 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, _, _): 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." return "Received an unexpected status code: \(statusCode). If you continue to have problems, please submit a bug report in the Help menu."
} }

View file

@ -127,6 +127,7 @@ class AppState: ObservableObject {
} }
func signIn(username: String, password: String) { func signIn(username: String, password: String) {
authError = nil
signIn(username: username, password: password) signIn(username: username, password: password)
.sink( .sink(
receiveCompletion: { _ in }, receiveCompletion: { _ in },

View file

@ -133,6 +133,7 @@ struct MainWindow: View {
.padding() .padding()
} else { } else {
SignInCredentialsView() SignInCredentialsView()
.frame(width: 400)
} }
} }
} }

View file

@ -14,30 +14,42 @@ struct SignInCredentialsView: View {
Text("Apple ID:") Text("Apple ID:")
.frame(minWidth: 100, alignment: .trailing) .frame(minWidth: 100, alignment: .trailing)
TextField("example@icloud.com", text: $username) TextField("example@icloud.com", text: $username)
.frame(width: 250)
} }
HStack { HStack {
Text("Password:") Text("Password:")
.frame(minWidth: 100, alignment: .trailing) .frame(minWidth: 100, alignment: .trailing)
SecureField("Required", text: $password) 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 { HStack {
Spacer() Spacer()
Button("Cancel") { appState.presentedSheet = nil } Button("Cancel") {
.keyboardShortcut(.cancelAction) appState.authError = nil
ProgressButton(isInProgress: appState.isProcessingAuthRequest, appState.presentedSheet = nil
action: { appState.signIn(username: username, password: password) }) {
Text("Next")
} }
.keyboardShortcut(.cancelAction)
ProgressButton(
isInProgress: appState.isProcessingAuthRequest,
action: { appState.signIn(username: username, password: password) },
label: {
Text("Next")
}
)
.disabled(username.isEmpty || password.isEmpty) .disabled(username.isEmpty || password.isEmpty)
.keyboardShortcut(.defaultAction) .keyboardShortcut(.defaultAction)
} }
.frame(height: 25) .frame(height: 25)
} }
.padding() .padding()
.emittingError($appState.authError, recoveryHandler: { _ in })
} }
} }
@ -45,5 +57,6 @@ struct SignInCredentialsView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
SignInCredentialsView() SignInCredentialsView()
.environmentObject(AppState()) .environmentObject(AppState())
.previewLayout(.sizeThatFits)
} }
} }