diff --git a/Source/Components/Controllers/MUFormController.swift b/Source/Components/Controllers/MUFormController.swift index 0dc448f..3a56842 100644 --- a/Source/Components/Controllers/MUFormController.swift +++ b/Source/Components/Controllers/MUFormController.swift @@ -259,28 +259,28 @@ open class MUFormController: MUViewController { extension MUFormController: MUTextFieldViewDelegate { - public func textFieldViewChanged(_ textFieldView: UIView) { + open func textFieldViewChanged(_ textFieldView: UIView) { validateWhenEditing() fieldChanged(textFieldView) } - public func textFieldViewBeginEditing(_ textFieldView: UIView) { + open func textFieldViewBeginEditing(_ textFieldView: UIView) { activeEditedField = textFieldView fieldBeginEditing(textFieldView) } - public func textFieldViewDidEndEditing(_ textFieldView: UIView) { + open func textFieldViewDidEndEditing(_ textFieldView: UIView) { activeEditedField = nil validate() } - public func textFieldViewShouldReturn(_ textFieldView: UIView) { + open func textFieldViewShouldReturn(_ textFieldView: UIView) { for (index, object) in verifyObjects.enumerated() { @@ -309,7 +309,7 @@ extension MUFormController: MUTextFieldViewDelegate { } } - public func textFieldViewBackwardDidTap(_ textFieldView: UIView) { + open func textFieldViewBackwardDidTap(_ textFieldView: UIView) { deleteBackward(textFieldView) } diff --git a/Source/Components/Controllers/MUListController.swift b/Source/Components/Controllers/MUListController.swift index 9086ea9..7ef40a6 100644 --- a/Source/Components/Controllers/MUListController.swift +++ b/Source/Components/Controllers/MUListController.swift @@ -76,6 +76,8 @@ open class MUListController: MUViewController, MUListControlDelegate, MUReusable open var emptyStateStyle: MUEmptyStateControl.Style { .none } + open var shouldDeselectRow: Bool { return true } + open var hasCache: Bool { return false } open var cacheKey: String? { return nil } diff --git a/Source/Components/Controllers/MUViewController.swift b/Source/Components/Controllers/MUViewController.swift index b708114..de62670 100644 --- a/Source/Components/Controllers/MUViewController.swift +++ b/Source/Components/Controllers/MUViewController.swift @@ -41,6 +41,8 @@ open class MUViewController: UIViewController { open var shouldRemoveFromNavigation: Bool { return false } open var hasNavigationBar: Bool? { return nil } + + open var hasBottomBarWhenPushed: Bool { return true } open var hasScroll: Bool { return false } @@ -63,6 +65,13 @@ open class MUViewController: UIViewController { private static var viewControllersArray: [String: MUViewControllerContainer] = [:] // MARK: Override methods + + open override func awakeFromNib() { + + super.awakeFromNib() + + hidesBottomBarWhenPushed = hasBottomBarWhenPushed == false + } open override func viewDidLoad() { @@ -73,8 +82,10 @@ open class MUViewController: UIViewController { popupControl.setup(with: self) loadControl.setup(with: self) - - subscribeOnCustomNotifications() + + localize() + + subscribeOnLanguageNotifications() } open override func viewWillAppear(_ animated: Bool) { @@ -85,8 +96,6 @@ open class MUViewController: UIViewController { updateViewControllersArray() - configureNavigatioBar() - configureInteractivePopGestureRecognizer() subscribeOnNotifications() @@ -126,7 +135,7 @@ open class MUViewController: UIViewController { // MARK: - Public methods - func subscribeOnNotifications() { + open func subscribeOnNotifications() { guard isVisible, isNotificationSubscribed == false else { return } @@ -139,67 +148,85 @@ open class MUViewController: UIViewController { subscribeOnAppNotifications() } - func unsubscribeFromNotifications() { + open func unsubscribeFromNotifications() { isNotificationSubscribed = false keyboardControl.unsubscribeOnNotifications() - - NotificationCenter.default.removeObserver(self) + + unsubscribeOnErrorNotifications() + + unsubscribeOnAppNotification() } - func subscribeOnCustomNotifications() { + open func appDidBecomeActive() { } - func appDidBecomeActive() { + open func appWillResignActive() { } - func appWillResignActive() { - - } - - func appErrorDidBecome(error: Error) { + open func appErrorDidBecome(error: Error) { isLoading = false } - func appErrorDidClear() { + open func appErrorDidClear() { } + + @objc open func localize() { + + view.localize() + } - // MARK: - Public methods - - func close(animated: Bool = true, toRoot: Bool = false, popOnly: Bool = false, completion: (() -> Void)? = nil) { + open func close(animated: Bool = true, toRoot: Bool = false, popOnly: Bool = false, completion: (() -> Void)? = nil) { + + var completionMethod = completion - popupControl.dismiss() + popupControl.dismiss(with: { + + completionMethod?() + + completionMethod = nil + + }) if let presentingMUViewController = presentingViewController, popOnly == false { presentingMUViewController.dismiss(animated: animated) - completion?() + completionMethod?() } else if let navigationController = navigationController, navigationController.viewControllers.count > 1 { if toRoot { - navigationController.popToRootViewController(animated: animated, completion: completion) + navigationController.popToRootViewController(animated: animated, completion: completionMethod) } else { - navigationController.popViewController(animated: animated, completion: completion) + navigationController.popViewController(animated: animated, completion: completionMethod) } } } + + deinit { + + NotificationCenter.default.removeObserver(self) + } // MARK: - Private methods - - private func configureNavigatioBar() { - - guard let hasNavigationBar = hasNavigationBar else { return } - - navigationController?.setNavigationBarHidden(hasNavigationBar == false, animated: true) + + private func subscribeOnLanguageNotifications() { + + NotificationCenter.default.addObserver( + + self, + selector : #selector(localize), + name : .languageDidChange, + object : nil + ) } private func configureInteractivePopGestureRecognizer() { @@ -238,6 +265,12 @@ extension MUViewController: UIGestureRecognizerDelegate { // MARK: - Errors public extension MUViewController { + + func unsubscribeOnErrorNotifications() { + + NotificationCenter.default.removeObserver(self, name: .appErrorDidCome, object: nil) + NotificationCenter.default.removeObserver(self, name: .appErrorDidClear, object: nil) + } func updateErrorRecipient() { @@ -307,7 +340,7 @@ public extension MUViewController { return MUViewController.viewControllersArray["\(T.self)"]?.controller as? T } - static func getInstantiate(with type: T.Type) -> T? { + static func getInstance(with type: T.Type) -> T? { switch T.initMethod { case .fromNib : return T.instantiate() @@ -316,7 +349,7 @@ public extension MUViewController { } } - static func getInstantiate() -> T? { + static func getInstance() -> T? { guard storyboardName != "" else { @@ -348,16 +381,16 @@ public extension MUViewController { setup : ((T) -> Void)? = nil ) { - guard let instantiate = T.getInstantiate(with: T.self) else { + guard let instance = T.getInstance(with: T.self) else { return } - setup?(instantiate) + setup?(instance) push( - with : instantiate, + with : instance, animated : animated, pushCompletion : pushCompletion ) @@ -373,18 +406,18 @@ public extension MUViewController { ) { - guard let instantiate = T.getInstantiate(with: T.self) else { + guard let instance = T.getInstance(with: T.self) else { return } - setup?(instantiate) + setup?(instance) - var presentedController: UIViewController = instantiate + var presentedController: UIViewController = instance if withNavigation { - presentedController = instantiate.addNavigation() + presentedController = instance.addNavigation() } if let style = style { @@ -405,16 +438,16 @@ public extension MUViewController { setup : ((T) -> Void)? = nil) { - guard let instantiate: T = getInstantiate() as? T else { + guard let instance: T = getInstance() as? T else { - return Log.error("failed to create instantiate for \(self)") + return Log.error("failed to create instance for \(self)") } - setup?(instantiate) + setup?(instance) controller?.push( - with : instantiate, + with : instance, animated : animated, pushCompletion : pushCompletion ) @@ -431,26 +464,26 @@ public extension MUViewController { ) { - guard let instantiate: T = getInstantiate() else { + guard let instance: T = getInstance() else { return } - setup?(instantiate) + setup?(instance) - var presentedController: UIViewController = instantiate + var presentedController: UIViewController = instance if asRoot { if let navigationController = findNavigationController(storyboardName: storyboardName) { - navigationController.setViewControllers([instantiate], animated: false) + navigationController.setViewControllers([instance], animated: false) presentedController = navigationController } else { - presentedController = createNavigationController(with: instantiate) + presentedController = createNavigationController(with: instance) } } @@ -471,40 +504,40 @@ public extension MUViewController { setup : ((T) -> Void)? = nil ) { - guard let instantiate: T = screen.getInstantiate() else { + guard let instance: T = screen.getInstance() else { return } - setup?(instantiate) + setup?(instance) - addChild(instantiate) + addChild(instance) - instantiate.didMove(toParent: self) + instance.didMove(toParent: self) if let appendTargetView = appendTargetView { - instantiate.view.frame = appendTargetView.frame + instance.view.frame = appendTargetView.frame - view.insertSubview(instantiate.view, aboveSubview: appendTargetView) + view.insertSubview(instance.view, aboveSubview: appendTargetView) } else { - (insertTargetView ?? view).addSubview(instantiate.view) + (insertTargetView ?? view).addSubview(instance.view) } - instantiate.view.appendConstraints(to: insertTargetView ?? view) + instance.view.appendConstraints(to: insertTargetView ?? view) } - func insert(controller instantiate: UIViewController, into insertTargetView: UIView? = nil) { + func insert(controller instance: UIViewController, into insertTargetView: UIView? = nil) { - addChild(instantiate) + addChild(instance) - (insertTargetView ?? view).addSubview(instantiate.view) + (insertTargetView ?? view).addSubview(instance.view) - instantiate.view.appendConstraints(to: insertTargetView ?? view) + instance.view.appendConstraints(to: insertTargetView ?? view) - instantiate.didMove(toParent: self) + instance.didMove(toParent: self) } func remove(child controller: UIViewController) { @@ -595,6 +628,16 @@ extension MUViewController { NotificationCenter.addObserver(self, selector: #selector(appNotification), name: .appDidBecomeActive) NotificationCenter.addObserver(self, selector: #selector(appNotification), name: .appWillResignActive) + NotificationCenter.addObserver(self, selector: #selector(appNotification), name: .appDidEnterBackground) + NotificationCenter.addObserver(self, selector: #selector(appNotification), name: .appWillEnterBackground) + } + + private func unsubscribeOnAppNotification() { + + NotificationCenter.default.removeObserver(self, name: .appDidBecomeActive, object: nil) + NotificationCenter.default.removeObserver(self, name: .appWillResignActive, object: nil) + NotificationCenter.default.removeObserver(self, name: .appDidEnterBackground, object: nil) + NotificationCenter.default.removeObserver(self, name: .appWillEnterBackground, object: nil) } } @@ -606,6 +649,7 @@ public extension Notification.Name { static let appWillResignActive = Notification.Name("appWillResignActive") static let appDidEnterBackground = Notification.Name("appDidEnterBackground") static let appWillEnterBackground = Notification.Name("appWillEnterBackground") + static let languageDidChange = Notification.Name("languageDidChange") } // MARK: - MUViewControllerContainer diff --git a/Source/Components/Controls/MUCollectionControl.swift b/Source/Components/Controls/MUCollectionControl.swift index 7aa44ee..53e4469 100644 --- a/Source/Components/Controls/MUCollectionControl.swift +++ b/Source/Components/Controls/MUCollectionControl.swift @@ -115,7 +115,10 @@ extension MUCollectionControl: delegate?.cellDidSelected(for: object, at: indexPath) } - collectionView.deselectItem(at: indexPath, animated: true) + if controller?.shouldDeselectRow ?? false { + + collectionView.deselectItem(at: indexPath, animated: true) + } } public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { diff --git a/Source/Components/Controls/MULoadControl.swift b/Source/Components/Controls/MULoadControl.swift index 7bb117e..e964c78 100644 --- a/Source/Components/Controls/MULoadControl.swift +++ b/Source/Components/Controls/MULoadControl.swift @@ -90,7 +90,7 @@ open class MULoadControl: NSObject { updateEmptyEmptyItems() - DispatchQueue.main.asyncAfter(deadline: .now() + (isLoading ? 0.01 : 0.3)) { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + (isLoading ? 0.1 : 0.3)) { [weak self] in self?.updateLoading() } @@ -170,7 +170,7 @@ open class MULoadControl: NSObject { view.hideSkeleton(transition: .none) - controller?.objects = controller?.objects ?? [] + controller?.objects = controller?.objects.filter { ($0 is MUEmptyModel) == false } ?? [] } private func createEmptyItems() -> [MUModel] { @@ -194,6 +194,6 @@ public extension UIView { // MARK: - MUEmptyModel -public class MUEmptyModel: MUModel { +open class MUEmptyModel: MUModel { } diff --git a/Source/Components/Controls/MUPaginationControl.swift b/Source/Components/Controls/MUPaginationControl.swift index 7314aa5..c95e164 100644 --- a/Source/Components/Controls/MUPaginationControl.swift +++ b/Source/Components/Controls/MUPaginationControl.swift @@ -178,13 +178,13 @@ open class MUPaginationControl: NSObject { @objc private func createIndicator(with timer: Timer? = nil) { - guard let targetView = targetView, self.bottomActivityIndicator == nil else { return } + guard let targetView = targetView, bottomActivityIndicator == nil, hasMoreObjects == true else { return } guard isDeleting == false else { return } bottomActivityIndicator?.containerView?.removeFromSuperview() - self.bottomActivityIndicator = getIndicator(in: targetView) + bottomActivityIndicator = getIndicator(in: targetView) makeBottomInset(in: targetView) } diff --git a/Source/Components/Controls/MUPopupControl.swift b/Source/Components/Controls/MUPopupControl.swift index 88df884..de22c65 100644 --- a/Source/Components/Controls/MUPopupControl.swift +++ b/Source/Components/Controls/MUPopupControl.swift @@ -5,7 +5,6 @@ // Created by Ilya B Macmini on 22.05.2019. // Copyright © 2019 MobileUp LLC. All rights reserved. // -// Pods: SwiftEntryKit import UIKit import SwiftEntryKit @@ -24,6 +23,17 @@ open class MUPopupControl { case scale } + // MARK: - StatusBar + + public enum StatusBar { + + case ignored + case hidden + case dark + case light + case inferred + } + //MARK: - ScreenInteraction public enum ScreenInteractionType { @@ -118,6 +128,8 @@ open class MUPopupControl { // MARK: - Public methods + public init() { } + open func setup(with controller: MUViewController) { self.controller = controller @@ -298,11 +310,13 @@ open class MUPopupControl { isClosedOnBackgroundTouch : Bool, isClosedOnSwipe : Bool, isShadowEnabled : Bool, + statusBar : StatusBar = .inferred, widthRatio : CGFloat, heightRatio : CGFloat, arrowIcon : UIImage? = nil, arrowIconOffset : CGFloat = 12, priority : Priority = .normal, + popupName : String? = nil, onDisappear : (() -> Void)? = nil ) { @@ -322,10 +336,12 @@ open class MUPopupControl { isClosedOnBackgroundTouch : isClosedOnBackgroundTouch, isClosedOnSwipe : isClosedOnSwipe, isShadowEnabled : isShadowEnabled, + statusBar : statusBar, size : size, arrowIcon : arrowIcon, arrowIconOffset : arrowIconOffset, priority : priority, + popupName : popupName, onDisappear : onDisappear ) } @@ -340,6 +356,7 @@ open class MUPopupControl { isClosedOnBackgroundTouch : Bool, isClosedOnSwipe : Bool, isShadowEnabled : Bool, + statusBar : StatusBar, widthRatio : CGFloat, topOffset : CGFloat, arrowIcon : UIImage? = nil, @@ -364,6 +381,7 @@ open class MUPopupControl { isClosedOnBackgroundTouch : isClosedOnBackgroundTouch, isClosedOnSwipe : isClosedOnSwipe, isShadowEnabled : isShadowEnabled, + statusBar : statusBar, size : size, arrowIcon : arrowIcon, arrowIconOffset : arrowIconOffset, @@ -382,15 +400,21 @@ open class MUPopupControl { isClosedOnBackgroundTouch : Bool, isClosedOnSwipe : Bool, isShadowEnabled : Bool, + statusBar : StatusBar, size : EKAttributes.PositionConstraints.Size, arrowIcon : UIImage? = nil, arrowIconOffset : CGFloat = 12, priority : Priority = .normal, + popupName : String? = nil, onDisappear : (() -> Void)? = nil ) { var attributes = getAttributes(position: position) + attributes.name = popupName + + attributes.statusBar = getStatusBarStyle(statusBar: statusBar) + attributes.positionConstraints.size = size attributes.positionConstraints.verticalOffset = 0 @@ -457,6 +481,18 @@ open class MUPopupControl { } } + private func getStatusBarStyle(statusBar: StatusBar) -> EKAttributes.StatusBar { + + switch statusBar { + + case .ignored : return .ignored + case .hidden : return .hidden + case .dark : return .dark + case .light : return .light + case .inferred : return .inferred + } + } + private func getAnimations(type: AnimationType) -> (entrance: EKAttributes.Animation, exit: EKAttributes.Animation){ switch type { @@ -630,6 +666,7 @@ public extension MUViewController { isClosedOnBackgroundTouch : Bool = true, isClosedOnSwipe : Bool = false, isShadowEnabled : Bool = true, + statusBar : MUPopupControl.StatusBar = .inferred, widthRatio : CGFloat = 1.0, heightRatio : CGFloat = 0.6, arrowIcon : UIImage? = nil, @@ -646,6 +683,7 @@ public extension MUViewController { isClosedOnBackgroundTouch : isClosedOnBackgroundTouch, isClosedOnSwipe : isClosedOnSwipe, isShadowEnabled : isShadowEnabled, + statusBar : statusBar, widthRatio : widthRatio, heightRatio : heightRatio, arrowIcon : arrowIcon, diff --git a/Source/Components/Controls/MURefreshControl.swift b/Source/Components/Controls/MURefreshControl.swift index 516b498..0bcea0c 100644 --- a/Source/Components/Controls/MURefreshControl.swift +++ b/Source/Components/Controls/MURefreshControl.swift @@ -84,14 +84,17 @@ open class MURefreshControl: NSObject { } private func configure(with tintColor: UIColor? = nil) { + + if refreshControl == nil { - refreshControl = UIRefreshControl() - - refreshControl!.tintColor = tintColor ?? refreshControl!.tintColor - - refreshControl!.addTarget(self, action: #selector(startAnimation), for: .valueChanged) + refreshControl = UIRefreshControl() + + refreshControl?.addTarget(self, action: #selector(startAnimation), for: .valueChanged) + + scrollView?.addSubview(refreshControl!) + } - scrollView?.addSubview(refreshControl!) + refreshControl?.tintColor = tintColor ?? refreshControl?.tintColor } private func resetTopContentInset() { diff --git a/Source/Components/Controls/MUTableControl.swift b/Source/Components/Controls/MUTableControl.swift index 1681fc3..324cadf 100644 --- a/Source/Components/Controls/MUTableControl.swift +++ b/Source/Components/Controls/MUTableControl.swift @@ -709,7 +709,10 @@ extension MUTableControl: UITableViewDelegate, UITableViewDataSource { delegate?.cellDidSelected(for: object, at: indexPath) } - tableView.deselectRow(at: indexPath, animated: true) + if controller?.shouldDeselectRow ?? false { + + tableView.deselectRow(at: indexPath, animated: true) + } } public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { diff --git a/Source/Components/DeveloperTools/MUDeveloperToolsController.swift b/Source/Components/DeveloperTools/MUDeveloperToolsController.swift index 5897f21..dea1026 100644 --- a/Source/Components/DeveloperTools/MUDeveloperToolsController.swift +++ b/Source/Components/DeveloperTools/MUDeveloperToolsController.swift @@ -12,8 +12,18 @@ import UIKit public struct MUEnvironment { - let index: String - let title: String + // MARK: - Public properties + + public let index: String + public let title: String + + // MARK: - Public methods + + public init(index: String, title: String) { + + self.index = index + self.title = title + } } // MARK: - MUDeveloperToolsDelegate @@ -226,14 +236,21 @@ public extension MUDeveloperToolsManager { topController.dismiss(animated: true, completion: nil) } else { - - guard let controller = MUDeveloperToolsController.instantiate(storyboardName: "DeveloperTools", identifier: "MUDeveloperToolsController", bundle: Bundle(for: self)) else { return } + + let controller = MUDeveloperToolsController.instantiate( + + storyboardName : "DeveloperTools", + identifier : "MUDeveloperToolsController", + bundle : Bundle(for: MUDeveloperToolsController.self) + ) + + guard let developerToolsController = controller else { return } MUPopupControl.closeAll() - controller.modalPresentationStyle = .overCurrentContext + developerToolsController.modalPresentationStyle = .overCurrentContext - topController.present(controller, animated: true, completion: nil) + topController.present(developerToolsController, animated: true, completion: nil) } } } diff --git a/Source/Components/Protocols/MUReusableControllerProvider.swift b/Source/Components/Protocols/MUReusableControllerProvider.swift index 0d16320..09ee6cf 100644 --- a/Source/Components/Protocols/MUReusableControllerProvider.swift +++ b/Source/Components/Protocols/MUReusableControllerProvider.swift @@ -57,7 +57,7 @@ public extension MUReusableControllerProvider { let type = getControllerClass(forIdentifier: id) - guard let controller = type.getInstantiate() else { + guard let controller = type.getInstance() else { fatalError("Unable to initialize controller with class: \(type) for reuse identifier: \(id)") } diff --git a/Source/Components/Views/MUCustomView.swift b/Source/Components/Views/MUCustomView.swift index 0c517a9..033beeb 100755 --- a/Source/Components/Views/MUCustomView.swift +++ b/Source/Components/Views/MUCustomView.swift @@ -131,7 +131,7 @@ open class MUCustomView: MUView { if nib == nil { - nib = UINib(nibName: nibName, bundle: Bundle(for: MUCustomView.self)) + nib = UINib(nibName: nibName, bundle: Bundle(for: type(of: self))) MUCustomView.nibCache[nibName] = nib } diff --git a/Source/Extension/UIView+Constraints.swift b/Source/Extension/UIView+Constraints.swift index fdf0f43..7782b97 100644 --- a/Source/Extension/UIView+Constraints.swift +++ b/Source/Extension/UIView+Constraints.swift @@ -8,11 +8,11 @@ import UIKit -// MARK: LayoutInsets +// MARK: - LayoutInsets public struct LayoutInsets { - // MARK: Public properites + // MARK: - Public properites static var zero: LayoutInsets { self.init(top: 0, left: 0, bottom: 0, right: 0) } @@ -24,9 +24,9 @@ public struct LayoutInsets { public var right: CGFloat? - // MARK: Public methods + // MARK: - Public methods - static func insets( + public static func insets( top : CGFloat? = 0, left : CGFloat? = 0, bottom : CGFloat? = 0, @@ -37,22 +37,31 @@ public struct LayoutInsets { } } -// MARK: LayoutDimension +// MARK: - LayoutDimension -struct LayoutDimension: OptionSet { +public struct LayoutDimension: OptionSet { - let rawValue: Int + // MARK: - Public properties + + public let rawValue: Int - static let height = LayoutDimension(rawValue: 1) + public static let height = LayoutDimension(rawValue: 1) - static let width = LayoutDimension(rawValue: 2) + public static let width = LayoutDimension(rawValue: 2) + + // MARK: - Public methods + + public init(rawValue: Int) { + + self.rawValue = rawValue + } } -// MARK: AutoLayout Extensions +// MARK: - AutoLayout Extensions -extension UIView { +public extension UIView { - // MARK: Layout Subview + // MARK: - Layout Subview func layoutCenter(_ view: UIView, xOffset: CGFloat = 0, yOffset: CGFloat = 0) { @@ -141,7 +150,7 @@ extension UIView { } } - // MARK: Find Constraint + // MARK: - Find Constraint func findConstraint(byId id: String) -> NSLayoutConstraint? { @@ -196,7 +205,7 @@ extension UIView { return nil } - // MARK: Set Constraint + // MARK: - Set Constraint func setConstraint(type: NSLayoutConstraint.Attribute, value: CGFloat, updateSuperview: Bool = true) { @@ -260,10 +269,10 @@ extension UIView { } } -// MARK: Anchors +// MARK: - Anchors @available(iOS 11.0, *) -extension UIView { +public extension UIView { func getTopAnchor(safe: Bool) -> NSLayoutYAxisAnchor { @@ -286,9 +295,9 @@ extension UIView { } } -// MARK: NSLayoutAnchor +// MARK: - NSLayoutAnchor -extension NSLayoutAnchor { +public extension NSLayoutAnchor { @objc func makeConstraint(equalTo anchor: NSLayoutAnchor, constant: CGFloat) { diff --git a/Source/Extension/UIView.swift b/Source/Extension/UIView.swift index 2d2bd4f..e00b260 100755 --- a/Source/Extension/UIView.swift +++ b/Source/Extension/UIView.swift @@ -104,7 +104,7 @@ public extension UIView { static private var viewDataStorage: [String: [String: Any?]] = [:] - // MARK :- Visibility + // MARK: - Visibility func masked(corners: UIRectCorner, radius: CGFloat) { @@ -130,7 +130,7 @@ public extension UIView { layer.mask = maskLayer } - // MARK :- Subviews + // MARK: - Subviews func getSubview(id: String) -> UIView? { @@ -382,7 +382,7 @@ public extension UIView { self.layer.add(animation, forKey: "position") } - // MARK :- Private methods + // MARK: - Private methods private func updateDefaultShadow() { @@ -422,3 +422,16 @@ public extension UIView { return image ?? UIImage() } } + +extension UIView { + + // MARK: - Localization + + @objc open func localize() { + + for subview in subviews { + + subview.localize() + } + } +} diff --git a/Source/Extension/UserDefaults.swift b/Source/Extension/UserDefaults.swift index 15cddda..70e28fc 100644 --- a/Source/Extension/UserDefaults.swift +++ b/Source/Extension/UserDefaults.swift @@ -23,7 +23,12 @@ public extension UserDefaults { // MARK: - Public methods - class func set(_ value: Any, forKey key: String, timeout: TimeInterval? = nil) { + class func set(_ value: Any?, forKey key: String, timeout: TimeInterval? = nil) { + + guard let value = value else { + + return UserDefaults.standard.removeObject(forKey: key) + } UserDefaults.standard.set(value, forKey: key) diff --git a/Source/Helpers/MUNetworkManager.swift b/Source/Helpers/MUNetworkManager.swift index e48b44f..c8fddc0 100755 --- a/Source/Helpers/MUNetworkManager.swift +++ b/Source/Helpers/MUNetworkManager.swift @@ -46,7 +46,7 @@ open class MUNetworkManager: NSObject { // MARK: - Override methods - init(serverTrustPolicies: [String: MUServerTrustPolicy]? = nil) { + public init(serverTrustPolicies: [String: MUServerTrustPolicy]? = nil) { let configuration = URLSessionConfiguration.default @@ -77,7 +77,8 @@ open class MUNetworkManager: NSObject { super.init() } - convenience init(serverUrl: String, serverTrustPolicies: [String: MUServerTrustPolicy]? = nil) { + public convenience init(serverUrl: String, serverTrustPolicies: [String: MUServerTrustPolicy]? = nil) { + self.init(serverTrustPolicies: serverTrustPolicies) self.serverUrl = serverUrl @@ -190,10 +191,12 @@ open class MUNetworkManager: NSObject { failure : @escaping (MUNetworkError?,Any?) -> Void ) { + logResponse(with: responseObject, requestId: requestId) + if responseObject.response?.statusCode == nil, let error = responseObject.result.error as NSError? { switch error.code { - case -999 : return + case -999 : failure(MUNetworkError.unknownError, nil) case -1009, -1005 : failure(MUNetworkError.connectionError, nil) default : failure(MUNetworkError.serverError, nil) } @@ -254,6 +257,23 @@ open class MUNetworkManager: NSObject { // MARK: - Log methods + private func logResponse(with responseObject: DataResponse, requestId: Int) { + + let body = responseObject.result.value as? NSDictionary ?? responseObject.result.value as? NSArray + + let logDictionary = [ + + "Body" : body ?? responseObject.result.value ?? "" + ] + + let statusCode = responseObject.response?.statusCode ?? -1 + + DispatchQueue.global().async { + + Log.event("\n[\(requestId)] Response (code: \(statusCode)):\n\(logDictionary)\n".unescapingUnicodeCharacters) + } + } + private func getRequestId() -> Int { numberOfRequests += 1 @@ -318,14 +338,14 @@ public extension MUNetworkManager { public struct MUNetworkRequest { - var url : String - var method : MUNetworkHttpMethod - var headers : [String : String]? = nil - var params : [String : Any]? = nil - var body : Any? = nil - var encoding : MUNetworkEncoding = .url - var success : ((Any) -> Void)? = nil - var failure : ((Error?) -> Void)? = nil + public var url : String + public var method : MUNetworkHttpMethod + public var headers : [String : String]? = nil + public var params : [String : Any]? = nil + public var body : Any? = nil + public var encoding : MUNetworkEncoding = .url + public var success : ((Any) -> Void)? = nil + public var failure : ((Error?) -> Void)? = nil } // MARK: - MUNetworkManager @@ -410,4 +430,28 @@ public enum MUServerTrustPolicy { case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) case disableEvaluation case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) + + // MARK: - Public methods + + public static func publicKeys() -> [SecKey] { + + return ServerTrustPolicy.publicKeys() + } } + +// MARK: - String + +extension String { + + var unescapingUnicodeCharacters: String { + + let string = self.replace(pattern: "\\\\U", with: "\\\\u") + + let mutableString = NSMutableString(string: string as NSString) + + CFStringTransform(mutableString, nil, "Any-Hex/Java" as NSString, true) + + return mutableString as String + } +} +