Change Credentials to be a struct instead of an enum
This commit is contained in:
parent
b0b3f58036
commit
8e939e9942
@ -274,68 +274,31 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
// MARK: - API
|
||||
|
||||
public func storeCredentials(_ credentials: Credentials) throws {
|
||||
username = credentials.username
|
||||
guard let server = delegate.server else {
|
||||
throw CredentialsError.incompleteCredentials
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
switch credentials {
|
||||
case .basic(let username, _):
|
||||
self.username = username
|
||||
case .readerAPIBasicLogin(let username, _):
|
||||
self.username = username
|
||||
case .readerAPIAuthLogin(let username, _):
|
||||
self.username = username
|
||||
case .oauthAccessToken(let username, _):
|
||||
self.username = username
|
||||
case .oauthRefreshToken(let username, _):
|
||||
self.username = username
|
||||
}
|
||||
|
||||
try CredentialsManager.storeCredentials(credentials, server: server)
|
||||
|
||||
delegate.credentials = credentials
|
||||
}
|
||||
|
||||
public func retrieveCredentials() throws -> Credentials? {
|
||||
switch type {
|
||||
case .feedbin:
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return nil
|
||||
}
|
||||
return try CredentialsManager.retrieveBasicCredentials(server: server, username: username)
|
||||
case .freshRSS:
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return nil
|
||||
}
|
||||
return try CredentialsManager.retrieveReaderAPIAuthCredentials(server: server, username: username)
|
||||
default:
|
||||
public func retrieveCredentials(type: CredentialsType) throws -> Credentials? {
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return nil
|
||||
}
|
||||
return try CredentialsManager.retrieveCredentials(type: type, server: server, username: username)
|
||||
}
|
||||
|
||||
public func removeCredentials() throws {
|
||||
switch type {
|
||||
case .feedbin:
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return
|
||||
}
|
||||
try CredentialsManager.removeBasicCredentials(server: server, username: username)
|
||||
self.username = nil
|
||||
case .freshRSS:
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return
|
||||
}
|
||||
try CredentialsManager.removeReaderAPIAuthCredentials(server: server, username: username)
|
||||
self.username = nil
|
||||
default:
|
||||
break
|
||||
public func removeCredentials(type: CredentialsType) throws {
|
||||
guard let username = self.username, let server = delegate.server else {
|
||||
return
|
||||
}
|
||||
try CredentialsManager.removeCredentials(type: type, server: server, username: username)
|
||||
}
|
||||
|
||||
public static func validateCredentials(transport: Transport = URLSession.webserviceTransport(), type: AccountType, credentials: Credentials, endpoint: URL? = nil, completion: @escaping (Result<Credentials?, Error>) -> Void) {
|
||||
switch type {
|
||||
case .onMyMac:
|
||||
LocalAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion)
|
||||
case .feedbin:
|
||||
FeedbinAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion)
|
||||
case .freshRSS:
|
||||
|
@ -13,11 +13,22 @@ public enum CredentialsError: Error {
|
||||
case unhandledError(status: OSStatus)
|
||||
}
|
||||
|
||||
public enum Credentials {
|
||||
case basic(username: String, password: String)
|
||||
case readerAPIBasicLogin(username: String, password: String)
|
||||
case readerAPIAuthLogin(username: String, apiKey: String)
|
||||
case oauthAccessToken(username: String, token: String)
|
||||
case oauthRefreshToken(username: String, token: String)
|
||||
public enum CredentialsType: String {
|
||||
case basic = "password"
|
||||
case readerBasic = "readerBasic"
|
||||
case readerAPIKey = "readerAPIKey"
|
||||
case oauthAccessToken = "oauthAccessToken"
|
||||
case oauthRefreshToken = "oauthRefreshToken"
|
||||
}
|
||||
|
||||
public struct Credentials {
|
||||
public let type: CredentialsType
|
||||
public let username: String
|
||||
public let secret: String
|
||||
|
||||
public init(type: CredentialsType, username: String, secret: String) {
|
||||
self.type = type
|
||||
self.username = username
|
||||
self.secret = secret
|
||||
}
|
||||
}
|
||||
|
@ -12,127 +12,17 @@ public struct CredentialsManager {
|
||||
|
||||
public static func storeCredentials(_ credentials: Credentials, server: String) throws {
|
||||
|
||||
switch credentials {
|
||||
case .basic(let username, let password):
|
||||
try storeBasicCredentials(server: server, username: username, password: password)
|
||||
case .readerAPIBasicLogin(let username, let password):
|
||||
try storeBasicCredentials(server: server, username: username, password: password)
|
||||
case .readerAPIAuthLogin(let username, let apiKey):
|
||||
try storeBasicCredentials(server: server, username: username, password: apiKey)
|
||||
case .oauthAccessToken(let username, let token):
|
||||
try storeBasicCredentials(server: server, username: username, password: token)
|
||||
case .oauthRefreshToken(let username, let token):
|
||||
try storeBasicCredentials(server: server, username: username, password: token)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static func retrieveBasicCredentials(server: String, username: String) throws -> Credentials? {
|
||||
|
||||
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
var item: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||
|
||||
guard status != errSecItemNotFound else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard status == errSecSuccess else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
guard let existingItem = item as? [String : Any],
|
||||
let passwordData = existingItem[kSecValueData as String] as? Data,
|
||||
let password = String(data: passwordData, encoding: String.Encoding.utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Credentials.basic(username: username, password: password)
|
||||
|
||||
}
|
||||
|
||||
public static func removeBasicCredentials(server: String, username: String) throws {
|
||||
|
||||
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static func retrieveReaderAPIAuthCredentials(server: String, username: String) throws -> Credentials? {
|
||||
|
||||
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
var item: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||
|
||||
guard status != errSecItemNotFound else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard status == errSecSuccess else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
guard let existingItem = item as? [String : Any],
|
||||
let passwordData = existingItem[kSecValueData as String] as? Data,
|
||||
let password = String(data: passwordData, encoding: String.Encoding.utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Credentials.readerAPIAuthLogin(username: username, apiKey: password)
|
||||
|
||||
}
|
||||
|
||||
public static func removeReaderAPIAuthCredentials(server: String, username: String) throws {
|
||||
|
||||
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
extension CredentialsManager {
|
||||
|
||||
static func storeBasicCredentials(server: String, username: String, password: String) throws {
|
||||
|
||||
let passwordData = password.data(using: String.Encoding.utf8)!
|
||||
|
||||
let updateQuery: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: credentials.username,
|
||||
kSecAttrServer as String: server]
|
||||
let attributes: [String: Any] = [kSecValueData as String: passwordData]
|
||||
let status = SecItemUpdate(updateQuery as CFDictionary, attributes as CFDictionary)
|
||||
|
||||
if credentials.type != .basic {
|
||||
query[kSecAttrSecurityDomain as String] = credentials.type.rawValue
|
||||
}
|
||||
|
||||
let secretData = credentials.secret.data(using: String.Encoding.utf8)!
|
||||
let attributes: [String: Any] = [kSecValueData as String: secretData]
|
||||
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
||||
|
||||
switch status {
|
||||
case errSecSuccess:
|
||||
@ -147,15 +37,67 @@ extension CredentialsManager {
|
||||
return
|
||||
}
|
||||
|
||||
let addQuery: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecValueData as String: passwordData]
|
||||
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
||||
query[kSecValueData as String] = secretData
|
||||
|
||||
let addStatus = SecItemAdd(query as CFDictionary, nil)
|
||||
if addStatus != errSecSuccess {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static func retrieveCredentials(type: CredentialsType, server: String, username: String) throws -> Credentials? {
|
||||
|
||||
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
if type != .basic {
|
||||
query[kSecAttrSecurityDomain as String] = type.rawValue
|
||||
}
|
||||
|
||||
var item: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||
|
||||
guard status != errSecItemNotFound else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard status == errSecSuccess else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
guard let existingItem = item as? [String : Any],
|
||||
let secretData = existingItem[kSecValueData as String] as? Data,
|
||||
let secret = String(data: secretData, encoding: String.Encoding.utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Credentials(type: type, username: username, secret: secret)
|
||||
|
||||
}
|
||||
|
||||
public static func removeCredentials(type: CredentialsType, server: String, username: String) throws {
|
||||
|
||||
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||
kSecAttrAccount as String: username,
|
||||
kSecAttrServer as String: server,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnData as String: true]
|
||||
|
||||
if type != .basic {
|
||||
query[kSecAttrSecurityDomain as String] = type.rawValue
|
||||
}
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||||
throw CredentialsError.unhandledError(status: status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -19,29 +19,28 @@ public extension URLRequest {
|
||||
return
|
||||
}
|
||||
|
||||
switch credentials {
|
||||
case .basic(let username, let password):
|
||||
let data = "\(username):\(password)".data(using: .utf8)
|
||||
switch credentials.type {
|
||||
case .basic:
|
||||
let data = "\(credentials.username):\(credentials.secret)".data(using: .utf8)
|
||||
let base64 = data?.base64EncodedString()
|
||||
let auth = "Basic \(base64 ?? "")"
|
||||
setValue(auth, forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||
case .readerAPIBasicLogin(let username, let password):
|
||||
case .readerBasic:
|
||||
setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
httpMethod = "POST"
|
||||
let postData = "Email=\(username)&Passwd=\(password)"
|
||||
let postData = "Email=\(credentials.username)&Passwd=\(credentials.secret)"
|
||||
httpBody = postData.data(using: String.Encoding.utf8)
|
||||
case .readerAPIAuthLogin(_, let apiKey):
|
||||
let auth = "GoogleLogin auth=\(apiKey)"
|
||||
case .readerAPIKey:
|
||||
let auth = "GoogleLogin auth=\(credentials.secret)"
|
||||
setValue(auth, forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||
case .oauthAccessToken(_, let token):
|
||||
let auth = "OAuth \(token)"
|
||||
case .oauthAccessToken:
|
||||
let auth = "OAuth \(credentials.secret)"
|
||||
setValue(auth, forHTTPHeaderField: "Authorization")
|
||||
case .oauthRefreshToken:
|
||||
// While both access and refresh tokens are credentials, it seems the `Credentials` cases
|
||||
// enumerates how the identity of the user can be proved rather than
|
||||
// credentials-in-general, such as in this refresh token case,
|
||||
// the authority to prove an identity.
|
||||
// TODO: Refactor as usage becomes clearer.
|
||||
assertionFailure("Refresh tokens are used to replace expired access tokens. Did you mean to use `accessToken` instead?")
|
||||
break
|
||||
}
|
||||
|
@ -525,7 +525,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
credentials = try? account.retrieveCredentials()
|
||||
credentials = try? account.retrieveCredentials(type: .basic)
|
||||
accountMetadata = account.metadata
|
||||
}
|
||||
|
||||
@ -1229,7 +1229,7 @@ private extension FeedbinAccountDelegate {
|
||||
|
||||
func retrieveCredentialsIfNecessary(_ account: Account) {
|
||||
if credentials == nil {
|
||||
credentials = try? account.retrieveCredentials()
|
||||
credentials = try? account.retrieveCredentials(type: .basic)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,7 +414,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
accountMetadata = account.metadata
|
||||
credentials = try? account.retrieveCredentials()
|
||||
credentials = try? account.retrieveCredentials(type: .readerAPIKey)
|
||||
}
|
||||
|
||||
static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, completion: @escaping (Result<Credentials?, Error>) -> Void) {
|
||||
|
@ -84,11 +84,6 @@ final class ReaderAPICaller: NSObject {
|
||||
return
|
||||
}
|
||||
|
||||
guard case .readerAPIBasicLogin(let username, _) = credentials else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
let request = URLRequest(url: endpoint.appendingPathComponent(ReaderAPIEndpoints.login.rawValue), credentials: credentials)
|
||||
|
||||
transport.send(request: request) { result in
|
||||
@ -117,7 +112,7 @@ final class ReaderAPICaller: NSObject {
|
||||
}
|
||||
|
||||
// Save Auth Token for later use
|
||||
self.credentials = .readerAPIAuthLogin(username: username, apiKey: authString)
|
||||
self.credentials = Credentials(type: .readerAPIKey, username: credentials.username, secret: authString)
|
||||
|
||||
completion(.success(self.credentials))
|
||||
case .failure(let error):
|
||||
|
@ -27,10 +27,8 @@ class AccountsFeedbinWindowController: NSWindowController {
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
if let account = account, let credentials = try? account.retrieveCredentials() {
|
||||
if case .basic(let username, _) = credentials {
|
||||
usernameTextField.stringValue = username
|
||||
}
|
||||
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
usernameTextField.stringValue = credentials.username
|
||||
actionButton.title = NSLocalizedString("Update", comment: "Update")
|
||||
} else {
|
||||
actionButton.title = NSLocalizedString("Create", comment: "Create")
|
||||
@ -63,7 +61,7 @@ class AccountsFeedbinWindowController: NSWindowController {
|
||||
progressIndicator.isHidden = false
|
||||
progressIndicator.startAnimation(self)
|
||||
|
||||
let credentials = Credentials.basic(username: usernameTextField.stringValue, password: passwordTextField.stringValue)
|
||||
let credentials = Credentials(type: .basic, username: usernameTextField.stringValue, secret: passwordTextField.stringValue)
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { [weak self] result in
|
||||
|
||||
guard let self = self else { return }
|
||||
@ -79,6 +77,7 @@ class AccountsFeedbinWindowController: NSWindowController {
|
||||
self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error")
|
||||
return
|
||||
}
|
||||
|
||||
var newAccount = false
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .feedbin)
|
||||
@ -86,7 +85,7 @@ class AccountsFeedbinWindowController: NSWindowController {
|
||||
}
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials()
|
||||
try self.account?.removeCredentials(type: .basic)
|
||||
try self.account?.storeCredentials(validatedCredentials)
|
||||
if newAccount {
|
||||
self.account?.refreshAll() { result in
|
||||
|
@ -42,10 +42,9 @@ class AccountsReaderAPIWindowController: NSWindowController {
|
||||
}
|
||||
}
|
||||
|
||||
if let account = account, let credentials = try? account.retrieveCredentials() {
|
||||
if case .basic(let username, _) = credentials {
|
||||
usernameTextField.stringValue = username
|
||||
}
|
||||
if let account = account, let credentials = try? account.retrieveCredentials(type: .readerBasic) {
|
||||
usernameTextField.stringValue = credentials.username
|
||||
apiURLTextField.stringValue = account.endpointURL?.absoluteString ?? ""
|
||||
actionButton.title = NSLocalizedString("Update", comment: "Update")
|
||||
} else {
|
||||
actionButton.title = NSLocalizedString("Create", comment: "Create")
|
||||
@ -83,7 +82,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
|
||||
return
|
||||
}
|
||||
|
||||
let credentials = Credentials.readerAPIBasicLogin(username: usernameTextField.stringValue, password: passwordTextField.stringValue)
|
||||
let credentials = Credentials(type: .readerBasic, username: usernameTextField.stringValue, secret: passwordTextField.stringValue)
|
||||
Account.validateCredentials(type: accountType!, credentials: credentials, endpoint: apiURL) { [weak self] result in
|
||||
|
||||
guard let self = self else { return }
|
||||
@ -109,7 +108,9 @@ class AccountsReaderAPIWindowController: NSWindowController {
|
||||
do {
|
||||
self.account?.endpointURL = apiURL
|
||||
|
||||
try self.account?.removeCredentials()
|
||||
try self.account?.removeCredentials(type: .readerBasic)
|
||||
try self.account?.removeCredentials(type: .readerAPIKey)
|
||||
try self.account?.storeCredentials(credentials)
|
||||
try self.account?.storeCredentials(validatedCredentials)
|
||||
|
||||
if newAccount {
|
||||
|
@ -62,7 +62,7 @@ struct SettingsFeedbinAccountView : View {
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials.basic(username: emailAddress, password: viewModel.password)
|
||||
let credentials = Credentials(type: .basic, username: emailAddress, secret: viewModel.password)
|
||||
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
|
||||
@ -85,7 +85,7 @@ struct SettingsFeedbinAccountView : View {
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials()
|
||||
try workAccount.removeCredentials(type: .basic)
|
||||
} catch {}
|
||||
try workAccount.storeCredentials(credentials)
|
||||
|
||||
@ -125,9 +125,8 @@ struct SettingsFeedbinAccountView : View {
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
if case .basic(let username, let password) = try? account.retrieveCredentials() {
|
||||
self.email = username
|
||||
self.password = password
|
||||
if let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
self.email = credentials.username
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ struct SettingsReaderAPIAccountView : View {
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials.readerAPIBasicLogin(username: emailAddress, password: viewModel.password)
|
||||
let credentials = Credentials(type: .readerBasic, username: emailAddress, secret: viewModel.password)
|
||||
guard let apiURL = URL(string: viewModel.apiURL) else {
|
||||
self.error = "Invalid API URL."
|
||||
return
|
||||
@ -94,11 +94,13 @@ struct SettingsReaderAPIAccountView : View {
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials()
|
||||
try workAccount.removeCredentials(type: .readerBasic)
|
||||
try workAccount.removeCredentials(type: .readerAPIKey)
|
||||
} catch {}
|
||||
|
||||
workAccount.endpointURL = apiURL
|
||||
|
||||
try workAccount.storeCredentials(credentials)
|
||||
try workAccount.storeCredentials(validatedCredentials)
|
||||
|
||||
if newAccount {
|
||||
@ -136,9 +138,8 @@ struct SettingsReaderAPIAccountView : View {
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
self.accountType = account.type
|
||||
if case .readerAPIBasicLogin(let username, let password) = try? account.retrieveCredentials() {
|
||||
self.email = username
|
||||
self.password = password
|
||||
if let credentials = try? account.retrieveCredentials(type: .readerBasic) {
|
||||
self.email = credentials.username
|
||||
self.apiURL = account.endpointURL?.absoluteString ?? ""
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user