diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..868f075 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,40 @@ +# https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md + +# format options + +--symlinks ignore +--swiftversion 6.2 + +# format options + +--closingparen same-line +--commas inline +--comments indent +--decimalgrouping 3,4 +--exponentcase lowercase +--exponentgrouping disabled +--fractiongrouping disabled +--ifdef no-indent +--importgrouping testable-top +--nospaceoperators ..<, ... +--selfrequired validate +--stripunusedargs closure-only +--wraparguments after-first +--wrapcollections after-first +--wrapparameters after-first +--disable wrapArguments +--indent 4 +--maxwidth 115 +--wrapcollections before-first +--ranges no-space + +# rules + +--disable wrapMultilineStatementBraces + + +--enable isEmpty,\ +blankLineAfterImports,\ +sortSwitchCases,\ +redundantAsync,\ + diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..c68b1e0 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,207 @@ +# You can find the documentation here : +# https://realm.github.io/SwiftLint/rule-directory.html + +############## +#Disabled rules# +############## + +disabled_rules: + - nesting + +############## +#Opted in rules# +############## + +opt_in_rules: + - array_init + - closure_spacing + - collection_alignment + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - contains_over_range_nil_comparison + - convenience_type + - discouraged_object_literal + - discouraged_optional_boolean + - empty_collection_literal + - empty_count + - empty_xctest_method + - enum_case_associated_values_count + - expiring_todo + - explicit_init + - extension_access_modifier + - fallthrough + - fatal_error_message + - file_name_no_space + - first_where + - flatmap_over_map_reduce + - force_unwrapping + - ibinspectable_in_extension + - identical_operands + - implicit_return + - joined_default_parameter + - last_where + - legacy_multiple + - legacy_random + - let_var_whitespace + - literal_expression_end_indentation + - multiline_arguments + - multiline_parameters + - nimble_operator + - nslocalizedstring_key + - nslocalizedstring_require_bundle + - number_separator + - operator_usage_whitespace + - optional_enum_case_matching + - overridden_super_call + - override_in_extension + - pattern_matching_keywords + - prefer_self_type_over_type_of_self + - prefer_zero_over_explicit_init + - prefixed_toplevel_constant + - private_action + - private_outlet + - prohibited_super_call + - quick_discouraged_call + - quick_discouraged_focused_test + - quick_discouraged_pending_test + - raw_value_for_camel_cased_codable_enum + - reduce_into + - redundant_nil_coalescing + - redundant_type_annotation + - required_enum_case + - single_test_class + - sorted_first_last + - sorted_imports + - static_operator + - toggle_bool + - trailing_closure + - unavailable_function + - unneeded_parentheses_in_closure_argument + - untyped_error_in_catch + - vertical_parameter_alignment_on_call + - vertical_whitespace_closing_braces + - vertical_whitespace_opening_braces + - xct_specific_matcher + - yoda_condition + - unhandled_throwing_task + +############## +#Specific analyzer rules# +############## + +analyzer_rules: # Rules run by `swiftlint analyze` + - explicit_self + - unused_declaration + - unused_import + +################ +#Customed rules# +################ + +# configurable rules can be customized from this configuration file +# binary rules can set their severity level +#force_cast: warning # implicitly + +#force_try: +# severity: warning # explicitly +# +# rules that have both warning and error levels, can set just the warning level +# implicitly +line_length: + warning: 120 + error: 200 + ignores_urls: true + ignores_comments: true + +# they can set both implicitly with an array +type_body_length: + warning: 300 + error: 400 + +trailing_closure: + only_single_muted_parameter: true + +trailing_whitespace: + ignores_empty_lines: true + +# or they can set both explicitly +file_length: + warning: 700 + error: 1200 + +function_body_length: # Functions bodies should not span too many lines. + warning: 100 + error: 200 + +# naming rules can set warnings/errors for min_length and max_length +# additionally they can set excluded names +type_name: + min_length: 3 # only warning + max_length: # warning and error + warning: 45 + error: 50 + excluded: iPhone # excluded via string + allowed_symbols: ["_"] # these are allowed in type names + +identifier_name: + min_length: # only min_length + warning: 2 + excluded: # excluded via string array +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown) + +custom_rules: + drop_first_one: + name: "Drop first one" + message: "Implicitly drop first one element" + regex: "(\\.dropFirst\\(1\\))" + + discouraged_optional_self: + name: "Discouraged optional self" + message: "Unwrap self via guard let and use implicit self instead of optional self" + regex: "(self\\?\\.)" + + discouraged_previewprovider: + name: "Discouraged PreviewProvider" + message: "Use #Preview macro insteaf of PreviewProvider" + regex: "(PreviewProvider)" + + discouraged_anyview: + name: "Discouraged AnyView" + message: "Avoid using AnyView for better performance" + regex: "(AnyView)" + + discouraged_body_font: + name: "Discouraged body font" + message: "Prefer implicit body font" + regex: "(\\.font\\(\\.body\\))" + + discouraged_regular_font_weight: + name: "Discouraged regular font weight" + message: "Prefer implicit regular font weight" + regex: "(\\.fontWeight\\(\\.regular\\))" + + discouraged_verbose_contentshape_rectangle: + name: "Discouraged verbose .contentShape(Rectangle())" + message: "Prefer static member lookup version .contentShape(.rectangle)" + regex: "(\\.contentShape\\(Rectangle\\(\\))" + + deprecated_foregroundcolor_modifier: + name: "Deprecated foregroundColor modifier" + message: "Prefer foregroundStyle over deprecated foregroundColor" + regex: "(foregroundColor\\()" + + deprecated_navigationbarleading: + name: "Deprecated navigationBarLeading" + message: "Prefer topBarLeading over deprecated navigationBarLeading" + regex: "\\.navigationBarLeading" + + deprecated_navigationbartrailing: + name: "Deprecated navigationBarTrailing" + message: "Prefer topBarTrailing over deprecated navigationBarTrailing" + regex: "\\.navigationBarTrailing" + + nil_if_empty: + name: "Nil if empty" + message: "Use Collection's computed property nilIfEmpty" + regex: "\\.isEmpty \\? nil" diff --git a/SimpleLogin/Keyboard Extension/CreatedAliasView.swift b/SimpleLogin/Keyboard Extension/CreatedAliasView.swift index bc7fdd7..751d5dd 100644 --- a/SimpleLogin/Keyboard Extension/CreatedAliasView.swift +++ b/SimpleLogin/Keyboard Extension/CreatedAliasView.swift @@ -25,7 +25,7 @@ struct CreatedAliasView: View { }, label: { Label("Back", systemImage: "arrowshape.turn.up.backward.fill") }) - .foregroundColor(.slPurple) + .foregroundColor(.slPurple) } .padding(.horizontal, 44) } diff --git a/SimpleLogin/Keyboard Extension/KeyboardContentView.swift b/SimpleLogin/Keyboard Extension/KeyboardContentView.swift index 4777ea0..d757e21 100644 --- a/SimpleLogin/Keyboard Extension/KeyboardContentView.swift +++ b/SimpleLogin/Keyboard Extension/KeyboardContentView.swift @@ -14,7 +14,7 @@ struct KeyboardContentView: View { init(session: Session, onTap: @escaping (Alias) -> Void) { _viewModel = StateObject(wrappedValue: .init(session: session)) - self.onSelectAlias = onTap + onSelectAlias = onTap } var body: some View { @@ -31,7 +31,7 @@ struct KeyboardContentView: View { }, label: { Label("Retry", systemImage: "gobackward") }) - .foregroundColor(.slPurple) + .foregroundColor(.slPurple) } .padding() } else { @@ -69,7 +69,7 @@ final class KeyboardContentViewModel: ObservableObject { } func getMoreAliasesIfNeed(currentAlias alias: Alias?) { - guard let alias = alias else { + guard let alias else { getMoreAliases() return } @@ -81,7 +81,7 @@ final class KeyboardContentViewModel: ObservableObject { } private func getMoreAliases() { - guard !isLoading && canLoadMorePages else { return } + guard !isLoading, canLoadMorePages else { return } Task { @MainActor in defer { isLoading = false } isLoading = true diff --git a/SimpleLogin/Keyboard Extension/KeyboardViewController.swift b/SimpleLogin/Keyboard Extension/KeyboardViewController.swift index 5ec4399..f5d49e1 100644 --- a/SimpleLogin/Keyboard Extension/KeyboardViewController.swift +++ b/SimpleLogin/Keyboard Extension/KeyboardViewController.swift @@ -34,7 +34,7 @@ final class KeyboardViewController: UIInputViewController { private func setSession(session: Session?) { let subView: UIView - if let session = session { + if let session { let contentView = KeyboardContentView(session: session) { [unowned self] alias in textDocumentProxy.insertText(alias.email) } diff --git a/SimpleLogin/ShareExtension/ShareViewController.swift b/SimpleLogin/ShareExtension/ShareViewController.swift index c5f8410..6f76197 100644 --- a/SimpleLogin/ShareExtension/ShareViewController.swift +++ b/SimpleLogin/ShareExtension/ShareViewController.swift @@ -20,7 +20,7 @@ final class ShareViewController: UIViewController { self.setUpUI() } catch { alert(error: error) { [unowned self] in - self.dismiss() + dismiss() } } } @@ -42,17 +42,16 @@ final class ShareViewController: UIViewController { private func setSession(session: Session?) { let subView: UIView - if let session = session { - let createAliasView = CreateAliasView( - session: session, - mode: createAliasViewMode, - onCreateAlias: { [unowned self] alias in - self.handleAliasCreation(alias: alias) - }, - onCancel: { [unowned self] in - self.dismiss() - }, - onOpenMyAccount: nil) + if let session { + let createAliasView = CreateAliasView(session: session, + mode: createAliasViewMode, + onCreateAlias: { [unowned self] alias in + handleAliasCreation(alias: alias) + }, + onCancel: { [unowned self] in + dismiss() + }, + onOpenMyAccount: nil) let hostingController = UIHostingController(rootView: createAliasView) subView = hostingController.view addChild(hostingController) @@ -114,12 +113,12 @@ final class ShareViewController: UIViewController { preferredStyle: .alert) let copyAndCloseAction = UIAlertAction(title: "Copy & close", style: .default) { [unowned self] _ in UIPasteboard.general.string = alias.email - self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) + extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } alert.addAction(copyAndCloseAction) let closeAction = UIAlertAction(title: "Close", style: .cancel) { [unowned self] _ in - self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) + extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } alert.addAction(closeAction) alert.view.tintColor = .slPurple @@ -127,6 +126,6 @@ final class ShareViewController: UIViewController { } private func dismiss() { - self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) + extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } } diff --git a/SimpleLogin/SimpleLogin/AppDelegate.swift b/SimpleLogin/SimpleLogin/AppDelegate.swift index 9190ed1..361ec2b 100644 --- a/SimpleLogin/SimpleLogin/AppDelegate.swift +++ b/SimpleLogin/SimpleLogin/AppDelegate.swift @@ -9,11 +9,11 @@ import SwiftyStoreKit import UIKit final class AppDelegate: NSObject, UIApplicationDelegate { - func application(_ application: UIApplication, + func application(_: UIApplication, // swiftlint:disable:next line_length - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { UIView.appearance(whenContainedInInstancesOf: - [UIAlertController.self]).tintColor = .slPurple + [UIAlertController.self]).tintColor = .slPurple SwiftyStoreKit.completeTransactions(atomically: true) { purchases in for purchase in purchases { switch purchase.transaction.transactionState { diff --git a/SimpleLogin/SimpleLogin/Core Data/DataController.swift b/SimpleLogin/SimpleLogin/Core Data/DataController.swift index c355fc6..67e12cc 100644 --- a/SimpleLogin/SimpleLogin/Core Data/DataController.swift +++ b/SimpleLogin/SimpleLogin/Core Data/DataController.swift @@ -51,7 +51,7 @@ struct DataController { func fetchAliases(page: Int, option: AliasFilterOption?) throws -> [Alias] { let fetchRequest = LocalAlias.fetchRequest() - if let option = option { + if let option { switch option { case .disabled: fetchRequest.predicate = .init(format: "enabled == %@", NSNumber(value: false)) @@ -70,12 +70,11 @@ struct DataController { func fetchAliases(page: Int, searchTerm: String) throws -> [Alias] { let fetchRequest = LocalAlias.fetchRequest() fetchRequest.sortDescriptors = [.init(key: "creationTimestamp", ascending: false)] - fetchRequest.predicate = NSCompoundPredicate( - orPredicateWithSubpredicates: [ - .init(format: "email CONTAINS[c] %@", searchTerm), - .init(format: "note CONTAINS[c] %@", searchTerm), - .init(format: "name CONTAINS[c] %@", searchTerm) - ]) + fetchRequest.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [ + .init(format: "email CONTAINS[c] %@", searchTerm), + .init(format: "note CONTAINS[c] %@", searchTerm), + .init(format: "name CONTAINS[c] %@", searchTerm) + ]) fetchRequest.fetchLimit = kDefaultPageSize fetchRequest.fetchOffset = kDefaultPageSize * page return try context.fetch(fetchRequest).compactMap { Alias(from: $0) } diff --git a/SimpleLogin/SimpleLogin/Core Data/LocalAliasExtensions.swift b/SimpleLogin/SimpleLogin/Core Data/LocalAliasExtensions.swift index cd02d92..ddd972b 100644 --- a/SimpleLogin/SimpleLogin/Core Data/LocalAliasExtensions.swift +++ b/SimpleLogin/SimpleLogin/Core Data/LocalAliasExtensions.swift @@ -40,22 +40,22 @@ extension LocalAlias { private func update(from alias: Alias, with managedContext: NSManagedObjectContext) throws { - self.blockCount = Int64(alias.blockCount) - self.creationTimestamp = alias.creationTimestamp - self.email = alias.email - self.enabled = alias.enabled - self.forwardCount = Int64(alias.forwardCount) - self.name = alias.name - self.note = alias.note - self.pgpDisabled = alias.pgpDisabled - self.pgpSupported = alias.pgpSupported - self.pinned = alias.pinned - self.replyCount = Int64(alias.replyCount) - self.mailboxes = try alias.mailboxes.toLocalMailboxLites(with: managedContext) + blockCount = Int64(alias.blockCount) + creationTimestamp = alias.creationTimestamp + email = alias.email + enabled = alias.enabled + forwardCount = Int64(alias.forwardCount) + name = alias.name + note = alias.note + pgpDisabled = alias.pgpDisabled + pgpSupported = alias.pgpSupported + pinned = alias.pinned + replyCount = Int64(alias.replyCount) + mailboxes = try alias.mailboxes.toLocalMailboxLites(with: managedContext) } } -private extension Array where Element == MailboxLite { +private extension [MailboxLite] { func toLocalMailboxLites(with managedContext: NSManagedObjectContext) throws -> NSSet { let localMailboxLites = NSMutableSet() for mailbox in self { diff --git a/SimpleLogin/SimpleLogin/Models/Action.swift b/SimpleLogin/SimpleLogin/Models/Action.swift index 3fdbc45..fe47ea2 100644 --- a/SimpleLogin/SimpleLogin/Models/Action.swift +++ b/SimpleLogin/SimpleLogin/Models/Action.swift @@ -11,26 +11,26 @@ import SwiftUI extension ActivityAction { var iconSystemName: String { switch self { - case .bounced, .block: return "nosign" - case .reply: return "arrowshape.turn.up.left.fill" - case .forward: return "paperplane.fill" + case .block, .bounced: "nosign" + case .reply: "arrowshape.turn.up.left.fill" + case .forward: "paperplane.fill" } } var color: Color { switch self { - case .bounced, .block: return .red - case .reply: return .blue - case .forward: return .green + case .block, .bounced: .red + case .reply: .blue + case .forward: .green } } var title: String { switch self { - case .forward: return "Forward" - case .reply: return "Reply" - case .block: return "Block" - case .bounced: return "Bounced" + case .forward: "Forward" + case .reply: "Reply" + case .block: "Block" + case .bounced: "Bounced" } } } diff --git a/SimpleLogin/SimpleLogin/Models/KeyboardExtensionMode.swift b/SimpleLogin/SimpleLogin/Models/KeyboardExtensionMode.swift index 2eb5d48..1088b47 100644 --- a/SimpleLogin/SimpleLogin/Models/KeyboardExtensionMode.swift +++ b/SimpleLogin/SimpleLogin/Models/KeyboardExtensionMode.swift @@ -13,9 +13,9 @@ enum KeyboardExtensionMode: Int, CaseIterable { var title: String { switch self { case .pinned: - return "Pinned alises" + "Pinned alises" case .all: - return "All aliases" + "All aliases" } } } diff --git a/SimpleLogin/SimpleLogin/Models/SLError.swift b/SimpleLogin/SimpleLogin/Models/SLError.swift index 0d00cba..e7ea7f6 100644 --- a/SimpleLogin/SimpleLogin/Models/SLError.swift +++ b/SimpleLogin/SimpleLogin/Models/SLError.swift @@ -18,17 +18,17 @@ enum SLError: Error { var localizedDescription: String { switch self { case .emptyClipboard: - return "Empty clipboard" - case .invalidApiUrl(let urlString): - return "Invalid API URL: \(urlString)" + "Empty clipboard" + case let .invalidApiUrl(urlString): + "Invalid API URL: \(urlString)" case .invalidValidationCodeSyntax: - return "Invalid validation code syntax" + "Invalid validation code syntax" case .missingApiKey: - return "Missing API Key" + "Missing API Key" case .contactExists: - return "Contact already exists" + "Contact already exists" case .unknown: - return "Unknown error" + "Unknown error" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/ApiKeyView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/ApiKeyView.swift index 1636e87..61aa0f4 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/ApiKeyView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/ApiKeyView.swift @@ -39,7 +39,7 @@ struct ApiKeyView: View { .frame(maxWidth: .infinity) .multilineTextAlignment(.center) }) - .disabled(value.isEmpty) + .disabled(value.isEmpty) } } .navigationBarTitle("Log in using API key", displayMode: .inline) @@ -54,7 +54,7 @@ struct ApiKeyView: View { showingLoadingAlert = isLoading } .onReceive(Just(viewModel.apiKey)) { apiKey in - if let apiKey = apiKey { + if let apiKey { onSetApiKey(apiKey) } } diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/ApiUrlView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/ApiUrlView.swift index 45453e4..5da157e 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/ApiUrlView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/ApiUrlView.swift @@ -18,7 +18,7 @@ struct ApiUrlView: View { init(apiUrl: String) { self.apiUrl = apiUrl - self._currentApiUrl = .init(initialValue: apiUrl) + _currentApiUrl = .init(initialValue: apiUrl) } var body: some View { @@ -64,8 +64,8 @@ struct ApiUrlView: View { private var warningText: some View { Text("⚠️ Do not change API URL unless you are hosting SimpleLogin with your own server") - .font(.footnote) - .foregroundColor(.red) + .font(.footnote) + .foregroundColor(.red) } private var closeOrCancelButton: some View { diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/EmailPasswordView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/EmailPasswordView.swift index 89a3e76..783bcf4 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/EmailPasswordView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/EmailPasswordView.swift @@ -13,8 +13,8 @@ extension EmailPasswordView { var title: String { switch self { - case .logIn: return "Log in" - case .signUp: return "Create account" + case .logIn: "Log in" + case .signUp: "Create account" } } } @@ -128,10 +128,8 @@ struct EmailPasswordView: View { } } .padding(16) - .overlay( - RoundedRectangle(cornerRadius: 8.0) - .stroke(Color.gray.opacity(0.2), lineWidth: 1) - ) + .overlay(RoundedRectangle(cornerRadius: 8.0) + .stroke(Color.gray.opacity(0.2), lineWidth: 1)) PrimaryButton(title: mode.title) { switch mode { @@ -142,7 +140,7 @@ struct EmailPasswordView: View { invalidEmail = !email.isValidEmail invalidPassword = password.count < 8 } - if !invalidEmail && !invalidPassword { + if !invalidEmail, !invalidPassword { await onAction() } } @@ -156,12 +154,12 @@ struct EmailPasswordView: View { } /* -struct EmailPasswordView_Previews: PreviewProvider { - static var previews: some View { - EmailPasswordView(email: .constant(""), - password: .constant(""), - mode: .logIn) {} - .padding() - } -} -*/ + struct EmailPasswordView_Previews: PreviewProvider { + static var previews: some View { + EmailPasswordView(email: .constant(""), + password: .constant(""), + mode: .logIn) {} + .padding() + } + } + */ diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/LogInView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/LogInView.swift index 0c9c8f8..7bfed8c 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/LogInView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/LogInView.swift @@ -86,7 +86,7 @@ struct LogInView: View { viewModel.error = error }) .frame(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? - UIScreen.main.minLength * 3 / 5 : .infinity) + UIScreen.main.minLength * 3 / 5 : .infinity) } .padding() .sheet(isPresented: showingOtpViewSheet) { otpView() } @@ -132,7 +132,7 @@ struct LogInView: View { showingLoadingAlert = isLoading } .onReceive(Just(viewModel.userLogin)) { userLogin in - guard let userLogin = userLogin else { return } + guard let userLogin else { return } if userLogin.isMfaEnabled { otpMode = .logIn(mfaKey: userLogin.mfaKey ?? "") } else if let apiKey = userLogin.apiKey { @@ -273,13 +273,12 @@ struct LogInView: View { } private func otpView() -> some View { - OtpView( - mode: $otpMode, - apiService: viewModel.apiService, - onVerification: { apiKey in - onComplete(apiKey, viewModel.apiService) - }, - onActivation: viewModel.logIn) + OtpView(mode: $otpMode, + apiService: viewModel.apiService, + onVerification: { apiKey in + onComplete(apiKey, viewModel.apiService) + }, + onActivation: viewModel.logIn) } private var resetPasswordConfig: TextFieldAlertConfig { diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/LogInViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Log in/LogInViewModel.swift index 639c7e8..045170b 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/LogInViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/LogInViewModel.swift @@ -74,12 +74,12 @@ final class LogInViewModel: ObservableObject { let logInEndpoint = LogInEndpoint(email: email, password: password, device: UIDevice.current.name) - self.userLogin = try await apiService.execute(logInEndpoint) + userLogin = try await apiService.execute(logInEndpoint) } catch { if let apiServiceError = error as? APIServiceError, - case .clientError(let errorResponse) = apiServiceError, + case let .clientError(errorResponse) = apiServiceError, errorResponse.statusCode == 422 { - self.shouldActivate = true + shouldActivate = true } else { self.error = error } @@ -95,9 +95,9 @@ final class LogInViewModel: ObservableObject { let forgotPasswordEndpoint = ForgotPasswordEndpoint(email: email) let response = try await apiService.execute(forgotPasswordEndpoint) if response.value { - self.resetEmail = email + resetEmail = email } else { - self.error = SLError.unknown + error = SLError.unknown } } catch { self.error = error @@ -118,9 +118,8 @@ extension URLSessionConfiguration { } private extension APIService { - static let `default`: APIService = { + static let `default`: APIService = .init(baseURL: URL(string: "https://app.simplelogin.io/")!, // swiftlint:disable:this force_unwrapping session: .init(configuration: .simpleLogin), printDebugInformation: featureFlags.printNetworkDebugInformation) - }() } diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/LogInWithProtonButtonView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/LogInWithProtonButtonView.swift index ec49558..7b7c2d5 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/LogInWithProtonButtonView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/LogInWithProtonButtonView.swift @@ -41,10 +41,8 @@ struct LogInWithProtonButtonView: View { .contentShape(Rectangle()) }) .buttonStyle(.proton) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(Color.proton, lineWidth: 4) - ) + .overlay(RoundedRectangle(cornerRadius: 8) + .stroke(Color.proton, lineWidth: 4)) .clipShape(RoundedRectangle(cornerRadius: 8)) .webAuthenticationSession(isPresented: isShowingSafariView) { // swiftlint:disable:next force_unwrapping @@ -57,7 +55,7 @@ struct LogInWithProtonButtonView: View { private func handleResult(_ result: Result) { switch result { - case .success(let url): + case let .success(url): guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) else { return } if let apiQueryItem = components.queryItems?.first(where: { $0.name == "apikey" }), let apiKeyValue = apiQueryItem.value { @@ -65,7 +63,7 @@ struct LogInWithProtonButtonView: View { onSuccess(apiKey) } - case .failure(let error): + case let .failure(error): if let webAuthenticationSessionError = error as? ASWebAuthenticationSessionError { // User clicks on cancel button => do not handle this "error" if case ASWebAuthenticationSessionError.canceledLogin = webAuthenticationSessionError { diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/OtpView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/OtpView.swift index e08fc39..2b6a4b2 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/OtpView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/OtpView.swift @@ -22,10 +22,10 @@ struct OtpView: View { apiService: APIServiceProtocol, onVerification: ((ApiKey) -> Void)? = nil, onActivation: (() async -> Void)? = nil) { - self._mode = mode + _mode = mode let viewModel = OtpViewModel(apiService: apiService, mode: mode.wrappedValue ?? .logIn(mfaKey: "")) - self._viewModel = StateObject(wrappedValue: viewModel) + _viewModel = StateObject(wrappedValue: viewModel) self.onVerification = onVerification self.onActivation = onActivation } @@ -157,7 +157,7 @@ struct OtpView: View { }, label: { Label("Paste from clipboard", systemImage: "doc.on.clipboard") }) - .padding() + .padding() Spacer() } @@ -173,7 +173,7 @@ struct OtpView: View { showingLoadingHud = isLoading } .onReceive(Just(viewModel.apiKey)) { apiKey in - if let apiKey = apiKey { + if let apiKey { onVerification?(apiKey) } } @@ -208,23 +208,17 @@ struct OtpView: View { } /* -struct OtpView_Previews: PreviewProvider { - static var previews: some View { - OtpView(mode: .constant(.activate(email: "john.doe@example.com")), - client: .default) - } -} - */ + struct OtpView_Previews: PreviewProvider { + static var previews: some View { + OtpView(mode: .constant(.activate(email: "john.doe@example.com")), + client: .default) + } + } + */ struct OtpButton: View { let action: () -> Void - let label: () -> Label - - init(action: @escaping () -> Void, - @ViewBuilder label: @escaping () -> Label) { - self.action = action - self.label = label - } + @ViewBuilder let label: () -> Label var body: some View { Button(action: action, label: label) @@ -259,18 +253,18 @@ enum OtpMode { var title: String { switch self { case .logIn: - return "Enter OTP code" + "Enter OTP code" case .activate: - return "Enter activation code" + "Enter activation code" } } var description: String? { switch self { case .logIn: - return nil - case .activate(let email): - return "Please enter the activation code that we've sent to \(email)" + nil + case let .activate(email): + "Please enter the activation code that we've sent to \(email)" } } } @@ -347,7 +341,7 @@ private final class OtpViewModel: ObservableObject { } func paste(string: String?) { - guard let string = string else { + guard let string else { error = SLError.emptyClipboard return } @@ -357,17 +351,17 @@ private final class OtpViewModel: ObservableObject { } let getDigit: (String?) -> Digit = { digitString in switch digitString { - case "0": return .zero - case "1": return .one - case "2": return .two - case "3": return .three - case "4": return .four - case "5": return .five - case "6": return .six - case "7": return .seven - case "8": return .eighth - case "9": return .nine - default: return .none + case "0": .zero + case "1": .one + case "2": .two + case "3": .three + case "4": .four + case "5": .five + case "6": .six + case "7": .seven + case "8": .eighth + case "9": .nine + default: .none } } add(digit: getDigit(string[0])) @@ -383,11 +377,11 @@ private final class OtpViewModel: ObservableObject { isLoading = true let token = [firstDigit, secondDigit, thirdDigit, fourthDigit, fifthDigit, sixthDigit] - .map { $0.rawValue } - .reduce(into: "") { $0 += "\($1)" } + .map(\.rawValue) + .reduce(into: "") { $0 += "\($1)" } switch mode { - case .logIn(let mfaKey): + case let .logIn(mfaKey): Task { @MainActor in do { let mfaEndpoint = MFAEndpoint(token: token, key: mfaKey, device: UIDevice.current.name) @@ -402,7 +396,7 @@ private final class OtpViewModel: ObservableObject { } } - case .activate(let email): + case let .activate(email): Task { @MainActor in do { let activateEndpoint = ActivateEndpoint(email: email, code: token) @@ -415,7 +409,7 @@ private final class OtpViewModel: ObservableObject { } self.reset() if let apiServiceError = error as? APIServiceError, - case .clientError(let errorResponse) = apiServiceError, + case let .clientError(errorResponse) = apiServiceError, errorResponse.statusCode == 410 { self.shouldReactivate = true } else { @@ -436,7 +430,7 @@ private final class OtpViewModel: ObservableObject { } func reactivate() { - guard case .activate(let email) = mode else { return } + guard case let .activate(email) = mode else { return } Task { @MainActor in do { let reactivateEndpoint = ReactivateEndpoint(email: email) diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/SignUpView.swift b/SimpleLogin/SimpleLogin/Modules/Log in/SignUpView.swift index 60fe322..d4da30e 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/SignUpView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/SignUpView.swift @@ -21,7 +21,7 @@ struct SignUpView: View { init(apiService: APIServiceProtocol, onSignUp: @escaping (String, String) -> Void) { - self._viewModel = StateObject(wrappedValue: .init(apiService: apiService)) + _viewModel = StateObject(wrappedValue: .init(apiService: apiService)) self.onSignUp = onSignUp } @@ -53,7 +53,7 @@ struct SignUpView: View { password: $viewModel.password, mode: .signUp, onAction: viewModel.register) - .padding() + .padding() Group { Text("By clicking \"Create account\", you agree to abide by SimpleLogin's Terms & Conditions.") @@ -69,7 +69,7 @@ struct SignUpView: View { } .padding(.horizontal) .frame(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? - UIScreen.main.minLength * 3 / 5 : .infinity) + UIScreen.main.minLength * 3 / 5 : .infinity) Spacer() @@ -109,8 +109,8 @@ struct SignUpView: View { Alert(title: Text("You are all set"), message: Text("We've sent an email to \(viewModel.email). Please check your inbox."), dismissButton: .default(Text("OK")) { - otpMode = .activate(email: viewModel.email) - }) + otpMode = .activate(email: viewModel.email) + }) } } @@ -119,9 +119,9 @@ struct SignUpView: View { OtpView(mode: $otpMode, apiService: viewModel.apiService, onActivation: { - onSignUp(viewModel.email, viewModel.password) - dismiss.callAsFunction() - }) + onSignUp(viewModel.email, viewModel.password) + dismiss.callAsFunction() + }) // swiftlint:enable trailing_closure } } diff --git a/SimpleLogin/SimpleLogin/Modules/Log in/SignUpViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Log in/SignUpViewModel.swift index ddd9197..c3241b0 100644 --- a/SimpleLogin/SimpleLogin/Modules/Log in/SignUpViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Log in/SignUpViewModel.swift @@ -48,7 +48,7 @@ final class SignUpViewModel: ObservableObject { do { let registerEndpoint = RegisterEndpoint(email: email, password: password) _ = try await apiService.execute(registerEndpoint) - self.registeredEmail = email + registeredEmail = email } catch { self.error = error } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/About/AboutView.swift b/SimpleLogin/SimpleLogin/Modules/Main/About/AboutView.swift index 4630fd8..c77eed3 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/About/AboutView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/About/AboutView.swift @@ -21,13 +21,12 @@ struct AboutView: View { Image("Schema") .resizable() .scaledToFit() - NavigationLink( - destination: { - HowItWorksView() - }, - label: { - Text("More detail") - }) + NavigationLink(destination: { + HowItWorksView() + }, + label: { + Text("More detail") + }) } Section { @@ -131,11 +130,11 @@ struct AboutView: View { .scaledToFit() .frame(width: 20, height: 20) }) - .frame(maxWidth: .infinity, alignment: .leading) - .contentShape(Rectangle()) - .onTapGesture { - selectedUrlString = urlString - } + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) + .onTapGesture { + selectedUrlString = urlString + } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/About/How it works/HowItWorkStep.swift b/SimpleLogin/SimpleLogin/Modules/Main/About/How it works/HowItWorkStep.swift index 28cf369..b963a85 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/About/How it works/HowItWorkStep.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/About/How it works/HowItWorkStep.swift @@ -13,34 +13,34 @@ enum HowItWorkStep { var title: String { switch self { case .one: - return "Use email alias everywhere" + "Use email alias everywhere" case .two: - return "Receive emails safely in your inbox" + "Receive emails safely in your inbox" case .three: - return "Send emails anonymously" + "Send emails anonymously" } } var description: String { switch self { case .one: - return "Next time a website asks for your email address, give an alias instead of your real email." + "Next time a website asks for your email address, give an alias instead of your real email." case .two: - return "Emails sent to an alias are forwarded to your inbox without the sender knowing anything." + "Emails sent to an alias are forwarded to your inbox without the sender knowing anything." case .three: // swiftlint:disable:next line_length - return "Just hit \"Reply\" if you want to reply to a forwarded email: the reply is sent from your alias and your real email stays hidden.\nYou can also easily send emails from your alias." + "Just hit \"Reply\" if you want to reply to a forwarded email: the reply is sent from your alias and your real email stays hidden.\nYou can also easily send emails from your alias." } } var imageName: String { switch self { case .one: - return "Step1" + "Step1" case .two: - return "Step2" + "Step2" case .three: - return "Step3" + "Step3" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/Tip.swift b/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/Tip.swift index 6136769..9933c69 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/Tip.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/Tip.swift @@ -14,66 +14,66 @@ enum Tip { var title: String { switch self { case .touchId: - return "Touch ID" + "Touch ID" case .faceId: - return "Face ID" + "Face ID" case .contextMenu: - return "Context menu" + "Context menu" case .fullScreen: - return "Full screen mode" + "Full screen mode" case .shareExtension: - return "Share extension" + "Share extension" case .keyboardExtension: - return "Keyboard extension" + "Keyboard extension" } } var description: String { switch self { case .touchId: - return "Restrict unwelcome access to your SimpleLogin application on this device with Touch ID." + "Restrict unwelcome access to your SimpleLogin application on this device with Touch ID." case .faceId: - return "Restrict unwelcome access to your SimpleLogin application on this device with Face ID." + "Restrict unwelcome access to your SimpleLogin application on this device with Face ID." case .contextMenu: // swiftlint:disable:next line_length - return "Quickly take action on an alias by long pressing to reveal extra options.\nTry it with the test alias below 👇" + "Quickly take action on an alias by long pressing to reveal extra options.\nTry it with the test alias below 👇" case .fullScreen: // swiftlint:disable:next line_length - return "Show your aliases to other people easily without dictating. In alias detail page, either tap on alias or choose \"Enter Full Screen\" option." + "Show your aliases to other people easily without dictating. In alias detail page, either tap on alias or choose \"Enter Full Screen\" option." case .shareExtension: // swiftlint:disable:next line_length - return "Create aliases on the fly without leaving the current context. Whenever you need to create an alias for a website, simply \"share\" the URL and choose SimpleLogin." + "Create aliases on the fly without leaving the current context. Whenever you need to create an alias for a website, simply \"share\" the URL and choose SimpleLogin." case .keyboardExtension: // swiftlint:disable:next line_length - return "Type your aliases without opening SimpleLogin application. Go to Settings ➝ General ➝ Keyboard ➝ Keyboards to enable SimpleLogin keyboard as well as \"Allow Full Access\"" + "Type your aliases without opening SimpleLogin application. Go to Settings ➝ General ➝ Keyboard ➝ Keyboards to enable SimpleLogin keyboard as well as \"Allow Full Access\"" } } var action: String? { switch self { - case .touchId, .faceId, .contextMenu: - return nil + case .contextMenu, .faceId, .touchId: + nil case .fullScreen, .shareExtension: - return "Try it" + "Try it" case .keyboardExtension: - return "Open settings" + "Open settings" } } var systemIconName: String { switch self { case .touchId: - return "touchid" + "touchid" case .faceId: - return "faceid" + "faceid" case .contextMenu: - return "contextualmenu.and.cursorarrow" + "contextualmenu.and.cursorarrow" case .fullScreen: - return "iphone" + "iphone" case .shareExtension: - return "square.and.arrow.up" + "square.and.arrow.up" case .keyboardExtension: - return "keyboard" + "keyboard" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/TipsView.swift b/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/TipsView.swift index adf4597..566c9bb 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/TipsView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/About/Tips/TipsView.swift @@ -96,7 +96,7 @@ private struct TipView: View { .font(.title3) .fontWeight(.bold) } - .toggleStyle(SwitchToggleStyle(tint: .slPurple)) + .toggleStyle(SwitchToggleStyle(tint: .slPurple)) case .faceId: Toggle(isOn: $localAuthenticator.biometricAuthEnabled) { @@ -104,7 +104,7 @@ private struct TipView: View { .font(.title3) .fontWeight(.bold) } - .toggleStyle(SwitchToggleStyle(tint: .slPurple)) + .toggleStyle(SwitchToggleStyle(tint: .slPurple)) default: Text(tip.title) @@ -168,7 +168,7 @@ private struct TipView: View { private func handleAction() { switch tip { - case .touchId, .faceId, .contextMenu: + case .contextMenu, .faceId, .touchId: break case .fullScreen, .shareExtension: showingSheet = true diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountView.swift index 11f309b..32b34cd 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountView.swift @@ -25,8 +25,8 @@ struct AccountView: View { init(session: Session, upgradeNeeded: Binding, onLogOut: @escaping () -> Void) { - self._viewModel = StateObject(wrappedValue: .init(session: session)) - self._upgradeNeeded = upgradeNeeded + _viewModel = StateObject(wrappedValue: .init(session: session)) + _upgradeNeeded = upgradeNeeded self.onLogOut = onLogOut } @@ -71,7 +71,7 @@ struct AccountView: View { return .init(url: url, callbackURLScheme: "auth.simplelogin", onCompletion: viewModel.handleLinkingResult) - .prefersEphemeralWebBrowserSession(true) + .prefersEphemeralWebBrowserSession(true) } // swiftlint:disable:next force_unwrapping return .init(url: URL(string: "https://simplelogin.io")!, @@ -111,37 +111,35 @@ struct AccountView: View { @ViewBuilder private var trailingButton: some View { - if !viewModel.userInfo.inTrial && viewModel.userInfo.isPremium { - NavigationLink( - isActive: $showingPremiumView, - destination: { - PremiumView() - }, - label: { - Button(action: { - showingPremiumView = true - }, label: { - Text("Premium") - }) - }) + if !viewModel.userInfo.inTrial, viewModel.userInfo.isPremium { + NavigationLink(isActive: $showingPremiumView, + destination: { + PremiumView() + }, + label: { + Button(action: { + showingPremiumView = true + }, label: { + Text("Premium") + }) + }) } else { - NavigationLink( - isActive: $showingUpgradeView, - destination: { - UpgradeView(session: viewModel.session) { - confettiCounter += 1 - Task { - await viewModel.refresh(force: true) - } - } - }, - label: { - Button(action: { - showingUpgradeView = true - }, label: { - Text("Upgrade") - }) - }) + NavigationLink(isActive: $showingUpgradeView, + destination: { + UpgradeView(session: viewModel.session) { + confettiCounter += 1 + Task { + await viewModel.refresh(force: true) + } + } + }, + label: { + Button(action: { + showingUpgradeView = true + }, label: { + Text("Upgrade") + }) + }) } } } @@ -229,15 +227,15 @@ private struct UserInfoSection: View { }, label: { Image(systemName: "square.and.pencil") }) - .disabled(viewModel.isLoading) + .disabled(viewModel.isLoading) } private var settingsAlert: Alert { Alert(title: Text("Please allow access to photo library"), message: nil, primaryButton: .default(Text("Open Settings")) { - viewModel.openAppSettings() - }, + viewModel.openAppSettings() + }, secondaryButton: .cancel()) } @@ -283,8 +281,8 @@ private struct AliasesSection: View { .tag(mode) } } - .pickerStyle(SegmentedPickerStyle()) - .disabled(viewModel.isLoading) + .pickerStyle(SegmentedPickerStyle()) + .disabled(viewModel.isLoading) Text("Ex: \(viewModel.randomMode.example)") .font(.caption) @@ -319,8 +317,8 @@ private struct AliasesSection: View { .tag(mode) } } - .pickerStyle(SegmentedPickerStyle()) - .disabled(viewModel.isLoading) + .pickerStyle(SegmentedPickerStyle()) + .disabled(viewModel.isLoading) Text("Ex: \(viewModel.randomAliasSuffix.example)") .font(.caption) @@ -366,7 +364,8 @@ private struct SenderFormatSection: View { private var footerAttributedString: AttributedString { // swiftlint:disable:next line_length - var attributedString = AttributedString("John Doe who uses john.doe@example.com to send you an email, how would you like to format his email?") + var attributedString = + AttributedString("John Doe who uses john.doe@example.com to send you an email, how would you like to format his email?") if let range = attributedString.range(of: "john.doe@example.com") { attributedString[range].foregroundColor = .accentColor } @@ -416,23 +415,23 @@ private struct ConnectToProtonSection: View { } }, label: { Label(protonAddress == nil ? - "Connect with Proton" : "Unlink account", - image: "Proton") + "Connect with Proton" : "Unlink account", + image: "Proton") }) .foregroundColor(.proton) }, header: { Text("Connect with Proton") }, footer: { - if let protonAddress = protonAddress { + if let protonAddress { Text("Your account is currently linked to the Proton account ") + - Text(protonAddress) + Text(protonAddress) .fontWeight(.bold) } else { Text(""" -You can connect your Proton and SimpleLogin accounts. -You can then quickly log in to your SimpleLogin account using the Proton one. -If you have Proton Unlimited, Business or Visionary, you can have SimpleLogin premium for free. -""") + You can connect your Proton and SimpleLogin accounts. + You can then quickly log in to your SimpleLogin account using the Proton one. + If you have Proton Unlimited, Business or Visionary, you can have SimpleLogin premium for free. + """) } }) .alert(isPresented: $showingAlert) { @@ -458,14 +457,14 @@ private struct LogOutSection: View { Text("Log out") .foregroundColor(.red) }) - .disabled(viewModel.isLoading) - .opacity(viewModel.isLoading ? 0.5 : 1.0) - .alert(isPresented: $isShowingAlert) { - Alert(title: Text("You will be logged out"), - message: Text("Please confirm"), - primaryButton: .destructive(Text("Yes, log me out"), action: onLogOut), - secondaryButton: .cancel()) - } + .disabled(viewModel.isLoading) + .opacity(viewModel.isLoading ? 0.5 : 1.0) + .alert(isPresented: $isShowingAlert) { + Alert(title: Text("You will be logged out"), + message: Text("Please confirm"), + primaryButton: .destructive(Text("Yes, log me out"), action: onLogOut), + secondaryButton: .cancel()) + } } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountViewModel.swift index 38d2848..33ac04e 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/AccountViewModel.swift @@ -33,61 +33,61 @@ final class AccountViewModel: ObservableObject { init(session: Session) { self.session = session let shouldUpdateUserSettings: () -> Bool = { [unowned self] in - self.isInitialized && self.error == nil + isInitialized && error == nil } $isLoading .sink { [weak self] isLoading in - guard let self = self else { return } + guard let self else { return } if isLoading { - self.error = nil + error = nil } } .store(in: &cancellables) $notification .sink { [weak self] selectedNotification in - guard let self = self else { return } - if shouldUpdateUserSettings(), selectedNotification != self.notification { - self.update(option: .notification(selectedNotification)) + guard let self else { return } + if shouldUpdateUserSettings(), selectedNotification != notification { + update(option: .notification(selectedNotification)) } } .store(in: &cancellables) $randomMode .sink { [weak self] selectedRandomMode in - guard let self = self else { return } - if shouldUpdateUserSettings(), selectedRandomMode != self.randomMode { - self.update(option: .randomMode(selectedRandomMode)) + guard let self else { return } + if shouldUpdateUserSettings(), selectedRandomMode != randomMode { + update(option: .randomMode(selectedRandomMode)) } } .store(in: &cancellables) $randomAliasDefaultDomain .sink { [weak self] selectedUsableDomain in - guard let self = self else { return } - if let selectedUsableDomain = selectedUsableDomain, + guard let self else { return } + if let selectedUsableDomain, shouldUpdateUserSettings(), - selectedUsableDomain != self.randomAliasDefaultDomain { - self.update(option: .randomAliasDefaultDomain(selectedUsableDomain.domain)) + selectedUsableDomain != randomAliasDefaultDomain { + update(option: .randomAliasDefaultDomain(selectedUsableDomain.domain)) } } .store(in: &cancellables) $senderFormat .sink { [weak self] selectedSenderFormat in - guard let self = self else { return } - if shouldUpdateUserSettings(), selectedSenderFormat != self.senderFormat { - self.update(option: .senderFormat(selectedSenderFormat)) + guard let self else { return } + if shouldUpdateUserSettings(), selectedSenderFormat != senderFormat { + update(option: .senderFormat(selectedSenderFormat)) } } .store(in: &cancellables) $randomAliasSuffix .sink { [weak self] selectedRandomAliasSuffix in - guard let self = self else { return } - if shouldUpdateUserSettings(), selectedRandomAliasSuffix != self.randomAliasSuffix { - self.update(option: .randomAliasSuffix(selectedRandomAliasSuffix)) + guard let self else { return } + if shouldUpdateUserSettings(), selectedRandomAliasSuffix != randomAliasSuffix { + update(option: .randomAliasSuffix(selectedRandomAliasSuffix)) } } .store(in: &cancellables) @@ -111,13 +111,13 @@ final class AccountViewModel: ObservableObject { bind(userSettings: userSettings) self.usableDomains = usableDomains // swiftlint:disable:next line_length - self.randomAliasDefaultDomain = usableDomains.first { $0.domain == userSettings.randomAliasDefaultDomain } + randomAliasDefaultDomain = usableDomains.first { $0.domain == userSettings.randomAliasDefaultDomain } isInitialized = true } catch { if let apiServiceError = error as? APIServiceError, - case .clientError(let errorResponse) = apiServiceError, + case let .clientError(errorResponse) = apiServiceError, errorResponse.statusCode == 401 { - self.shouldLogOut = true + shouldLogOut = true return } self.error = error @@ -144,12 +144,12 @@ final class AccountViewModel: ObservableObject { } private func bind(userSettings: UserSettings) { - self.notification = userSettings.notification - self.randomMode = userSettings.randomMode - self.randomAliasDefaultDomain = usableDomains.first { $0.domain == userSettings.randomAliasDefaultDomain } - self.senderFormat = userSettings.senderFormat - self.randomAliasSuffix = userSettings.randomAliasSuffix - self.lastKnownUserSettings = userSettings + notification = userSettings.notification + randomMode = userSettings.randomMode + randomAliasDefaultDomain = usableDomains.first { $0.domain == userSettings.randomAliasDefaultDomain } + senderFormat = userSettings.senderFormat + randomAliasSuffix = userSettings.randomAliasSuffix + lastKnownUserSettings = userSettings } func uploadNewProfilePhoto(_ image: UIImage) { @@ -247,7 +247,7 @@ final class AccountViewModel: ObservableObject { func handleLinkingResult(_ result: Result) { switch result { - case .success(let url): + case let .success(url): if url.absoluteString.contains("link") { message = "Your Proton account has been successfully linked" Task { @@ -255,7 +255,7 @@ final class AccountViewModel: ObservableObject { } } - case .failure(let error): + case let .failure(error): if let webAuthenticationSessionError = error as? ASWebAuthenticationSessionError { // User clicks on cancel button => do not handle this "error" if case ASWebAuthenticationSessionError.canceledLogin = webAuthenticationSessionError { @@ -283,15 +283,15 @@ extension UserInfo { extension RandomMode: CustomStringConvertible { public var description: String { switch self { - case .uuid: return "UUID" - case .word: return "Random words" + case .uuid: "UUID" + case .word: "Random words" } } var example: String { switch self { - case .uuid: return "hdy792o-ydy8-269d-ojan@example.com" - case .word: return "meaningless_random@example.com" + case .uuid: "hdy792o-ydy8-269d-ojan@example.com" + case .word: "meaningless_random@example.com" } } } @@ -299,15 +299,15 @@ extension RandomMode: CustomStringConvertible { extension RandomAliasSuffix: CustomStringConvertible { public var description: String { switch self { - case .word: return "Random word" - case .randomString: return "Random 5 characters" + case .word: "Random word" + case .randomString: "Random 5 characters" } } var example: String { switch self { - case .word: return ".meaningless@example.com" - case .randomString: return ".u9jnqn@example.com" + case .word: ".meaningless@example.com" + case .randomString: ".u9jnqn@example.com" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/PremiumView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/PremiumView.swift index c6da8e7..b4a549d 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/PremiumView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/PremiumView.swift @@ -33,12 +33,14 @@ struct PremiumView: View { } private var gradientBackground: some View { - LinearGradient(gradient: .init(colors: [.slPurple.opacity(0.05), - .slPurple.opacity(0.1), - .slPurple.opacity(0.15), - .slPurple.opacity(0.2)]), - startPoint: .top, - endPoint: .bottom) + LinearGradient(gradient: .init(colors: [ + .slPurple.opacity(0.05), + .slPurple.opacity(0.1), + .slPurple.opacity(0.15), + .slPurple.opacity(0.2) + ]), + startPoint: .top, + endPoint: .bottom) .edgesIgnoringSafeArea([.leading, .trailing, .bottom]) } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeView.swift index 761ca3f..9456ff4 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeView.swift @@ -61,23 +61,24 @@ struct UpgradeView: View { .alertToastError($viewModel.error) .betterSafariView(urlString: $selectedUrlString) .alert(isPresented: $showingThankAlert) { - Alert( - title: Text("Thank you"), - message: Text("You are now a premium user 🎉"), - dismissButton: .default(Text("Got it 👍")) { - onSubscription() - presentationMode.wrappedValue.dismiss() - }) + Alert(title: Text("Thank you"), + message: Text("You are now a premium user 🎉"), + dismissButton: .default(Text("Got it 👍")) { + onSubscription() + presentationMode.wrappedValue.dismiss() + }) } } private var gradientBackground: some View { - LinearGradient(gradient: .init(colors: [.slPurple.opacity(0.05), - .slPurple.opacity(0.1), - .slPurple.opacity(0.15), - .slPurple.opacity(0.2)]), - startPoint: .top, - endPoint: .bottom) + LinearGradient(gradient: .init(colors: [ + .slPurple.opacity(0.05), + .slPurple.opacity(0.1), + .slPurple.opacity(0.15), + .slPurple.opacity(0.2) + ]), + startPoint: .top, + endPoint: .bottom) .edgesIgnoringSafeArea([.leading, .trailing, .bottom]) } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeViewModel.swift index dd03e96..5758e61 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/UpgradeViewModel.swift @@ -24,7 +24,7 @@ final class UpgradeViewModel: ObservableObject { } func retrieveProductsInfo() { - let productIds = Set(Subscription.allCases.map { $0.productId }) + let productIds = Set(Subscription.allCases.map(\.productId)) SwiftyStoreKit.retrieveProductsInfo(productIds) { result in if let error = result.error { self.error = error @@ -53,15 +53,15 @@ final class UpgradeViewModel: ObservableObject { } private func purchase(_ product: SKProduct?) { - guard let product = product, !isLoading else { return } + guard let product, !isLoading else { return } isLoading = true SwiftyStoreKit.purchaseProduct(product) { [weak self] result in - guard let self = self else { return } - self.isLoading = false + guard let self else { return } + isLoading = false switch result { case .success: - self.fetchAndSendReceipt() - case .error(let error): + fetchAndSendReceipt() + case let .error(error): self.error = error case .deferred: break @@ -72,9 +72,9 @@ final class UpgradeViewModel: ObservableObject { private func fetchAndSendReceipt() { isLoading = true SwiftyStoreKit.fetchReceipt(forceRefresh: false) { [weak self] result in - guard let self = self else { return } + guard let self else { return } switch result { - case .success(let receiptData): + case let .success(receiptData): let encryptedReceipt = receiptData.base64EncodedString() Task { @MainActor in defer { self.isLoading = false } @@ -89,8 +89,8 @@ final class UpgradeViewModel: ObservableObject { } } - case .error(let error): - self.isLoading = false + case let .error(error): + isLoading = false self.error = error } } @@ -102,8 +102,8 @@ enum Subscription: CaseIterable { var productId: String { switch self { - case .monthly: return "io.simplelogin.ios_app.subscription.premium.monthly" - case .yearly: return "io.simplelogin.ios_app.subscription.premium.yearly" + case .monthly: "io.simplelogin.ios_app.subscription.premium.monthly" + case .yearly: "io.simplelogin.ios_app.subscription.premium.yearly" } } } @@ -161,9 +161,9 @@ extension SKError.Code { case .unsupportedPlatform: return "The current platform does not support overlays" case .unknown: - return "Unknown SKError (\(self.rawValue))" + return "Unknown SKError (\(rawValue))" @unknown default: - return "Unknown default SKError (\(self.rawValue))" + return "Unknown default SKError (\(rawValue))" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Account/UsableDomainsView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Account/UsableDomainsView.swift index 2be8d93..8fa2b08 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Account/UsableDomainsView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Account/UsableDomainsView.swift @@ -1,5 +1,5 @@ // -// DefaultDomainsView.swift +// UsableDomainsView.swift // SimpleLogin // // Created by Nhon Nguyen on 25/04/2022. diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/AdvancedView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/AdvancedView.swift index 593afeb..770d0ed 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/AdvancedView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/AdvancedView.swift @@ -1,5 +1,5 @@ // -// OthersView.swift +// AdvancedView.swift // SimpleLogin // // Created by Thanh-Nhon Nguyen on 02/09/2021. @@ -18,25 +18,23 @@ struct AdvancedView: View { NavigationView { Form { Section(footer: mailboxesSectionFooter) { - NavigationLink( - isActive: $showingMailboxesView, - destination: { - MailboxesView(session: session) - }, - label: { - Label("Mailboxes", systemImage: "tray.2.fill") - }) + NavigationLink(isActive: $showingMailboxesView, + destination: { + MailboxesView(session: session) + }, + label: { + Label("Mailboxes", systemImage: "tray.2.fill") + }) } Section(footer: customDomainsSectionFooter) { - NavigationLink( - isActive: $showingCustomDomainsView, - destination: { - CustomDomainsView(session: session) - }, - label: { - Label("Custom domains", systemImage: "globe") - }) + NavigationLink(isActive: $showingCustomDomainsView, + destination: { + CustomDomainsView(session: session) + }, + label: { + Label("Custom domains", systemImage: "globe") + }) } } .navigationTitle("Advanced") diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsView.swift index ef81e00..3107c73 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsView.swift @@ -68,7 +68,7 @@ struct CustomDomainsView: View { } private var unverifiedDomainAlert: Alert { - guard let selectedUnverifiedDomain = selectedUnverifiedDomain else { + guard let selectedUnverifiedDomain else { return .init(title: Text("selectedUnverifiedDomain is nil"), message: nil, dismissButton: .cancel()) @@ -104,12 +104,12 @@ private struct DomainView: View { } /* -struct CustomDomainsView_Previews: PreviewProvider { - static var previews: some View { - Form { - DomainView(domain: .verified) - DomainView(domain: .unverified) - } - } -} -*/ + struct CustomDomainsView_Previews: PreviewProvider { + static var previews: some View { + Form { + DomainView(domain: .verified) + DomainView(domain: .unverified) + } + } + } + */ diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsViewModel.swift index f2d3382..adf8f52 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/CustomDomainsViewModel.swift @@ -15,7 +15,9 @@ final class CustomDomainsViewModel: ObservableObject { @Published var isLoading = false @Published var error: Error? - var noDomains: Bool { !isLoading && domains.isEmpty } + var noDomains: Bool { + !isLoading && domains.isEmpty + } let session: Session @@ -41,7 +43,7 @@ final class CustomDomainsViewModel: ObservableObject { } } -extension Array where Element == CustomDomain { +extension [CustomDomain] { func sortedById() -> Self { sorted { $0.id > $1.id } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DeletedAliasesViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DeletedAliasesViewModel.swift index abceb4e..697ff4f 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DeletedAliasesViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DeletedAliasesViewModel.swift @@ -18,7 +18,9 @@ final class DeletedAliasesViewModel: ObservableObject { private let session: Session let domain: CustomDomain - var noAliases: Bool { !isLoading && deletedAliases.isEmpty } + var noAliases: Bool { + !isLoading && deletedAliases.isEmpty + } init(session: Session, domain: CustomDomain) { self.domain = domain diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailView.swift index 3dab969..b7af6f4 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailView.swift @@ -38,6 +38,7 @@ struct DomainDetailView: View { } // MARK: - Sections + private struct DomainNameSection: View { let domain: CustomDomain @@ -71,7 +72,7 @@ private struct CatchAllSection: View { NavigationLink(destination: { EditMailboxesView(viewModel: viewModel) }, label: { - Text(domain.mailboxes.map { $0.email }.joined(separator: "\n")) + Text(domain.mailboxes.map(\.email).joined(separator: "\n")) }) } .frame(maxWidth: .infinity, alignment: .leading) @@ -92,7 +93,7 @@ private struct EditMailboxesView: View { init(viewModel: DomainDetailViewModel) { _viewModel = .init(wrappedValue: viewModel) - _selectedIds = .init(initialValue: viewModel.domain.mailboxes.map { $0.id }) + _selectedIds = .init(initialValue: viewModel.domain.mailboxes.map(\.id)) } var body: some View { @@ -114,7 +115,7 @@ private struct EditMailboxesView: View { .contentShape(Rectangle()) .onTapGesture { // guard mailbox.verified else { return } - if selectedIds.contains(mailbox.id) && selectedIds.count > 1 { + if selectedIds.contains(mailbox.id), selectedIds.count > 1 { selectedIds.removeAll { $0 == mailbox.id } } else if !selectedIds.contains(mailbox.id) { selectedIds.append(mailbox.id) diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailViewModel.swift index 9efd590..78dce72 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/DomainDetailViewModel.swift @@ -33,18 +33,18 @@ final class DomainDetailViewModel: ObservableObject { $catchAll .sink { [weak self] selectedCatchAll in - guard let self = self else { return } - if selectedCatchAll != self.catchAll { - self.update(option: .catchAll(selectedCatchAll)) + guard let self else { return } + if selectedCatchAll != catchAll { + update(option: .catchAll(selectedCatchAll)) } } .store(in: &cancellables) $randomPrefixGeneration .sink { [weak self] selectedRandomPrefixGeneration in - guard let self = self else { return } - if selectedRandomPrefixGeneration != self.randomPrefixGeneration { - self.update(option: .randomPrefixGeneration(selectedRandomPrefixGeneration)) + guard let self else { return } + if selectedRandomPrefixGeneration != randomPrefixGeneration { + update(option: .randomPrefixGeneration(selectedRandomPrefixGeneration)) } } .store(in: &cancellables) @@ -56,8 +56,8 @@ final class DomainDetailViewModel: ObservableObject { private func bind(domain: CustomDomain) { self.domain = domain - self.catchAll = domain.catchAll - self.randomPrefixGeneration = domain.randomPrefixGeneration + catchAll = domain.catchAll + randomPrefixGeneration = domain.randomPrefixGeneration } @MainActor @@ -98,7 +98,7 @@ final class DomainDetailViewModel: ObservableObject { do { let getMailboxesEndpoint = GetMailboxesEndpoint(apiKey: session.apiKey.value) mailboxes = try await session.execute(getMailboxesEndpoint).mailboxes - .filter { $0.verified } + .filter(\.verified) .sortedById() } catch { self.error = error diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesView.swift index 874d190..34709a4 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesView.swift @@ -64,7 +64,7 @@ struct MailboxesView: View { } private var deletionAlert: Alert { - guard let mailboxToBeDeleted = mailboxToBeDeleted else { + guard let mailboxToBeDeleted else { return .init(title: Text("mailboxToBeDeleted is nil"), message: nil, dismissButton: .cancel()) @@ -87,7 +87,7 @@ struct MailboxesView: View { autocapitalizationType: .none, clearButtonMode: .never, actionTitle: "Submit") { newMailbox in - if let newMailbox = newMailbox { + if let newMailbox { viewModel.addMailbox(email: newMailbox) } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesViewModel.swift index c689a76..1117c5a 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Advanced/MailboxesViewModel.swift @@ -81,7 +81,7 @@ final class MailboxesViewModel: ObservableObject { } } -extension Array where Element == Mailbox { +extension [Mailbox] { func sortedById() -> Self { sorted { $0.id > $1.id } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasCompactView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasCompactView.swift index b0ae4f0..37cff3f 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasCompactView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasCompactView.swift @@ -47,9 +47,9 @@ struct AliasCompactView: View { Image(systemName: activity.action.iconSystemName) .foregroundColor(activity.action.color) }) - .font(.caption) - .frame(maxWidth: .infinity, alignment: .leading) - .fixedSize(horizontal: false, vertical: true) + .font(.caption) + .frame(maxWidth: .infinity, alignment: .leading) + .fixedSize(horizontal: false, vertical: true) } else { Label("\(alias.creationDateString) (\(alias.relativeCreationDateString))", systemImage: "clock.fill") @@ -69,7 +69,7 @@ struct AliasCompactView: View { .fixedSize(horizontal: false, vertical: true) } - if !alias.noActivities && displayMode == .default { + if !alias.noActivities, displayMode == .default { ActivitiesView(alias: alias) .padding(.leading) } @@ -81,10 +81,10 @@ struct AliasCompactView: View { }, icon: { Image(systemName: "square.and.pencil") }) - .font(.caption) - .foregroundColor(Color.secondary) - .frame(maxWidth: .infinity, alignment: .leading) - .fixedSize(horizontal: false, vertical: true) + .font(.caption) + .foregroundColor(Color.secondary) + .frame(maxWidth: .infinity, alignment: .leading) + .fixedSize(horizontal: false, vertical: true) } ActionsView(alias: alias, @@ -159,7 +159,7 @@ private struct ActivitiesView: View { Text("\(count)") .font(.headline) .fontWeight(.bold) - // swiftlint:disable:next empty_count + // swiftlint:disable:next empty_count .opacity(count == 0 ? 0.5 : 1) Spacer() diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsView.swift index a024071..3ee7775 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsView.swift @@ -48,9 +48,9 @@ struct AliasContactsView: View { ContactView(viewModel: viewModel, copiedText: $copiedText, contact: contact) - .onAppear { - viewModel.getMoreContactsIfNeed(currentContact: contact) - } + .onAppear { + viewModel.getMoreContactsIfNeed(currentContact: contact) + } } } else if !viewModel.isFetchingContacts { Text("No contacts") @@ -85,9 +85,9 @@ struct AliasContactsView: View { .alert("Please upgrade to create contacts", isPresented: $viewModel.shouldUpgrade, actions: { - Button("Upgrade", role: nil, action: onUpgrade) - Button("Cancel", role: .cancel) {} - }) + Button("Upgrade", role: nil, action: onUpgrade) + Button("Cancel", role: .cancel) {} + }) .betterSafariView(urlString: $selectedUrlString) .alertToastCopyMessage(isPresenting: showingCopyAlert, message: copiedText) .alertToastError($viewModel.error) @@ -108,9 +108,9 @@ struct AliasContactsView: View { private func makeShowingCreatedContactAlert() -> Binding { .init(get: { if viewModel.createdContact != nil { - return "Created new contact" + "Created new contact" } else { - return nil + nil } }, set: { message in if message == nil { diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsViewModel.swift index e49ac18..17fb570 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasContactsViewModel.swift @@ -29,7 +29,7 @@ final class AliasContactsViewModel: ObservableObject { } func getMoreContactsIfNeed(currentContact: Contact?) { - guard let currentContact = currentContact else { + guard let currentContact else { getMoreContacts() return } @@ -41,7 +41,7 @@ final class AliasContactsViewModel: ObservableObject { } private func getMoreContacts() { - guard !isFetchingContacts && canLoadMorePages else { return } + guard !isFetchingContacts, canLoadMorePages else { return } Task { @MainActor in defer { isFetchingContacts = false } isFetchingContacts = true @@ -61,8 +61,8 @@ final class AliasContactsViewModel: ObservableObject { do { let contacts = try await getContacts(page: 0) self.contacts = contacts - self.currentPage = 1 - self.canLoadMorePages = contacts.count == kDefaultPageSize + currentPage = 1 + canLoadMorePages = contacts.count == kDefaultPageSize } catch { self.error = error } @@ -149,7 +149,7 @@ final class AliasContactsViewModel: ObservableObject { } func handledCreatedContact() { - self.createdContact = nil + createdContact = nil } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailView.swift index 1c108b1..684a28f 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailView.swift @@ -9,9 +9,9 @@ import Combine import SimpleLoginPackage import SwiftUI -// A view that takes an alias as binding to properly show the alias details -// or a placeholder view when the binding is nil. -// To achieve the "dismiss" feeling when the alias is deleted in iPad. +/// A view that takes an alias as binding to properly show the alias details +/// or a placeholder view when the binding is nil. +/// To achieve the "dismiss" feeling when the alias is deleted in iPad. struct AliasDetailWrapperView: View { @Environment(\.dismiss) private var dismiss @Binding var selectedAlias: Alias? @@ -25,7 +25,7 @@ struct AliasDetailWrapperView: View { onUpdateAlias: @escaping (Alias) -> Void, onDeleteAlias: @escaping (Alias) -> Void, onUpgrade: @escaping () -> Void) { - self._selectedAlias = selectedAlias + _selectedAlias = selectedAlias self.session = session self.onUpdateAlias = onUpdateAlias self.onDeleteAlias = onDeleteAlias @@ -33,19 +33,18 @@ struct AliasDetailWrapperView: View { } var body: some View { - if let selectedAlias = selectedAlias { - AliasDetailView( - alias: selectedAlias, - session: session, - onUpdateAlias: onUpdateAlias, - onDeleteAlias: { deletedAlias in - onDeleteAlias(deletedAlias) - // Dismiss when in single view mode (iPhone) - dismiss() - // Show placeholder view in master detail mode (iPad) - self.selectedAlias = nil - }, - onUpgrade: onUpgrade) + if let selectedAlias { + AliasDetailView(alias: selectedAlias, + session: session, + onUpdateAlias: onUpdateAlias, + onDeleteAlias: { deletedAlias in + onDeleteAlias(deletedAlias) + // Dismiss when in single view mode (iPhone) + dismiss() + // Show placeholder view in master detail mode (iPad) + self.selectedAlias = nil + }, + onUpgrade: onUpgrade) } else { DetailPlaceholderView.aliasDetails } @@ -133,6 +132,7 @@ struct AliasDetailView: View { } // MARK: - Sections + private struct ActionsSection: View { @Environment(\.colorScheme) private var colorScheme @State private var showingContacts = false @@ -141,7 +141,9 @@ private struct ActionsSection: View { var enterFullScreen: () -> Void let onUpgrade: () -> Void - private var alias: Alias { viewModel.alias } + private var alias: Alias { + viewModel.alias + } var body: some View { Section(content: { @@ -182,58 +184,49 @@ private struct ActionsSection: View { } private var pinUnpinButton: some View { - button( - action: { - Vibration.soft.vibrate() - viewModel.update(option: .pinned(!alias.pinned)) - }, - image: Image(systemName: viewModel.alias.pinned ? "bookmark.slash" : "bookmark.fill"), - text: Text(viewModel.alias.pinned ? "unpin" : "pin") - ) + button(action: { + Vibration.soft.vibrate() + viewModel.update(option: .pinned(!alias.pinned)) + }, + image: Image(systemName: viewModel.alias.pinned ? "bookmark.slash" : "bookmark.fill"), + text: Text(viewModel.alias.pinned ? "unpin" : "pin")) .foregroundColor(alias.pinned ? .red : .slPurple) } private var activateDeactivateButton: some View { - button( - action: { - Vibration.soft.vibrate() - viewModel.toggle() - }, - image: Image(systemName: alias.enabled ? "circle.dashed" : "checkmark.circle.fill"), - text: Text(alias.enabled ? "deactivate" : "activate") - ) + button(action: { + Vibration.soft.vibrate() + viewModel.toggle() + }, + image: Image(systemName: alias.enabled ? "circle.dashed" : "checkmark.circle.fill"), + text: Text(alias.enabled ? "deactivate" : "activate")) .foregroundColor(alias.enabled ? .red : .slPurple) } private var copyButton: some View { - button( - action: { - Vibration.soft.vibrate() - copiedText = alias.email - UIPasteboard.general.string = alias.email - }, - image: Image(systemName: "doc.on.doc.fill"), - text: Text("copy") - ) + button(action: { + Vibration.soft.vibrate() + copiedText = alias.email + UIPasteboard.general.string = alias.email + }, + image: Image(systemName: "doc.on.doc.fill"), + text: Text("copy")) .foregroundColor(.slPurple) } private var sendEmailButton: some View { - NavigationLink( - isActive: $showingContacts, - destination: { - AliasContactsView(alias: alias, session: viewModel.session, onUpgrade: onUpgrade) - }, - label: { - button( - action: { - showingContacts = true - }, - image: Image(systemName: "paperplane.fill"), - text: Text("contacts") - ) - .foregroundColor(.slPurple) - }) + NavigationLink(isActive: $showingContacts, + destination: { + AliasContactsView(alias: alias, session: viewModel.session, onUpgrade: onUpgrade) + }, + label: { + button(action: { + showingContacts = true + }, + image: Image(systemName: "paperplane.fill"), + text: Text("contacts")) + .foregroundColor(.slPurple) + }) } } @@ -246,7 +239,7 @@ private struct MailboxesSection: View { NavigationLink(destination: { EditMailboxesView(viewModel: viewModel) }, label: { - let allMailboxes = viewModel.alias.mailboxes.map { $0.email }.joined(separator: "\n") + let allMailboxes = viewModel.alias.mailboxes.map(\.email).joined(separator: "\n") Text(allMailboxes) .lineLimit(5) }) @@ -258,7 +251,7 @@ private struct MailboxesSection: View { } .foregroundColor(.slPurple) }) - .betterSafariView(urlString: $selectedUrlString) + .betterSafariView(urlString: $selectedUrlString) } } @@ -351,9 +344,9 @@ private struct ActivitiesSection: View { } } }) - .onAppear { - viewModel.getMoreActivitiesIfNeed(currentActivity: nil) - } + .onAppear { + viewModel.getMoreActivitiesIfNeed(currentActivity: nil) + } } private func section(action: ActivityAction, count: Int) -> some View { @@ -464,6 +457,7 @@ private struct AllActivitiesView: View { } // MARK: - Edit views + private struct EditMailboxesView: View { @Environment(\.dismiss) private var dismiss @ObservedObject var viewModel: AliasDetailViewModel @@ -471,7 +465,7 @@ private struct EditMailboxesView: View { init(viewModel: AliasDetailViewModel) { _viewModel = .init(wrappedValue: viewModel) - _selectedIds = .init(initialValue: viewModel.alias.mailboxes.map { $0.id }) + _selectedIds = .init(initialValue: viewModel.alias.mailboxes.map(\.id)) } var body: some View { @@ -493,7 +487,7 @@ private struct EditMailboxesView: View { .contentShape(Rectangle()) .onTapGesture { guard mailbox.verified else { return } - if selectedIds.contains(mailbox.id) && selectedIds.count > 1 { + if selectedIds.contains(mailbox.id), selectedIds.count > 1 { selectedIds.removeAll { $0 == mailbox.id } } else if !selectedIds.contains(mailbox.id) { selectedIds.append(mailbox.id) diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailViewModel.swift index d29e6c0..cb30f12 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasDetailViewModel.swift @@ -39,7 +39,7 @@ final class AliasDetailViewModel: ObservableObject { } func getMoreActivitiesIfNeed(currentActivity activity: AliasActivity?) { - guard let activity = activity else { + guard let activity else { getMoreActivities() return } @@ -51,7 +51,7 @@ final class AliasDetailViewModel: ObservableObject { } private func getMoreActivities() { - guard !isLoadingActivities && canLoadMorePages else { return } + guard !isLoadingActivities, canLoadMorePages else { return } defer { isLoadingActivities = false } isLoadingActivities = true Task { @MainActor in @@ -93,11 +93,11 @@ final class AliasDetailViewModel: ObservableObject { do { let getAliasEndpoint = GetAliasEndpoint(apiKey: session.apiKey.value, aliasID: alias.id) - self.alias = try await session.execute(getAliasEndpoint) - self.activities = try await getActivities(page: 0) - self.currentPage = 1 - self.canLoadMorePages = activities.count == kDefaultPageSize - self.onUpdateAlias(alias) + alias = try await session.execute(getAliasEndpoint) + activities = try await getActivities(page: 0) + currentPage = 1 + canLoadMorePages = activities.count == kDefaultPageSize + onUpdateAlias(alias) } catch { self.error = error } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasEmailView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasEmailView.swift index 32cc180..af8d4fd 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasEmailView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasEmailView.swift @@ -21,15 +21,15 @@ struct AliasEmailView: View { var systemImageName: String { switch self { - case .text: return "textformat.abc" - case .qr: return "qrcode" + case .text: "textformat.abc" + case .qr: "qrcode" } } var oppositeMode: Mode { switch self { - case .text: return .qr - case .qr: return .text + case .text: .qr + case .qr: .text } } } @@ -71,7 +71,7 @@ struct AliasEmailView: View { ToolbarItem(placement: .navigationBarTrailing) { Button(action: { - self.mode = mode.oppositeMode + mode = mode.oppositeMode }, label: { Image(systemName: mode.oppositeMode.systemImageName) }) diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesView.swift index 6e12b84..9bf9362 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesView.swift @@ -54,57 +54,54 @@ struct AliasesView: View { NavigationView { ZStack { - NavigationLink( - tag: Link.details, - selection: $selectedLink, - destination: { - AliasDetailWrapperView( - selectedAlias: $selectedAlias, - session: viewModel.session, - onUpdateAlias: { updatedAlias in - if createdAlias?.id == updatedAlias.id { - createdAlias = updatedAlias - } - viewModel.update(alias: updatedAlias) - }, - onDeleteAlias: { deletedAlias in - if deletedAlias.id == createdAlias?.id { - createdAlias = nil - } - viewModel.remove(alias: deletedAlias) - }, - onUpgrade: onUpgrade) - .ignoresSafeArea(.keyboard) - .onAppear { - if UIDevice.current.userInterfaceIdiom != .phone { - selectedLink = nil - } - } - }, - label: { - EmptyView() - }) + NavigationLink(tag: Link.details, + selection: $selectedLink, + destination: { + AliasDetailWrapperView(selectedAlias: $selectedAlias, + session: viewModel.session, + onUpdateAlias: { updatedAlias in + if createdAlias?.id == updatedAlias.id { + createdAlias = updatedAlias + } + viewModel.update(alias: updatedAlias) + }, + onDeleteAlias: { deletedAlias in + if deletedAlias.id == createdAlias?.id { + createdAlias = nil + } + viewModel.remove(alias: deletedAlias) + }, + onUpgrade: onUpgrade) + .ignoresSafeArea(.keyboard) + .onAppear { + if UIDevice.current.userInterfaceIdiom != .phone { + selectedLink = nil + } + } + }, + label: { + EmptyView() + }) - NavigationLink( - tag: Link.contacts, - selection: $selectedLink, - destination: { - if let selectedAlias = selectedAlias { - AliasContactsView(alias: selectedAlias, - session: viewModel.session, - onUpgrade: onUpgrade) - .onAppear { - if UIDevice.current.userInterfaceIdiom != .phone { - selectedLink = nil - } - } - } else { - EmptyView() - } - }, - label: { - EmptyView() - }) + NavigationLink(tag: Link.contacts, + selection: $selectedLink, + destination: { + if let selectedAlias { + AliasContactsView(alias: selectedAlias, + session: viewModel.session, + onUpgrade: onUpgrade) + .onAppear { + if UIDevice.current.userInterfaceIdiom != .phone { + selectedLink = nil + } + } + } else { + EmptyView() + } + }, + label: { + EmptyView() + }) ScrollViewReader { proxy in List { @@ -113,9 +110,9 @@ struct AliasesView: View { } if !viewModel.aliases.isEmpty { - if let createdAlias = createdAlias { + if let createdAlias { switch (createdAlias.enabled, viewModel.selectedStatus) { - case (true, .all), (true, .active), (false, .inactive): + case (false, .inactive), (true, .active), (true, .all): aliasCompactView(for: createdAlias) default: EmptyView() @@ -149,7 +146,7 @@ struct AliasesView: View { .refreshable { await viewModel.refresh() } .animation(.default, value: viewModel.stats != nil) .onReceive(Just(createdAlias)) { createdAlias in - if let createdAlias = createdAlias { + if let createdAlias { if !viewModel.isHandled(createdAlias) { showingCreatedAliasAlert = true DispatchQueue.main.asyncAfter(deadline: .now() + 1) { @@ -162,7 +159,7 @@ struct AliasesView: View { } } .onReceive(Just(viewModel.updatedAlias)) { updatedAlias in - if let updatedAlias = updatedAlias, updatedAlias.id == createdAlias?.id { + if let updatedAlias, updatedAlias.id == createdAlias?.id { createdAlias = updatedAlias } } @@ -192,18 +189,17 @@ struct AliasesView: View { } } .sheet(isPresented: $showingSearchView) { - SearchAliasesView( - session: viewModel.session, - onUpdateAlias: { updatedAlias in - viewModel.update(alias: updatedAlias) - }, - onDeleteAlias: { deletedAlias in - if deletedAlias.id == createdAlias?.id { - createdAlias = nil - } - viewModel.remove(alias: deletedAlias) - }, - onUpgrade: onUpgrade) + SearchAliasesView(session: viewModel.session, + onUpdateAlias: { updatedAlias in + viewModel.update(alias: updatedAlias) + }, + onDeleteAlias: { deletedAlias in + if deletedAlias.id == createdAlias?.id { + createdAlias = nil + } + viewModel.remove(alias: deletedAlias) + }, + onUpgrade: onUpgrade) } } @@ -214,7 +210,7 @@ struct AliasesView: View { showingUpdatingAlert = isUpdating } .alert(isPresented: $showingDeleteConfirmationAlert) { - guard let selectedAlias = selectedAlias else { + guard let selectedAlias else { return Alert(title: Text("selectedAlias is nil")) } @@ -233,43 +229,42 @@ struct AliasesView: View { @ViewBuilder private func aliasCompactView(for alias: Alias) -> some View { let hightlight = alias.id == createdAlias?.id - AliasCompactView( - alias: alias, - onCopy: { - Vibration.soft.vibrate() - copiedEmail = alias.email - UIPasteboard.general.string = alias.email - }, - onSendMail: { - Vibration.soft.vibrate() - selectedAlias = alias - selectedLink = .contacts - }, - onToggle: { - Vibration.soft.vibrate() - viewModel.toggle(alias: alias) - }, - onPin: { - viewModel.update(alias: alias, option: .pinned(true)) - }, - onUnpin: { - viewModel.update(alias: alias, option: .pinned(false)) - }, - onDelete: { - Vibration.warning.vibrate(fallBackToOldSchool: true) - selectedAlias = alias - showingDeleteConfirmationAlert = true - }) - .id(alias.id) - .background(hightlight ? Color.slPurple.opacity(0.1) : Color.clear) - .clipShape(RoundedRectangle(cornerRadius: 8)) - .onAppear { - viewModel.getMoreAliasesIfNeed(currentAlias: alias) - } - .onTapGesture { - selectedAlias = alias - selectedLink = .details - } + AliasCompactView(alias: alias, + onCopy: { + Vibration.soft.vibrate() + copiedEmail = alias.email + UIPasteboard.general.string = alias.email + }, + onSendMail: { + Vibration.soft.vibrate() + selectedAlias = alias + selectedLink = .contacts + }, + onToggle: { + Vibration.soft.vibrate() + viewModel.toggle(alias: alias) + }, + onPin: { + viewModel.update(alias: alias, option: .pinned(true)) + }, + onUnpin: { + viewModel.update(alias: alias, option: .pinned(false)) + }, + onDelete: { + Vibration.warning.vibrate(fallBackToOldSchool: true) + selectedAlias = alias + showingDeleteConfirmationAlert = true + }) + .id(alias.id) + .background(hightlight ? Color.slPurple.opacity(0.1) : Color.clear) + .clipShape(RoundedRectangle(cornerRadius: 8)) + .onAppear { + viewModel.getMoreAliasesIfNeed(currentAlias: alias) + } + .onTapGesture { + selectedAlias = alias + selectedLink = .details + } } } @@ -278,9 +273,9 @@ enum AliasStatus: CustomStringConvertible, CaseIterable { var description: String { switch self { - case .all: return "All" - case .active: return "Active" - case .inactive: return "Inactive" + case .all: "All" + case .active: "Active" + case .inactive: "Inactive" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesViewModel.swift index 371a64a..4edff1d 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/AliasesViewModel.swift @@ -35,11 +35,11 @@ final class AliasesViewModel: BaseReachabilitySessionViewModel, ObservableObject private var filterOption: AliasFilterOption? { switch selectedStatus { case .all: - return nil + nil case .active: - return .enabled + .enabled case .inactive: - return .disabled + .disabled } } @@ -48,7 +48,7 @@ final class AliasesViewModel: BaseReachabilitySessionViewModel, ObservableObject init(session: Session, reachabilityObserver: ReachabilityObserver, managedObjectContext: NSManagedObjectContext) { - self.dataController = .init(context: managedObjectContext) + dataController = .init(context: managedObjectContext) super.init(session: session, reachabilityObserver: reachabilityObserver) } @@ -69,7 +69,7 @@ final class AliasesViewModel: BaseReachabilitySessionViewModel, ObservableObject } func getMoreAliasesIfNeed(currentAlias alias: Alias?) { - guard let alias = alias else { + guard let alias else { getMoreAliases() return } @@ -206,7 +206,7 @@ final class AliasesViewModel: BaseReachabilitySessionViewModel, ObservableObject option: option) _ = try await session.execute(updateAliasEndpoint) guard let index = self.aliases.firstIndex(where: { $0.id == alias.id }) else { return } - if case .pinned(let pinned) = option { + if case let .pinned(pinned) = option { let updatedAlias = Alias(id: alias.id, email: alias.email, name: alias.name, diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesResultView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesResultView.swift index ef513da..f819541 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesResultView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesResultView.swift @@ -37,35 +37,34 @@ struct SearchAliasesResultView: View { .padding() } else { ForEach(viewModel.aliases, id: \.id) { alias in - AliasCompactView( - alias: alias, - onCopy: { - Vibration.soft.vibrate() - copiedEmail = alias.email - UIPasteboard.general.string = alias.email - }, - onSendMail: { - onSendMail(alias) - }, - onToggle: { - viewModel.toggle(alias: alias) - }, - onPin: { - viewModel.update(alias: alias, option: .pinned(true)) - }, - onUnpin: { - viewModel.update(alias: alias, option: .pinned(false)) - }, - onDelete: { - viewModel.delete(alias: alias) - }) - .padding(.horizontal, 4) - .onTapGesture { - onSelect(alias) - } - .onAppear { - viewModel.getMoreAliasesIfNeed(currentAlias: alias) - } + AliasCompactView(alias: alias, + onCopy: { + Vibration.soft.vibrate() + copiedEmail = alias.email + UIPasteboard.general.string = alias.email + }, + onSendMail: { + onSendMail(alias) + }, + onToggle: { + viewModel.toggle(alias: alias) + }, + onPin: { + viewModel.update(alias: alias, option: .pinned(true)) + }, + onUnpin: { + viewModel.update(alias: alias, option: .pinned(false)) + }, + onDelete: { + viewModel.delete(alias: alias) + }) + .padding(.horizontal, 4) + .onTapGesture { + onSelect(alias) + } + .onAppear { + viewModel.getMoreAliasesIfNeed(currentAlias: alias) + } } if viewModel.isLoading { @@ -77,21 +76,19 @@ struct SearchAliasesResultView: View { } .listStyle(.plain) .offlineLabelled(reachable: viewModel.reachabilityObserver.reachable) - .simultaneousGesture( - DragGesture().onChanged { _ in - UIApplication.shared.endEditing() - } - ) + .simultaneousGesture(DragGesture().onChanged { _ in + UIApplication.shared.endEditing() + }) .onReceive(Just(viewModel.isUpdating)) { isUpdating in showingUpdatingAlert = isUpdating } .onReceive(Just(viewModel.updatedAlias)) { updatedAlias in - if let updatedAlias = updatedAlias { + if let updatedAlias { onUpdate(updatedAlias) } } .onReceive(Just(viewModel.deletedAlias)) { deletedAlias in - if let deletedAlias = deletedAlias { + if let deletedAlias { onDelete(deletedAlias) } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesView.swift index 883f9ab..d920d4a 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesView.swift @@ -17,7 +17,7 @@ struct SearchAliasesView: UIViewControllerRepresentable { let onDeleteAlias: (Alias) -> Void let onUpgrade: () -> Void - func makeUIViewController(context: Context) -> UINavigationController { + func makeUIViewController(context _: Context) -> UINavigationController { let viewController = SearchAliasesViewController(session: session, reachabilityObserver: reachabilityObserver, managedObjectContext: managedObjectContext, @@ -29,6 +29,6 @@ struct SearchAliasesView: UIViewControllerRepresentable { return navigationController } - func updateUIViewController(_ uiViewController: UINavigationController, - context: Context) {} + func updateUIViewController(_: UINavigationController, + context _: Context) {} } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewController.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewController.swift index e84ca20..8a23088 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewController.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewController.swift @@ -22,9 +22,9 @@ final class SearchAliasesViewController: BaseViewController { onUpdateAlias: @escaping (Alias) -> Void, onDeleteAlias: @escaping (Alias) -> Void, onUpgrade: @escaping () -> Void) { - self.viewModel = .init(session: session, - reachabilityObserver: reachabilityObserver, - managedObjectContext: managedObjectContext) + viewModel = .init(session: session, + reachabilityObserver: reachabilityObserver, + managedObjectContext: managedObjectContext) self.onUpdateAlias = onUpdateAlias self.onDeleteAlias = onDeleteAlias self.onUpgrade = onUpgrade @@ -32,7 +32,7 @@ final class SearchAliasesViewController: BaseViewController { } @available(*, unavailable) - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -47,16 +47,15 @@ final class SearchAliasesViewController: BaseViewController { navigationItem.titleView = searchBar navigationItem.hidesSearchBarWhenScrolling = false - let searchAliasesResultView = SearchAliasesResultView( - viewModel: viewModel, - onSelect: { [weak self] alias in - self?.showAliasDetail(alias) - }, - onSendMail: { [weak self] alias in - self?.showAliasContacts(alias) - }, - onUpdate: onUpdateAlias, - onDelete: onDeleteAlias) + let searchAliasesResultView = SearchAliasesResultView(viewModel: viewModel, + onSelect: { [weak self] alias in + self?.showAliasDetail(alias) + }, + onSendMail: { [weak self] alias in + self?.showAliasContacts(alias) + }, + onUpdate: onUpdateAlias, + onDelete: onDeleteAlias) let hostingController = UIHostingController(rootView: searchAliasesResultView) addChild(hostingController) hostingController.view.translatesAutoresizingMaskIntoConstraints = false @@ -71,20 +70,19 @@ final class SearchAliasesViewController: BaseViewController { } private func showAliasDetail(_ alias: Alias) { - let aliasDetailView = AliasDetailView( - alias: alias, - session: viewModel.session, - onUpdateAlias: { [weak self] updatedAlias in - guard let self = self else { return } - self.onUpdateAlias(updatedAlias) - self.viewModel.update(alias: updatedAlias) - }, - onDeleteAlias: { [weak self] deletedAlias in - guard let self = self else { return } - self.onDeleteAlias(deletedAlias) - self.viewModel.remove(alias: alias) - }, - onUpgrade: onUpgrade) + let aliasDetailView = AliasDetailView(alias: alias, + session: viewModel.session, + onUpdateAlias: { [weak self] updatedAlias in + guard let self else { return } + onUpdateAlias(updatedAlias) + viewModel.update(alias: updatedAlias) + }, + onDeleteAlias: { [weak self] deletedAlias in + guard let self else { return } + onDeleteAlias(deletedAlias) + viewModel.remove(alias: alias) + }, + onUpgrade: onUpgrade) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(action: dismissPresentedViewController) { @@ -98,10 +96,9 @@ final class SearchAliasesViewController: BaseViewController { } private func showAliasContacts(_ alias: Alias) { - let aliasContactsView = AliasContactsView( - alias: alias, - session: viewModel.session, - onUpgrade: onUpgrade) + let aliasContactsView = AliasContactsView(alias: alias, + session: viewModel.session, + onUpgrade: onUpgrade) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(action: dismissPresentedViewController) { @@ -120,12 +117,13 @@ final class SearchAliasesViewController: BaseViewController { } // MARK: - UISearchBarDelegate + extension SearchAliasesViewController: UISearchBarDelegate { - func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + func searchBar(_: UISearchBar, textDidChange searchText: String) { viewModel.search(term: searchText) } - func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + func searchBarCancelButtonClicked(_: UISearchBar) { dismiss(animated: true, completion: nil) } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewModel.swift index 3cf42d9..bce4ea0 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/Search/SearchAliasesViewModel.swift @@ -30,7 +30,7 @@ final class SearchAliasesViewModel: BaseReachabilitySessionViewModel, Observable init(session: Session, reachabilityObserver: ReachabilityObserver, managedObjectContext: NSManagedObjectContext) { - self.dataController = .init(context: managedObjectContext) + dataController = .init(context: managedObjectContext) super.init(session: session, reachabilityObserver: reachabilityObserver) searchTermSubject .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main) @@ -40,7 +40,7 @@ final class SearchAliasesViewModel: BaseReachabilitySessionViewModel, Observable lastSearchTerm = nil return } - self.initialSearch(term: term) + initialSearch(term: term) } .store(in: &cancellables) } @@ -61,13 +61,13 @@ final class SearchAliasesViewModel: BaseReachabilitySessionViewModel, Observable let thresholdIndex = aliases.index(aliases.endIndex, offsetBy: -1) guard aliases.firstIndex(where: { $0.id == alias.id }) == thresholdIndex else { return } - guard let lastSearchTerm = lastSearchTerm, canLoadMorePages else { return } + guard let lastSearchTerm, canLoadMorePages else { return } if !reachabilityObserver.reachable { do { let fetchedAliases = try dataController.fetchAliases(page: currentPage, searchTerm: lastSearchTerm) - self.aliases.append(contentsOf: fetchedAliases) + aliases.append(contentsOf: fetchedAliases) currentPage += 1 canLoadMorePages = fetchedAliases.count == kDefaultPageSize } catch { @@ -180,7 +180,7 @@ final class SearchAliasesViewModel: BaseReachabilitySessionViewModel, Observable option: option) _ = try await session.execute(updateAliasEndpoint) guard let index = self.aliases.firstIndex(where: { $0.id == alias.id }) else { return } - if case .pinned(let pinned) = option { + if case let .pinned(pinned) = option { let updatedAlias = Alias(id: alias.id, email: alias.email, name: alias.name, diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/UpgradeNeededView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/UpgradeNeededView.swift index 950d1c1..db93ab0 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Aliases/UpgradeNeededView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Aliases/UpgradeNeededView.swift @@ -51,7 +51,7 @@ struct UpgradeNeededView: View { } .onAppear { UIView.appearance(whenContainedInInstancesOf: - [UIAlertController.self]).tintColor = .slPurple + [UIAlertController.self]).tintColor = .slPurple } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasView.swift index 509b503..91f3e12 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasView.swift @@ -68,7 +68,7 @@ struct CreateAliasView: View { showingLoadingAlert = isLoading } .onReceive(Just(viewModel.createdAlias)) { createdAlias in - if let createdAlias = createdAlias { + if let createdAlias { // Workaround of a strange bug: https://developer.apple.com/forums/thread/675216 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { onCreateAlias(createdAlias) @@ -174,9 +174,9 @@ private struct ContentView: View { private var buttons: some View { VStack { PrimaryButton(title: "Create", action: viewModel.createAlias) - .padding(.vertical) - .opacity(viewModel.canCreate ? 1 : 0.5) - .disabled(!viewModel.canCreate) + .padding(.vertical) + .opacity(viewModel.canCreate ? 1 : 0.5) + .disabled(!viewModel.canCreate) GeometryReader { geometry in HStack { @@ -218,7 +218,7 @@ private struct ContentView: View { EditMailboxesView(mailboxIds: $viewModel.mailboxIds, mailboxes: viewModel.mailboxes) }, label: { let selectedMailboxes = viewModel.mailboxes.filter { viewModel.mailboxIds.contains($0.id) } - Text(selectedMailboxes.map { $0.email }.joined(separator: "\n")) + Text(selectedMailboxes.map(\.email).joined(separator: "\n")) }) }, header: { Text("Mailboxes") @@ -268,7 +268,7 @@ private struct EditMailboxesView: View { .contentShape(Rectangle()) .onTapGesture { guard mailbox.verified else { return } - if mailboxIds.contains(mailbox.id) && mailboxIds.count > 1 { + if mailboxIds.contains(mailbox.id), mailboxIds.count > 1 { mailboxIds.removeAll { $0 == mailbox.id } } else if !mailboxIds.contains(mailbox.id) { mailboxIds.append(mailbox.id) @@ -289,7 +289,7 @@ private struct EditMailboxesView: View { }, label: { Image(systemName: "gobackward") }) - .padding() + .padding() } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasViewModel.swift index fe7d6cf..60dc35a 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Create/CreateAliasViewModel.swift @@ -31,10 +31,10 @@ final class CreateAliasViewModel: ObservableObject { self.session = session self.mode = mode switch mode { - case .url(let url): + case let .url(url): prefix = url.notWwwHostname() ?? "" notes = url.host ?? "" - case .text(let text): + case let .text(text): notes = text case .none: break diff --git a/SimpleLogin/SimpleLogin/Modules/Main/MainTabBar.swift b/SimpleLogin/SimpleLogin/Modules/Main/MainTabBar.swift index e5046d5..679b383 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/MainTabBar.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/MainTabBar.swift @@ -29,7 +29,6 @@ struct MainTabBar: View { } } - @ViewBuilder private func tab(for item: TabBarItem) -> some View { Tab { Image(systemName: selectedItem == item ? item.selectedImage : item.image) @@ -83,39 +82,39 @@ enum TabBarItem { var title: String { switch self { case .aliases: - return "Aliases" + "Aliases" case .advanced: - return "Advanced" + "Advanced" case .myAccount: - return "My account" + "My account" case .settings: - return "Settings" + "Settings" } } var image: String { switch self { case .aliases: - return "at" + "at" case .advanced: - return "circle.grid.cross" + "circle.grid.cross" case .myAccount: - return "person" + "person" case .settings: - return "gear.circle" + "gear.circle" } } var selectedImage: String { switch self { case .aliases: - return "at" + "at" case .advanced: - return "circle.grid.cross.fill" + "circle.grid.cross.fill" case .myAccount: - return "person.fill" + "person.fill" case .settings: - return "gear.circle.fill" + "gear.circle.fill" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/MainView.swift b/SimpleLogin/SimpleLogin/Modules/Main/MainView.swift index d3aa7ce..0e65c91 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/MainView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/MainView.swift @@ -51,9 +51,9 @@ struct MainView: View { AliasesView(session: session, reachabilityObserver: reachabilityObserver, managedObjectContext: managedObjectContext, - createdAlias: $createdAlias, + createdAlias: $createdAlias, onUpgrade: beginUpgradeFlow) - .tag(TabBarItem.aliases) + .tag(TabBarItem.aliases) AdvancedView() .tag(TabBarItem.advanced) @@ -61,7 +61,7 @@ struct MainView: View { AccountView(session: session, upgradeNeeded: $upgradeNeeded, onLogOut: onLogOut) - .tag(TabBarItem.myAccount) + .tag(TabBarItem.myAccount) SettingsView() .tag(TabBarItem.settings) @@ -113,27 +113,29 @@ struct MainView: View { didShowTips = true } case .createAlias: - CreateAliasView( - session: session, - mode: nil, - onCreateAlias: { createdAlias in - aliasCreationCount += 1 - if launchCount >= 10, aliasCreationCount >= 5 { - if let scene = UIApplication.shared - .connectedScenes - .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { - if !ProcessInfo.processInfo.isiOSAppOnMac { - // Only ask for reviews when not in macOS because macOS doesn't respect - // the 3 time per year limit so users are prompted after every alias creations - SKStoreReviewController.requestReview(in: scene) - } - } - } - self.createdAlias = createdAlias - self.selectedItem = .aliases - }, - onCancel: nil, - onOpenMyAccount: beginUpgradeFlow) + CreateAliasView(session: session, + mode: nil, + onCreateAlias: { createdAlias in + aliasCreationCount += 1 + if launchCount >= 10, aliasCreationCount >= 5 { + if let scene = UIApplication.shared + .connectedScenes + .first(where: { $0.activationState == .foregroundActive + }) as? UIWindowScene { + if !ProcessInfo.processInfo.isiOSAppOnMac { + // Only ask for reviews when not in macOS because macOS doesn't + // respect + // the 3 time per year limit so users are prompted after every + // alias creations + SKStoreReviewController.requestReview(in: scene) + } + } + } + self.createdAlias = createdAlias + selectedItem = .aliases + }, + onCancel: nil, + onOpenMyAccount: beginUpgradeFlow) case .none: EmptyView() } @@ -164,7 +166,7 @@ final class MainViewModel: ObservableObject { } func handledBiometricAuthFailure() { - self.biometricAuthFailed = false + biometricAuthFailed = false } func biometricallyAuthenticate() { @@ -172,7 +174,7 @@ final class MainViewModel: ObservableObject { context.localizedFallbackTitle = "Or use your passcode" context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Please authenticate") { [weak self] success, _ in - guard let self = self else { return } + guard let self else { return } DispatchQueue.main.async { if success { self.canShowDetails = true diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsView.swift b/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsView.swift index 5979759..aa186ff 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsView.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsView.swift @@ -120,7 +120,7 @@ private struct AliasDisplayModeSection: View { .tag(mode) } } - .pickerStyle(SegmentedPickerStyle()) + .pickerStyle(SegmentedPickerStyle()) if showingSampleData { AliasCompactView(alias: Alias.sample, @@ -156,10 +156,10 @@ private struct KeyboardExtensionSection: View { .tag(mode) } } - .pickerStyle(SegmentedPickerStyle()) - .sheet(isPresented: $showingExplanation) { - KeyboardFullAccessExplanationView() - } + .pickerStyle(SegmentedPickerStyle()) + .sheet(isPresented: $showingExplanation) { + KeyboardFullAccessExplanationView() + } } } @@ -201,14 +201,14 @@ private struct KeyboardFullAccessExplanationView: View { VStack { // swiftlint:disable line_length Text(""" - Most of the functionalities of this application are based on making requests to our server. Every request is attached with an API key in order for our server to authenticate you. + Most of the functionalities of this application are based on making requests to our server. Every request is attached with an API key in order for our server to authenticate you. - When you successfully log in, our server sends a valid API key to the application. The application then saves this API key to a Keychain Group in order to reuse it without asking you to authenticate again. + When you successfully log in, our server sends a valid API key to the application. The application then saves this API key to a Keychain Group in order to reuse it without asking you to authenticate again. - The keyboard extension needs to use the API key saved in Keychain Group by the host application to make requests by itself (get the list of aliases and create new aliases). Such access to Keychain Group and requests require full access. The keyboard extension does not record nor share anything you type. + The keyboard extension needs to use the API key saved in Keychain Group by the host application to make requests by itself (get the list of aliases and create new aliases). Such access to Keychain Group and requests require full access. The keyboard extension does not record nor share anything you type. - More technical information [here]( https://developer.apple.com/documentation/uikit/keyboards_and_input/creating_a_custom_keyboard/configuring_open_access_for_a_custom_keyboard). - """) + More technical information [here]( https://developer.apple.com/documentation/uikit/keyboards_and_input/creating_a_custom_keyboard/configuring_open_access_for_a_custom_keyboard). + """) // swiftlint:enable line_length HStack { Text("Need more information?") @@ -219,7 +219,7 @@ private struct KeyboardFullAccessExplanationView: View { } .padding(.top) } - .padding() + .padding() } .navigationTitle("Why full access?") .navigationBarItems(leading: closeButton) @@ -271,14 +271,13 @@ private struct AboutSection: View { var body: some View { Section { - NavigationLink( - isActive: $showingAboutView, - destination: { - AboutView() - }, - label: { - Label("About SimpleLogin", systemImage: "info.circle") - }) + NavigationLink(isActive: $showingAboutView, + destination: { + AboutView() + }, + label: { + Label("About SimpleLogin", systemImage: "info.circle") + }) } } } @@ -289,9 +288,9 @@ enum AliasDisplayMode: Int, CaseIterable { var description: String { switch self { - case .default: return "Default" - case .comfortable: return "Comfortable" - case .compact: return "Compact" + case .default: "Default" + case .comfortable: "Comfortable" + case .compact: "Compact" } } } diff --git a/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsViewModel.swift b/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsViewModel.swift index 3747dbb..47f8df5 100644 --- a/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsViewModel.swift +++ b/SimpleLogin/SimpleLogin/Modules/Main/Settings/SettingsViewModel.swift @@ -8,5 +8,4 @@ import Combine import Foundation -final class SettingsViewModel: ObservableObject { -} +final class SettingsViewModel: ObservableObject {} diff --git a/SimpleLogin/SimpleLogin/SimpleLoginApp.swift b/SimpleLogin/SimpleLogin/SimpleLoginApp.swift index 2057235..8622ef6 100644 --- a/SimpleLogin/SimpleLogin/SimpleLoginApp.swift +++ b/SimpleLogin/SimpleLogin/SimpleLoginApp.swift @@ -25,7 +25,7 @@ struct SimpleLoginApp: App { private let persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "SimpleLogin") container.loadPersistentStores { _, error in - if let error = error { + if let error { print("Unable to load persistent stores: \(error)") } } @@ -34,17 +34,17 @@ struct SimpleLoginApp: App { var body: some Scene { WindowGroup { - if let apiKey = apiKey, let apiService = apiService { + if let apiKey, let apiService { MainView { try? KeychainService.shared.setApiKey(nil) try? DataController(context: persistentContainer.viewContext).reset() self.apiKey = nil self.apiService = nil - self.biometricAuthEnabled = false - self.ultraProtectionEnabled = false - self.forceDarkMode = false - self.displayMode = .default - self.didShowTips = false + biometricAuthEnabled = false + ultraProtectionEnabled = false + forceDarkMode = false + displayMode = .default + didShowTips = false if let cookies = HTTPCookieStorage.shared.cookies { for cookie in cookies { HTTPCookieStorage.shared.deleteCookie(cookie) diff --git a/SimpleLogin/SimpleLogin/Supporting Views/BetterSafariViewModifier.swift b/SimpleLogin/SimpleLogin/Supporting Views/BetterSafariViewModifier.swift index ac683db..6934b13 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/BetterSafariViewModifier.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/BetterSafariViewModifier.swift @@ -13,7 +13,7 @@ struct BetterSafariViewModifier: ViewModifier { func body(content: Content) -> some View { let showingSafariView = Binding(get: { - if let urlString = urlString, URL(string: urlString) != nil { + if let urlString, URL(string: urlString) != nil { return true } return false diff --git a/SimpleLogin/SimpleLogin/Supporting Views/Blur.swift b/SimpleLogin/SimpleLogin/Supporting Views/Blur.swift index be710e4..e160b4e 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/Blur.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/Blur.swift @@ -10,11 +10,11 @@ import SwiftUI struct Blur: UIViewRepresentable { var style: UIBlurEffect.Style = .systemMaterial - func makeUIView(context: Context) -> UIVisualEffectView { + func makeUIView(context _: Context) -> UIVisualEffectView { .init(effect: UIBlurEffect(style: style)) } - func updateUIView(_ uiView: UIVisualEffectView, context: Context) { + func updateUIView(_ uiView: UIVisualEffectView, context _: Context) { uiView.effect = UIBlurEffect(style: style) } } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/BorderedText.swift b/SimpleLogin/SimpleLogin/Supporting Views/BorderedText.swift index 126d0ba..dc840da 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/BorderedText.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/BorderedText.swift @@ -1,5 +1,5 @@ // -// UnverifiedLabel.swift +// BorderedText.swift // SimpleLogin // // Created by Nhon Nguyen on 24/04/2022. @@ -19,7 +19,7 @@ struct BorderedText: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background(RoundedRectangle(cornerRadius: 4) - .stroke(Color.red, lineWidth: 1)) + .stroke(Color.red, lineWidth: 1)) } } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/DetailPlaceholderView.swift b/SimpleLogin/SimpleLogin/Supporting Views/DetailPlaceholderView.swift index de894ff..290eeeb 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/DetailPlaceholderView.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/DetailPlaceholderView.swift @@ -25,7 +25,7 @@ struct DetailPlaceholderView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } - if let message = message { + if let message { Text(message) .font(.title) .foregroundColor(.secondary) diff --git a/SimpleLogin/SimpleLogin/Supporting Views/EmptyDataModifier.swift b/SimpleLogin/SimpleLogin/Supporting Views/EmptyDataModifier.swift index dbd1d92..0eeb0d8 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/EmptyDataModifier.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/EmptyDataModifier.swift @@ -31,10 +31,9 @@ struct EmptyDataModifier: ViewModifier { } extension View { - @ViewBuilder - func emptyPlaceholder(isEmpty: Bool, - useZStack: Bool = false, - placeholder: @escaping () -> PlaceholderView) -> some View { + func emptyPlaceholder(isEmpty: Bool, + useZStack: Bool = false, + placeholder: @escaping () -> some View) -> some View { modifier(EmptyDataModifier(isEmpty: isEmpty, useZStack: useZStack, placeholder: AnyView(placeholder()))) diff --git a/SimpleLogin/SimpleLogin/Supporting Views/PhotoPickerView.swift b/SimpleLogin/SimpleLogin/Supporting Views/PhotoPickerView.swift index 44f50ef..85af1e9 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/PhotoPickerView.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/PhotoPickerView.swift @@ -9,8 +9,8 @@ import SwiftUI struct PhotoPickerView: UIViewControllerRepresentable { @Environment(\.presentationMode) private var presentationMode -// @Binding var image: UIImage? - var onPickImage: ((UIImage) -> Void) + /// @Binding var image: UIImage? + var onPickImage: (UIImage) -> Void func makeUIViewController(context: Context) -> UIImagePickerController { let picker = UIImagePickerController() @@ -23,7 +23,7 @@ struct PhotoPickerView: UIViewControllerRepresentable { Coordinator(parent: self) } - func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {} + func updateUIViewController(_: UIImagePickerController, context _: Context) {} } extension PhotoPickerView { @@ -34,7 +34,7 @@ extension PhotoPickerView { self.parent = parent } - func imagePickerController(_ picker: UIImagePickerController, + func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { if let image = info[.originalImage] as? UIImage { parent.onPickImage(image) @@ -43,7 +43,7 @@ extension PhotoPickerView { parent.presentationMode.wrappedValue.dismiss() } - func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + func imagePickerControllerDidCancel(_: UIImagePickerController) { parent.presentationMode.wrappedValue.dismiss() } } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/SecondaryButton.swift b/SimpleLogin/SimpleLogin/Supporting Views/SecondaryButton.swift index c0f8dbc..88fee52 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/SecondaryButton.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/SecondaryButton.swift @@ -20,8 +20,7 @@ struct SecondaryButton: View { .frame(maxWidth: .infinity) .padding(.vertical) .overlay(RoundedRectangle(cornerRadius: 8) - .stroke(Color.slPurple, lineWidth: 2) - ) + .stroke(Color.slPurple, lineWidth: 2)) } } } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/SensitiveContentViewModifier.swift b/SimpleLogin/SimpleLogin/Supporting Views/SensitiveContentViewModifier.swift index 163f943..57c4f2f 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/SensitiveContentViewModifier.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/SensitiveContentViewModifier.swift @@ -26,7 +26,7 @@ struct SensitiveContentViewModifier: ViewModifier { } extension View { - func sensitiveContent(placeholderContent: @escaping () -> V) -> some View { + func sensitiveContent(placeholderContent: @escaping () -> some View) -> some View { modifier(SensitiveContentViewModifier(placeholderContent: placeholderContent)) } } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/ShakeEffect.swift b/SimpleLogin/SimpleLogin/Supporting Views/ShakeEffect.swift index 952bebc..9d8e432 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/ShakeEffect.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/ShakeEffect.swift @@ -7,13 +7,13 @@ import SwiftUI -// https://www.objc.io/blog/2019/10/01/swiftui-shake-animation/ +/// https://www.objc.io/blog/2019/10/01/swiftui-shake-animation/ struct ShakeEffect: GeometryEffect { var amount: CGFloat = 10 var shakesPerUnit = 3 var animatableData: CGFloat - func effectValue(size: CGSize) -> ProjectionTransform { + func effectValue(size _: CGSize) -> ProjectionTransform { let translationX = amount * sin(animatableData * .pi * CGFloat(shakesPerUnit)) return .init(CGAffineTransform(translationX: translationX, y: 0)) } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/ShareSheetView.swift b/SimpleLogin/SimpleLogin/Supporting Views/ShareSheetView.swift index 4b74a7d..e7f3ae5 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/ShareSheetView.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/ShareSheetView.swift @@ -12,12 +12,12 @@ struct ShareSheetView: UIViewControllerRepresentable { let applicationActivities: [UIActivity]? = nil let excludedActivityTypes: [UIActivity.ActivityType]? = nil - func makeUIViewController(context: Context) -> UIActivityViewController { + func makeUIViewController(context _: Context) -> UIActivityViewController { let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities) controller.excludedActivityTypes = excludedActivityTypes return controller } - func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} + func updateUIViewController(_: UIActivityViewController, context _: Context) {} } diff --git a/SimpleLogin/SimpleLogin/Supporting Views/TextFieldAlert.swift b/SimpleLogin/SimpleLogin/Supporting Views/TextFieldAlert.swift index 712f270..287ea99 100644 --- a/SimpleLogin/SimpleLogin/Supporting Views/TextFieldAlert.swift +++ b/SimpleLogin/SimpleLogin/Supporting Views/TextFieldAlert.swift @@ -33,7 +33,7 @@ struct TextFieldAlertModifier: ViewModifier { return } scene.windows.first?.rootViewController?.present(alertController, animated: true) - } else if !isPresented, let alertController = alertController { + } else if !isPresented, let alertController { alertController.dismiss(animated: true) self.alertController = nil } diff --git a/SimpleLogin/SimpleLogin/Utils/BaseReachabilitySessionViewModel.swift b/SimpleLogin/SimpleLogin/Utils/BaseReachabilitySessionViewModel.swift index 04e00d3..00ae222 100644 --- a/SimpleLogin/SimpleLogin/Utils/BaseReachabilitySessionViewModel.swift +++ b/SimpleLogin/SimpleLogin/Utils/BaseReachabilitySessionViewModel.swift @@ -19,11 +19,11 @@ class BaseReachabilitySessionViewModel { self.reachabilityObserver.$reachable .receive(on: DispatchQueue.main) .sink { [weak self] reachable in - guard let self = self else { return } + guard let self else { return } if reachable { - self.whenReachable() + whenReachable() } else { - self.whenUnreachable() + whenUnreachable() } } .store(in: &cancellables) diff --git a/SimpleLogin/SimpleLogin/Utils/BetterSafariViewExtensions.swift b/SimpleLogin/SimpleLogin/Utils/BetterSafariViewExtensions.swift index 126a654..07f226e 100644 --- a/SimpleLogin/SimpleLogin/Utils/BetterSafariViewExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/BetterSafariViewExtensions.swift @@ -11,8 +11,8 @@ import Foundation extension SafariView { init(url: URL) { let safariView = - SafariView(url: url, - configuration: .init(entersReaderIfAvailable: true, barCollapsingEnabled: true)) + SafariView(url: url, + configuration: .init(entersReaderIfAvailable: true, barCollapsingEnabled: true)) .accentColor(.slPurple) .dismissButtonStyle(.done) self = safariView diff --git a/SimpleLogin/SimpleLogin/Utils/CollectionExtensions.swift b/SimpleLogin/SimpleLogin/Utils/CollectionExtensions.swift index e615a32..1ea97a5 100644 --- a/SimpleLogin/SimpleLogin/Utils/CollectionExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/CollectionExtensions.swift @@ -8,7 +8,7 @@ import Foundation extension Collection { - subscript (safe index: Index) -> Element? { + subscript(safe index: Index) -> Element? { indices.contains(index) ? self[index] : nil } } diff --git a/SimpleLogin/SimpleLogin/Utils/ErrorExtensions.swift b/SimpleLogin/SimpleLogin/Utils/ErrorExtensions.swift index 275232f..32563a9 100644 --- a/SimpleLogin/SimpleLogin/Utils/ErrorExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/ErrorExtensions.swift @@ -11,11 +11,11 @@ extension Error { var safeLocalizedDescription: String { switch self { case let apiServiceError as APIServiceError: - return apiServiceError.description + apiServiceError.description case let slError as SLError: - return slError.localizedDescription + slError.localizedDescription default: - return localizedDescription + localizedDescription } } } diff --git a/SimpleLogin/SimpleLogin/Utils/KeychainService.swift b/SimpleLogin/SimpleLogin/Utils/KeychainService.swift index d3025c6..2da337a 100644 --- a/SimpleLogin/SimpleLogin/Utils/KeychainService.swift +++ b/SimpleLogin/SimpleLogin/Utils/KeychainService.swift @@ -18,7 +18,7 @@ struct KeychainService { static let shared = KeychainService() func setApiKey(_ apiKey: ApiKey?) throws { - if let apiKey = apiKey { + if let apiKey { try keychain.set(apiKey.value, key: kApiKey) } else { try keychain.remove(kApiKey) diff --git a/SimpleLogin/SimpleLogin/Utils/LocalAuthenticator.swift b/SimpleLogin/SimpleLogin/Utils/LocalAuthenticator.swift index f8a0b7c..b6bb6b2 100644 --- a/SimpleLogin/SimpleLogin/Utils/LocalAuthenticator.swift +++ b/SimpleLogin/SimpleLogin/Utils/LocalAuthenticator.swift @@ -16,6 +16,7 @@ final class LocalAuthenticator: ObservableObject { biometricallyAuthenticate() } } + @Published var message: String? @Published var error: Error? @@ -32,19 +33,19 @@ final class LocalAuthenticator: ObservableObject { let context = LAContext() context.localizedFallbackTitle = "Or use your passcode" let reason = biometricAuthEnabled ? - "Please authenticate to activate \(biometryType.description)" : - "Please authenticate to deactivate \(biometryType.description)" + "Please authenticate to activate \(biometryType.description)" : + "Please authenticate to deactivate \(biometryType.description)" context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { [weak self] success, error in - guard let self = self else { return } + guard let self else { return } DispatchQueue.main.async { defer { self.isBiometricallyAuthenticating = false } if success { self.message = self.biometricAuthEnabled ? - "\(self.biometryType.description) activated" : - "\(self.biometryType.description) deactivated" + "\(self.biometryType.description) activated" : + "\(self.biometryType.description) deactivated" return } @@ -67,9 +68,9 @@ extension LABiometryType: CustomStringConvertible { var systemImageName: String { switch self { - case .touchID: return "touchid" - case .faceID: return "faceid" - default: return "" + case .touchID: "touchid" + case .faceID: "faceid" + default: "" } } } diff --git a/SimpleLogin/SimpleLogin/Utils/ProtonButtonStyle.swift b/SimpleLogin/SimpleLogin/Utils/ProtonButtonStyle.swift index 5376cef..f45db6b 100644 --- a/SimpleLogin/SimpleLogin/Utils/ProtonButtonStyle.swift +++ b/SimpleLogin/SimpleLogin/Utils/ProtonButtonStyle.swift @@ -15,5 +15,7 @@ struct ProtonButtonStyle: ButtonStyle { } extension ButtonStyle where Self == ProtonButtonStyle { - static var proton: ProtonButtonStyle { .init() } + static var proton: ProtonButtonStyle { + .init() + } } diff --git a/SimpleLogin/SimpleLogin/Utils/ReachabilityObserver.swift b/SimpleLogin/SimpleLogin/Utils/ReachabilityObserver.swift index 95f3179..c1a7475 100644 --- a/SimpleLogin/SimpleLogin/Utils/ReachabilityObserver.swift +++ b/SimpleLogin/SimpleLogin/Utils/ReachabilityObserver.swift @@ -15,11 +15,11 @@ final class ReachabilityObserver: ObservableObject { init() { reachability?.whenReachable = { [unowned self] _ in - self.reachable = true + reachable = true } reachability?.whenUnreachable = { [unowned self] _ in - self.reachable = false + reachable = false } try? reachability?.startNotifier() diff --git a/SimpleLogin/SimpleLogin/Utils/SizePreferenceKey.swift b/SimpleLogin/SimpleLogin/Utils/SizePreferenceKey.swift index 05fb8d2..7b180c4 100644 --- a/SimpleLogin/SimpleLogin/Utils/SizePreferenceKey.swift +++ b/SimpleLogin/SimpleLogin/Utils/SizePreferenceKey.swift @@ -22,14 +22,12 @@ struct ChildSizeReader: View { var body: some View { content() - .background( - GeometryReader { proxy in - Color.clear - .preference(key: SizePreferenceKey.self, value: proxy.size) - } - ) + .background(GeometryReader { proxy in + Color.clear + .preference(key: SizePreferenceKey.self, value: proxy.size) + }) .onPreferenceChange(SizePreferenceKey.self) { preferences in - self.size = preferences + size = preferences } } } diff --git a/SimpleLogin/SimpleLogin/Utils/StringExtensions.swift b/SimpleLogin/SimpleLogin/Utils/StringExtensions.swift index cde8f74..39a8a5e 100644 --- a/SimpleLogin/SimpleLogin/Utils/StringExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/StringExtensions.swift @@ -14,22 +14,22 @@ extension String { } var isValidPrefix: Bool { - guard 1...100 ~= self.count else { return false } + guard 1...100 ~= count else { return false } return NSPredicate(format: "SELF MATCHES %@", "[0-9a-z-_.]+") .evaluate(with: self) } - subscript (idx: Int) -> String { + subscript(idx: Int) -> String { String(self[index(startIndex, offsetBy: idx)]) } - // https://www.hackingwithswift.com/example-code/strings/how-to-detect-a-url-in-a-string-using-nsdatadetector + /// https://www.hackingwithswift.com/example-code/strings/how-to-detect-a-url-in-a-string-using-nsdatadetector func firstUrl() -> URL? { guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { return nil } - let matches = detector.matches(in: self, range: .init(location: 0, length: self.utf16.count)) + let matches = detector.matches(in: self, range: .init(location: 0, length: utf16.count)) for match in matches { guard let range = Range(match.range, in: self), let url = URL(string: String(self[range]).lowercased()) else { continue } diff --git a/SimpleLogin/SimpleLogin/Utils/SuffixExtensions.swift b/SimpleLogin/SimpleLogin/Utils/SuffixExtensions.swift index 3ce5f05..fe0007a 100644 --- a/SimpleLogin/SimpleLogin/Utils/SuffixExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/SuffixExtensions.swift @@ -15,13 +15,13 @@ extension Suffix { var localizedDescription: String { switch self { case .custom: - return "Your domain" + "Your domain" case .public: - return "Public domain" + "Public domain" case .premium: - return "Premium domain" + "Premium domain" case .simpleLogin: - return "SimpleLogin domain" + "SimpleLogin domain" } } } @@ -37,13 +37,13 @@ extension Suffix.DomainType { var color: Color { switch self { case .custom: - return .blue + .blue case .public: - return .secondary + .secondary case .premium: - return .slPurple + .slPurple case .simpleLogin: - return .secondary + .secondary } } } diff --git a/SimpleLogin/SimpleLogin/Utils/UIViewControllerExtensions.swift b/SimpleLogin/SimpleLogin/Utils/UIViewControllerExtensions.swift index 41f0e7b..9e967bb 100644 --- a/SimpleLogin/SimpleLogin/Utils/UIViewControllerExtensions.swift +++ b/SimpleLogin/SimpleLogin/Utils/UIViewControllerExtensions.swift @@ -1,5 +1,5 @@ // -// UIAlertControllerExtensions.swift +// UIViewControllerExtensions.swift // SimpleLogin // // Created by Nhon Nguyen on 14/05/2022. diff --git a/SimpleLogin/SimpleLogin/Utils/Vibration.swift b/SimpleLogin/SimpleLogin/Utils/Vibration.swift index 8ee1d1f..6f2f5b7 100644 --- a/SimpleLogin/SimpleLogin/Utils/Vibration.swift +++ b/SimpleLogin/SimpleLogin/Utils/Vibration.swift @@ -26,7 +26,7 @@ enum Vibration { func vibrate(fallBackToOldSchool: Bool = false) { guard Self.hapticFeedbackEnabled else { return } - if fallBackToOldSchool && !CHHapticEngine.capabilitiesForHardware().supportsHaptics { + if fallBackToOldSchool, !CHHapticEngine.capabilitiesForHardware().supportsHaptics { AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) return } diff --git a/SimpleLogin/SimpleLoginTests/StringExtensionsTests.swift b/SimpleLogin/SimpleLoginTests/StringExtensionsTests.swift index 0a9df4e..e328c30 100644 --- a/SimpleLogin/SimpleLoginTests/StringExtensionsTests.swift +++ b/SimpleLogin/SimpleLoginTests/StringExtensionsTests.swift @@ -9,7 +9,7 @@ import XCTest final class StringExtensionsTests: XCTestCase { - func testExtractFirstUrl() throws { + func testExtractFirstUrl() { let string = "This string contains no url" XCTAssertNil(string.firstUrl())