Skip to content

Commit

Permalink
[Improvement] Minor Updates For Usability
Browse files Browse the repository at this point in the history
  • Loading branch information
sakkaras committed Apr 1, 2021
1 parent 7ff0cb0 commit 22fcd32
Show file tree
Hide file tree
Showing 30 changed files with 707 additions and 295 deletions.
4 changes: 4 additions & 0 deletions Macaroon.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ Pod::Spec.new do |s|
sssss.source_files = 'macaroon/Classes/Screens/CustomTransition/Modal/*.swift'
end
end

sss.subspec 'Other' do |ssss|
ssss.source_files = 'macaroon/Classes/Screens/Other/*.swift'
end
end

ss.subspec 'Utils' do |sss|
Expand Down
4 changes: 3 additions & 1 deletion macaroon/Classes/App/Dependencies/AppDependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

import Foundation

public protocol AppDependencies: AnyObject { }
public protocol AppDependencies: AnyObject {
func invalidate()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public protocol AppDependenciesResolver: AnyObject {
var appDependencies: AppDependencies! { get set }

static var shared: Self { get }

func invalidate()
}

extension AppDependenciesResolver {
Expand Down
6 changes: 3 additions & 3 deletions macaroon/Classes/App/Launching/AppLaunchController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extension AppLaunchController {
public protocol AppAuthLaunchController: AppLaunchController {
typealias AuthCompletionHandler = (Swift.Error?) -> Void

func signIn(onCompleted execute: AuthCompletionHandler?)
func signUp(onCompleted execute: AuthCompletionHandler?)
func signOut(onCompleted execute: AuthCompletionHandler?)
func launchAfterSignIn(onCompleted execute: AuthCompletionHandler?)
func launchAfterSignUp(onCompleted execute: AuthCompletionHandler?)
func launchAfterSignOut(onCompleted execute: AuthCompletionHandler?)
}
23 changes: 23 additions & 0 deletions macaroon/Classes/App/Routing/Flow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,26 @@ extension Flow {
return lhs.identifier == rhs.identifier
}
}

extension Flow where Self: RawRepresentable, Self.RawValue == String {
public var identifier: String {
return "flow.\(rawValue)"
}

public static func instance(
_ identifier: String
) -> Self {
let rawValue =
identifier.without(
prefix: "flow."
)

if let flow = Self(rawValue: rawValue) {
return flow
}

mc_crash(
.flowNotFound
)
}
}
120 changes: 82 additions & 38 deletions macaroon/Classes/App/Routing/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ extension Router {
animated: animated
) { [unowned self] in

if nextSubroute.flow == .current {
if nextSubroute.flow == .current ||
nextSubroute.flow == self.visibleFlow {
self.navigateToDestination(
nextSubroute,
animated: animated,
Expand Down Expand Up @@ -202,42 +203,6 @@ extension Router {
}
}

private func findFirstScreenInVisibleFlow() -> UIViewController? {
var firstScreenInVisibleFlow =
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: visibleScreen.parent ?? visibleScreen
)

while let presentingFirstScreenInVisibleFlow =
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: firstScreenInVisibleFlow?.presentingViewController
) {
firstScreenInVisibleFlow = presentingFirstScreenInVisibleFlow
}

return firstScreenInVisibleFlow
}

private func findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf screen: UIViewController?
) -> UIViewController? {
switch screen {
case let navigationContainer as UINavigationController:
return navigationContainer.viewControllers.first {
($0 as? ScreenRoutable)?.flowIdentifier == visibleFlow.identifier
}
case let tabbedContainer as TabbedContainer:
return tabbedContainer.screens.first {
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: $0
) != nil
}
case let someScreen as ScreenRoutable:
return someScreen.flowIdentifier == visibleFlow.identifier ? someScreen : nil
default: return nil
}
}

private func navigate(
to existingScreen: UIViewController,
animated: Bool,
Expand Down Expand Up @@ -476,6 +441,36 @@ extension Router {
}
}

/// <warning>
/// Normally, the visible screen is automatically updated by the router when a screen is
/// navigated programmatically. But, the interactive transitions(pop or dismiss or tab selection)
/// prevent this, so it should be set manually after returning the previous screen.
extension Router {
public func updateVisibleScreenAfterViewDidAppear(
of screen: UIViewController
) {
defer {
if let aVisibleScreen = visibleScreen as? ScreenRoutable,
!aVisibleScreen.flowIdentifier.isEmpty {
visibleFlow = SomeFlow.instance(aVisibleScreen.flowIdentifier)
}
}

guard let parent = screen.parent else {
visibleScreen = screen
return
}

if parent is UINavigationController ||
parent is TabbedContainer {
visibleScreen = screen
return
}

visibleScreen = parent
}
}

extension Router {
public func findVisibleScreen(
over screen: UIViewController? = nil
Expand Down Expand Up @@ -528,6 +523,55 @@ extension Router {
public func findVisibleScreen(
in tabbedContainer: TabbedContainer
) -> UIViewController {
return tabbedContainer.selectedScreen ?? tabbedContainer
guard let selectedScreen = tabbedContainer.selectedScreen else {
return tabbedContainer
}

switch selectedScreen {
case let navigationContainer as UINavigationController:
return findVisibleScreen(
in: navigationContainer
)
default:
return selectedScreen
}
}
}

extension Router {
private func findFirstScreenInVisibleFlow() -> UIViewController? {
var firstScreenInVisibleFlow =
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: visibleScreen.parent ?? visibleScreen
)

while let presentingFirstScreenInVisibleFlow =
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: firstScreenInVisibleFlow?.presentingViewController
) {
firstScreenInVisibleFlow = presentingFirstScreenInVisibleFlow
}

return firstScreenInVisibleFlow
}

private func findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf screen: UIViewController?
) -> UIViewController? {
switch screen {
case let navigationContainer as UINavigationController:
return navigationContainer.viewControllers.first {
($0 as? ScreenRoutable)?.flowIdentifier == visibleFlow.identifier
}
case let tabbedContainer as TabbedContainer:
return tabbedContainer.screens.first {
findFirstScreenInVisibleFlow(
appearedWithinHierarchyOf: $0
) != nil
}
case let someScreen as ScreenRoutable:
return someScreen.flowIdentifier == visibleFlow.identifier ? someScreen : nil
default: return nil
}
}
}
3 changes: 3 additions & 0 deletions macaroon/Classes/Error/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum Error: Swift.Error {
case appTargetCorrupted(reason: Swift.Error)
case rootContainerNotFound
case screenNotFound
case flowNotFound
case dismissNavigationBarButtonItemNotFound
case popNavigationBarButtonItemNotFound
case colorNotFound(String)
Expand Down Expand Up @@ -35,6 +36,8 @@ extension Error {
return "Root container not found"
case .screenNotFound:
return "Screen not found"
case .flowNotFound:
return "Flow not found"
case .dismissNavigationBarButtonItemNotFound:
return "Navigation bar button item not found for dismissing action"
case .popNavigationBarButtonItemNotFound:
Expand Down
2 changes: 1 addition & 1 deletion macaroon/Classes/Form/UI/Screens/FormScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ open class FormScreen:
didBeginEditing inputFieldView: FormInputFieldView
) {
keyboardController.scrollToEditingRect(
afterContentDidChange: false,
afterContentDidChange: true,
animated: true
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import AnyFormatKit
import Foundation

public struct BasicTextInputFormatter: TextInputFormatter {
public struct CommonTextInputFormatter: TextInputFormatter {
public let pattern: String
public let symbol: Character
public let placeholder: String?
Expand Down
18 changes: 11 additions & 7 deletions macaroon/Classes/Form/Utils/Formatter/MaskInputFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ public struct MaskInputFormatter: TextInputFormatter {
replacementString: string
)

if value.formattedText == pattern,
let placeholder = placeholder {
value = base.formatInput(
currentText: value.formattedText,
range: range,
replacementString: placeholder
)
if value.formattedText == pattern {
if let placeholder = placeholder {
value =
base.formatInput(
currentText: value.formattedText,
range: range,
replacementString: placeholder
)
} else {
value = FormattedTextValue(formattedText: "", caretBeginOffset: 0)
}
}

return (
Expand Down
7 changes: 5 additions & 2 deletions macaroon/Classes/Form/Utils/Validator/EmailValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import Foundation

public struct EmailValidator: Validator {
public let optional: Bool
public let failureReason: String

public init(
_ failureReason: String = ""
optional: Bool = false,
failureReason: String = ""
) {
self.optional = optional
self.failureReason = failureReason
}

Expand All @@ -30,7 +33,7 @@ public struct EmailValidator: Validator {
let text = text,
!text.isEmpty
else {
return .failure(failureReason)
return optional ? .success : .failure(failureReason)
}

let detector =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © 2019 hipolabs. All rights reserved.

import Foundation

public struct FixedCharacterCountValidator: Validator {
public let count: Int
public let failureReason: String

public init(
_ count: Int,
_ failureReason: String = ""
) {
self.count = count
self.failureReason = failureReason
}

public func validate(
_ inputFieldView: FormInputFieldView
) -> Validation {
guard let textInputFieldView = inputFieldView as? FormTextInputFieldView else {
return .failure(failureReason)
}

return validate(
textInputFieldView.text
)
}

public func validate(
_ text: String?
) -> Validation {
guard
let text = text,
text.count == count
else {
return .failure(failureReason)
}

return .success
}
}
51 changes: 51 additions & 0 deletions macaroon/Classes/Form/Utils/Validator/RegexValidator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2019 hipolabs. All rights reserved.

import Foundation

public struct RegexValidator: Validator {
public let regex: String
public let failureReason: String

public init(
_ regex: String,
_ failureReason: String = ""
) {
self.regex = regex
self.failureReason = failureReason
}

public func validate(
_ inputFieldView: FormInputFieldView
) -> Validation {
guard let textInputFieldView = inputFieldView as? FormTextInputFieldView else {
return .failure(failureReason)
}

return validate(
textInputFieldView.text
)
}

public func validate(
_ text: String?
) -> Validation {
guard
let text = text,
!text.isEmpty
else {
return .failure(failureReason)
}

guard let regexExpr = try? NSRegularExpression(pattern: regex) else {
return .success
}

let range = NSRange(text.startIndex..<text.endIndex, in: text)
let matches = regexExpr.matches(in: text, options: [.anchored], range: range)

switch matches.count {
case 1: return .success
default: return .failure(failureReason)
}
}
}
Loading

0 comments on commit 22fcd32

Please sign in to comment.