diff --git a/Example/MobileCoreExample.xcodeproj/project.pbxproj b/Example/MobileCoreExample.xcodeproj/project.pbxproj index 684801f..36850b9 100644 --- a/Example/MobileCoreExample.xcodeproj/project.pbxproj +++ b/Example/MobileCoreExample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -261,7 +261,7 @@ DE9221C71EF28CDF000C8FC7 /* Resources */, DEC55DE61EF2A16E00FAE024 /* Headers */, DE1485561F10C09400A250A6 /* Embed Frameworks */, - D437653C230E8E5B004974CC /* swiftlint Run Script */, + D437653C230E8E5B004974CC /* Swiftlint Run Script */, ); buildRules = ( ); @@ -284,11 +284,12 @@ DE9221C11EF28CDF000C8FC7 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; KnownAssetTags = ( Bundle, ); LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1220; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = "Praveen Prabhakar"; TargetAttributes = { D44E56FD2341D3B900A0E56C = { @@ -357,21 +358,21 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - D437653C230E8E5B004974CC /* swiftlint Run Script */ = { + D437653C230E8E5B004974CC /* Swiftlint Run Script */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); - name = "swiftlint Run Script"; + name = "Swiftlint Run Script"; outputFileListPaths = ( ); outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\n cd ..\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; @@ -525,6 +526,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -590,6 +592,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; diff --git a/Example/MobileCoreExample.xcodeproj/xcshareddata/xcschemes/MobileCoreExample.xcscheme b/Example/MobileCoreExample.xcodeproj/xcshareddata/xcschemes/MobileCoreExample.xcscheme index 526f65b..53323f2 100644 --- a/Example/MobileCoreExample.xcodeproj/xcshareddata/xcschemes/MobileCoreExample.xcscheme +++ b/Example/MobileCoreExample.xcodeproj/xcshareddata/xcschemes/MobileCoreExample.xcscheme @@ -1,6 +1,6 @@ UIAppearance { - let appearance = (containerClass == nil) ? self.appearance() : self.appearance(whenContainedInInstancesOf: containerClass!) + let appearance = (containerClass == nil) ? self.appearance() : self.appearance(whenContainedInInstancesOf: containerClass!) if let tintColor = theme[ThemeKey.tintColor.rawValue] { appearance.tintColor = ThemesManager.getColor(tintColor as? String) } @@ -62,16 +62,16 @@ extension UIView: AppearanceManagerProtocol { } return appearance } - + @objc public class func setBackgroundImage(_ imageTheme: Any) { self.setBackgroundImage(imageType: ThemeStyle.defaultStyle, imageName: imageTheme) } - + public class func setBackgroundImage(imageType: String?, imageName: Any) { if let types = imageName as? ThemeModel { types.forEach { setBackgroundImage(imageType: $0, imageName: $1) } } - + guard let imageType = imageType else { return } if let image = ThemesManager.getImage(imageName as? String), let segmentSelf = self as? UISegmentedControl.Type { @@ -81,12 +81,7 @@ extension UIView: AppearanceManagerProtocol { } extension UISegmentedControl { - override public class func setUpAppearance(theme: ThemeModel, containerClass: [UIAppearanceContainer.Type]?) -> UIAppearance { - super.setUpAppearance(theme: theme, containerClass: containerClass) -// let appearance = (containerClass == nil) ? self.appearance() : self.appearance(whenContainedInInstancesOf: containerClass!) -// return appearance - } - + public class func setBackgroundImage(imageType: String, image: UIImage) { let image = image.withRenderingMode(.alwaysTemplate) .resizableImage(withCapInsets: UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3)) @@ -104,8 +99,8 @@ extension UINavigationBar { // swiftlint:disable cyclomatic_complexity override public class func setUpAppearance(theme: ThemeModel, containerClass: [UIAppearanceContainer.Type]?) -> UIAppearance { super.setUpAppearance(theme: theme, containerClass: containerClass) - let appearance = (containerClass == nil) ? self.appearance() : self.appearance(whenContainedInInstancesOf: containerClass!) - + let appearance = (containerClass == nil) ? self.appearance() : self.appearance(whenContainedInInstancesOf: containerClass!) + for type in ThemeKey.allCases { guard let value = theme[type.rawValue] else { continue } switch type { @@ -145,7 +140,7 @@ extension UINavigationBar { } return appearance } - + @available(iOS 13.0, *) private class func navBarAppearance(theme: ThemeModel, tile: AttributedDictionary?, largeTitle: AttributedDictionary?) -> UINavigationBarAppearance { let navAppe = UINavigationBarAppearance() @@ -162,17 +157,17 @@ extension UINavigationBar { return navAppe } // swiftlint:enable cyclomatic_complexity - + override public class func setBackgroundImage(_ image: Any) { // defaultImage var defaultImage: UIImage? = ThemesManager.getImage(image) var landScapeImage: UIImage? - + if let imageTheme = image as? ThemeModel { defaultImage = ThemesManager.getImage(imageTheme[ThemeKey.defaultValue]) landScapeImage = ThemesManager.getImage(imageTheme["landScape"]) } - + if defaultImage != nil { self.applyBackgroundImage(navigationBar: nil, defaultImage: defaultImage!, landScapeImage: landScapeImage) } diff --git a/Sources/AppTheming/ThemeComponents/UIButton+Theme.swift b/Sources/AppTheming/ThemeComponents/UIButton+Theme.swift index e7d7372..7185891 100644 --- a/Sources/AppTheming/ThemeComponents/UIButton+Theme.swift +++ b/Sources/AppTheming/ThemeComponents/UIButton+Theme.swift @@ -9,6 +9,7 @@ import Foundation import UIKit +@objc extension UIButton: ControlThemeProtocol { // check view state, to update style open func subStyleName() -> String? { @@ -21,29 +22,29 @@ extension UIButton: ControlThemeProtocol { else if self.isHighlighted { return ThemeStyle.highlightedStyle } - + return ThemeStyle.disabledStyle } - + // For custome key:value pairs open func update(themeDic: ThemeModel, state: UIControl.State) { let text = self.title(for: state) ?? "" let range = NSRange(location: 0, length: text.count) let attribute = NSMutableAttributedString(string: text) - + if let color = ThemesManager.getColor(themeDic[ThemeKey.textcolor] as? String) { self.setTitleColor(color, for: state) // For attributed title attribute.addAttribute(.foregroundColor, value: color, range: range) } - + if let text = themeDic[ThemeKey.textfont] as? String, let font = ThemesManager.getFont(text) { self.titleLabel?.font = font // For attributed title attribute.addAttribute(.font, value: font, range: range) } - + if let underline = themeDic[ThemeKey.underline] as? ThemeModel { if let color = ThemesManager.getColor(underline[ThemesType.color] as? String) { attribute.addAttribute(.underlineColor, value: color, range: range) @@ -53,11 +54,11 @@ extension UIButton: ControlThemeProtocol { attribute.addAttribute(.underlineStyle, value: style, range: range) } } - + if let image = ThemesManager.getImage(themeDic[ThemeKey.image]) { self.setImage(image, for: state) } - + self.setAttributedTitle(attribute, for: state) } } diff --git a/Sources/AppTheming/ThemeComponents/UILabel+Theme.swift b/Sources/AppTheming/ThemeComponents/UILabel+Theme.swift index d107d1c..f512dbc 100644 --- a/Sources/AppTheming/ThemeComponents/UILabel+Theme.swift +++ b/Sources/AppTheming/ThemeComponents/UILabel+Theme.swift @@ -20,7 +20,7 @@ extension UILabel: ThemeProtocol { public func subStyleName() -> String? { self.isEnabled ? nil : ThemeStyle.disabledStyle } - + // Force update theme attibute public func updateTheme(_ theme: ThemeModel) { for (kind, value) in theme { diff --git a/Sources/AppTheming/ThemeComponents/UINavigationBar+Theme.swift b/Sources/AppTheming/ThemeComponents/UINavigationBar+Theme.swift index 14eacc9..ddf538f 100644 --- a/Sources/AppTheming/ThemeComponents/UINavigationBar+Theme.swift +++ b/Sources/AppTheming/ThemeComponents/UINavigationBar+Theme.swift @@ -6,11 +6,14 @@ // Copyright © 2017 Praveen Prabhakar. All rights reserved. // +#if canImport(CoreUtility) +import CoreUtility +#endif import Foundation import UIKit public extension UINavigationBar { - + /** * Configures the navigation bar to use an image as its background. */ @@ -18,14 +21,14 @@ public extension UINavigationBar { navigationBar: UINavigationBar?, defaultColor: UIColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), landScapeColor landScape: UIColor? = nil ) { - + self.applyBackgroundImage( navigationBar: navigationBar, defaultImage: defaultColor.generateImage(), landScapeImage: landScape?.generateImage() ?? defaultColor.generateImage() ) } - + /** * Configures the navigation bar to use an image as its background. */ @@ -33,12 +36,12 @@ public extension UINavigationBar { navigationBar: UINavigationBar?, defaultImage: UIImage? = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1).generateImage(), landScapeImage landScape: UIImage? = nil) { - + // These background images contain a small pattern which is displayed // in the lower right corner of the navigation bar. var defaultImage = defaultImage var landScape = landScape ?? defaultImage - + // Both of the above images are smaller than the navigation bar's // size. To enable the images to resize gracefully while keeping their // content pinned to the bottom right corner of the bar, the images are @@ -50,38 +53,40 @@ public extension UINavigationBar { // are empty. if let localImage = defaultImage { defaultImage = localImage.resizableImage( - withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: localImage.size.height - 1, right: localImage.size.width - 1) + withCapInsets: UIEdgeInsets( + top: 0, left: 0, bottom: localImage.size.height - 1, right: localImage.size.width - 1) ) } - + if let localImage = landScape { landScape = localImage.resizableImage( - withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: localImage.size.height - 1, right: localImage.size.width - 1) + withCapInsets: UIEdgeInsets( + top: 0, left: 0, bottom: localImage.size.height - 1, right: localImage.size.width - 1) ) } let navigationBar = navigationBar ?? UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]) - + // The bar metrics associated with a background image determine when it // is used. The background image associated with the defaultImage bar metrics // is used when a more suitable background image can not be found. navigationBar.setBackgroundImage(defaultImage, for: .default) - + // The background image associated with the LandscapePhone bar metrics // is used by the shorter variant of the navigation bar that is used on // iPhone when in landscape. navigationBar.setBackgroundImage(landScape, for: .compact) } - + /** * Configures the navigation bar to use a transparent background (see-through * but without any blur). */ static func applyTransparentBackground(navigationBar: UINavigationBar?, _ opacity: CGFloat) { - + let navigationBar = navigationBar ?? UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]) - + // The background of a navigation bar switches from being translucent // to transparent when a background image is applied. The intensity of // the background image's alpha channel is inversely related to the @@ -90,20 +95,21 @@ public extension UINavigationBar { // // Below, background image is dynamically generated with the desired // opacity. - if let transparentBackground = UIColor.white.generateImage( opacity: opacity, contentsScale: navigationBar.layer.contentsScale) { + if let transparentBackground = UIColor.white.generateImage( + opacity: opacity, contentsScale: navigationBar.layer.contentsScale) { navigationBar.setBackgroundImage(transparentBackground, for: .default) } } - + /** * Configures the navigation bar to use a custom color as its background. * The navigation bar remains translucent. */ static func applyTintColor(navigationBar: UINavigationBar?, _ color: UIColor) { - + let navigationBar = navigationBar ?? UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]) - + navigationBar.barTintColor = color } } diff --git a/Sources/AppTheming/ThemeComponents/UISearchBar+Theme.swift b/Sources/AppTheming/ThemeComponents/UISearchBar+Theme.swift index a022750..4f5c4d8 100644 --- a/Sources/AppTheming/ThemeComponents/UISearchBar+Theme.swift +++ b/Sources/AppTheming/ThemeComponents/UISearchBar+Theme.swift @@ -14,8 +14,8 @@ import UIKit // MARK: AssociatedKey private extension AssociatedKey { - static var searchUITextField = "searchUITextField" - static var searchBarAttributes = "searchBarAttributes" + static var searchUITextField = Int8(0) // "searchUITextField" + static var searchBarAttributes = Int8(1) // "searchBarAttributes" } // MARK: UISearchBar @@ -23,11 +23,11 @@ extension UISearchBar: ThemeProtocol { public func updateTheme(_ theme: ThemeModel) { searchBarStyle = .prominent - isTranslucent = false + isTranslucent = false var tintColor: UIColor? var textcolor: UIColor? var font: UIFont? - + for (kind, value) in theme { switch kind { case ThemeKey.barTintColor.rawValue: @@ -46,12 +46,12 @@ extension UISearchBar: ThemeProtocol { break } } - + addObserver(self, forKeyPath: #keyPath(UISearchBar.placeholder), options: [.new], context: nil) - + configure(tintColor: tintColor, textColor: textcolor, font: font) } - + // MARK: - Key-Value Observing // swiftlint:disable block_based_kvo override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { @@ -81,39 +81,39 @@ private extension UISearchBar { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.searchUITextField) } } - + // MARK: SearchTextField var searchBarAttributes: AttributedDictionary? { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.searchBarAttributes) } set { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.searchBarAttributes) } } - + func configure(tintColor: UIColor? = nil, textColor: UIColor? = nil, font: UIFont? = nil) { guard let tintColor = tintColor else { return } self.tintColor = tintColor - + guard let textField: UITextField = searchUITextField else { return } - + if let glassIconView = textField.leftView as? UIImageView { glassIconView.image = glassIconView.image?.withRenderingMode(.alwaysTemplate) glassIconView.tintColor = tintColor } - + if let clearButton = textField.value(forKey: "clearButton") as? UIButton { clearButton.setImage(clearButton.imageView?.image?.withRenderingMode(.automatic), for: .normal) clearButton.tintColor = tintColor } - + update(font: font, textColor: textColor) } - + func update(font: UIFont?, textColor: UIColor?) { - + // Find the index of the search field in the search bar subviews. if let searchField = searchUITextField { // Set its frame. searchField.frame = CGRect(x: 5.0, y: 5.0, width: frame.width - 10.0, height: frame.height - 10.0) - + var att = AttributedDictionary() // Set the font and text color of the search field. if font != nil { @@ -131,7 +131,7 @@ private extension UISearchBar { if let sting = searchField.attributedPlaceholder?.string, !att.isEmpty { searchField.attributedPlaceholder = NSAttributedString(string: sting, attributes: att) } - + // Save preference self.searchBarAttributes = att // Set the background color of the search field. diff --git a/Sources/AppTheming/ThemeComponents/UISegmentedControl+Theme.swift b/Sources/AppTheming/ThemeComponents/UISegmentedControl+Theme.swift index 7eeb12d..dc93dd2 100644 --- a/Sources/AppTheming/ThemeComponents/UISegmentedControl+Theme.swift +++ b/Sources/AppTheming/ThemeComponents/UISegmentedControl+Theme.swift @@ -8,15 +8,16 @@ import Foundation import UIKit +@objc extension UISegmentedControl: ControlThemeProtocol { // check view state, to update style open func subStyleName() -> String? { nil } - + public func update(themeDic: ThemeModel, state: UIControl.State) { if let text = themeDic[ThemeKey.tintColor] as? String, let color: UIColor = ThemesManager.getColor(text) { self.tintColor = color } - + if #available(iOS 13, *), (themeDic["iOS12Style"] as? Bool) ?? false { // Update with iOS 12 style var textFont: UIFont = .systemFont(ofSize: 13, weight: .regular) @@ -33,7 +34,7 @@ extension UISegmentedControl: ControlThemeProtocol { func ensureiOS12Style(font: UIFont, textColor: UIColor = .white) { self.backgroundColor = UIColor.clear - + let tintColorImage = UIImage(color: tintColor) // Must set the background image for normal to something (even clear) else the rest won't work setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default) diff --git a/Sources/AppTheming/ThemeComponents/UIView+Themes.swift b/Sources/AppTheming/ThemeComponents/UIView+Themes.swift index 442d782..c3ff431 100644 --- a/Sources/AppTheming/ThemeComponents/UIView+Themes.swift +++ b/Sources/AppTheming/ThemeComponents/UIView+Themes.swift @@ -19,7 +19,7 @@ extension ThemeModel { get { self[key.rawValue] } set { self[key.rawValue] = newValue } } - + subscript(theme: ThemesType) -> Any? { get { self[theme.rawValue] } set { self[theme.rawValue] = newValue } @@ -58,7 +58,7 @@ public struct ThemeStyle { public static let highlightedStyle = "highlighted" public static let selectedStyle = "selected" public static let disabledStyle = "disabled" - + public static func allStyles() -> [String] { [ ThemeStyle.highlightedStyle, @@ -83,7 +83,7 @@ extension UIView { public func updateVisualProperty() { self.needsThemesUpdate = true } - + public func updateShadowPathIfNeeded() { if self.layer.shadowPath != nil { let rect = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height) @@ -116,7 +116,7 @@ fileprivate extension UIView { // Update View with Theme properties self.generateVisualThemes() } - + func generateVisualThemes() { // If Theme is emtpy, retrun guard !ThemesManager.themesJSON.isEmpty else { return } @@ -129,7 +129,7 @@ fileprivate extension UIView { // Step 1. Config view with new Theme-style self.configureTheme(themeDic) } - + // Step 2. Only needed for UIControl types, Eg. Button guard let self = self as? ControlThemeProtocol else { return } // Get styles for diffrent states of UIControl @@ -148,7 +148,7 @@ fileprivate extension UIView { self.setThemes(styles) } } - + // Retruns ('classname', 'Theme-style-name') only if both are valid func getThemeName() -> (String, String)? { // Vadidate className and ThemeName @@ -157,14 +157,14 @@ fileprivate extension UIView { let themeName = self.theme else { return nil } - + var baseClassName: String? = className let getSuperClass = { (obj: AnyObject) -> AnyClass? in guard let superClass: AnyClass = class_getSuperclass(type(of: obj)) else { return nil } if let className = Reflection.classNameAsString(superClass), className.hasPrefix("UI") { return nil } - + return superClass } @@ -187,7 +187,7 @@ fileprivate extension UIView { // If there is no valid Theme, return nil return nil } - + // Update view with styleValues @objc func configureTheme(_ themeDic: ThemeModel?) { guard let theme = themeDic else { return } @@ -218,7 +218,7 @@ extension UIView { guard let self = self as? ThemeProtocol else { return } self.updateTheme(theme) } - + // views background color public func updateBackgroundColor(_ color: UIColor) { self.backgroundColor = color diff --git a/Sources/AppTheming/ThemeProtocol.swift b/Sources/AppTheming/ThemeProtocol.swift index 0177ed9..0ff3596 100644 --- a/Sources/AppTheming/ThemeProtocol.swift +++ b/Sources/AppTheming/ThemeProtocol.swift @@ -11,29 +11,29 @@ import UIKit // Used for UIView subclasses Type public protocol ThemeProtocol: AnyObject { - + // Retruns 'ThemeStyle' specific to current state of object. // Say if UIView is disabled, retrun "disabled", which can be clubed with main Theme style. // Eg, if currentTheme is 'viewB', then when disabled state, theme willbe : 'viewB:disabled' func subStyleName() -> String? - + // Custom Subclass can implement, to config Custom component func updateTheme(_ theme: ThemeModel) } // MARK: UIView public extension ThemeProtocol where Self: UIView { - + // If view is disabled, check for ".disabledStyle" style func subStyleName() -> String? { self.isUserInteractionEnabled ? nil : ThemeStyle.disabledStyle } - + // Color func getColor(_ colorName: String?) -> UIColor? { ThemesManager.getColor(colorName) } - + // font func getFont(_ fontName: String?) -> UIFont? { ThemesManager.getFont(fontName) @@ -43,7 +43,7 @@ public extension ThemeProtocol where Self: UIView { // MARK: ControlThemeProtocol // Used for UIControl objects, when multiple states are possible to set at initalization public protocol ControlThemeProtocol: ThemeProtocol { - + func getAllThemeSubType() -> Bool func setThemes(_ themes: ThemeModel) func update(themeDic: ThemeModel, state: UIControl.State) diff --git a/Sources/AppTheming/ThemesManager.swift b/Sources/AppTheming/ThemesManager.swift index a95ac2f..07f2bea 100644 --- a/Sources/AppTheming/ThemesManager.swift +++ b/Sources/AppTheming/ThemesManager.swift @@ -38,7 +38,7 @@ open class ThemesManager { ThemesManager.setupThemes(themes: themeContent, imageSourceBundle: imageSource) } } - + // public static func setupThemes(themes: ThemeModel, imageSourceBundle imageSource: [Bundle]? = nil) { // Usefull for loading Images that are stored in bundle which are defined in Themes's JSON @@ -47,7 +47,7 @@ open class ThemesManager { // NOTE: currently does not spport merging of two theme files ThemesManager.themesJSON = themes } - + // MARK: Theme components public static func generateVisualThemes(forClass name: String, styleName: String, subStyleName subStyle: String? = nil) -> ThemeModel? { var styleName = styleName @@ -62,24 +62,24 @@ open class ThemesManager { } return currentTheme } - + // MARK: ViewComponents public static func isViewComponentValid(componentName: String) -> Bool { self.isThemeComponentValid(componentName) } - + // TODO: custom themes for view-objects public static func getViewComponent(_ componentName: String, styleName: String?) -> ThemeModel? { guard styleName != nil else { return nil } return ThemesManager.getDefaults(type: .components, keyName: componentName, styleName: styleName) as? ThemeModel } - + // MARK: UIColor // TODO: gradian, rgb, alpha, ... public static func getColor(_ colorName: String?) -> UIColor? { guard let colorName = colorName else { return nil } // Check if its image coded string - if (colorName.hasPrefix("@")), let image = ThemesManager.getImage(colorName) { + if colorName.hasPrefix("@"), let image = ThemesManager.getImage(colorName) { return image.getColor() } // Get hex color @@ -96,7 +96,7 @@ open class ThemesManager { } return nil } - + // MARK: UIFont // TODO: bold, thin, ... public static func getFont(_ fontName: String?) -> UIFont? { @@ -123,7 +123,7 @@ open class ThemesManager { } return UIFont.systemFont(ofSize: 14.0) } - + // MARK: UIImage public static func getImage(_ imageName: Any?) -> UIImage? { guard let imageName = imageName as? String else { return nil } @@ -155,7 +155,7 @@ open class ThemesManager { public static func getLayer(_ layerName: String? = nil) -> ThemeModel? { ThemesManager.getDefaults(type: .layer, keyName: layerName) as? ThemeModel ?? [:] } - + // MARK: Size public static func getSize(_ value: Any? = nil) -> CGSize { guard var value = value as? String else { return .zero } @@ -167,7 +167,7 @@ open class ThemesManager { } return CGSize(width: comp.first ?? 0, height: comp.last ?? 0) } - + @discardableResult public static func getBackgroundLayer(_ layer: ThemeModel?, toLayer: CALayer? = nil) -> CALayer? { guard let layer = layer else { return nil } @@ -178,7 +178,7 @@ open class ThemesManager { } return 0.0 } - + let caLayer = toLayer ?? CALayer() for (name, value) in layer { switch name { @@ -210,7 +210,7 @@ open class ThemesManager { } return caLayer } - + // MARK: App Appearance // TODO: custom themes for view-objects public static func getAppearance(_ appearanceName: String? = nil) -> Any? { @@ -226,7 +226,7 @@ extension ThemesManager { fileprivate static func isThemeComponentValid(_ component: String) -> Bool { // Get all the components of spefic type - guard self.themeComponent?[component] as? ThemeModel != nil else { return false } + guard self.themeComponent?[component] is ThemeModel else { return false } return true } @@ -263,12 +263,12 @@ extension ThemesManager { fileprivate class var themeAppearance: ThemeModel? { ThemesManager.themesJSON[ThemesType.appearance] as? ThemeModel } - + // MARK: Layer fileprivate class var themeLayer: ThemeModel? { ThemesManager.themesJSON[ThemesType.layer] as? ThemeModel } - + // layer - fileprivate static func themeLayer(_ layerName: String) -> ThemeModel? { self.themeLayer?[layerName] as? ThemeModel @@ -285,7 +285,7 @@ extension ThemesManager { // Retruns all appearance, which has same base name return themeAppearance?.filter { $0.0.hasPrefix(appearanceName) } } - + // Get updated ThemeModel based on device's Version /* "UISegmentedControl": { @@ -303,24 +303,24 @@ extension ThemesManager { let deviceVersion = BundleURLScheme.osVersion var data = model var baseModel: (ThemeModel, Float)? - + let osModels = model.filter { $0.key.hasPrefix("ios_") } osModels.forEach { arg in let key = arg.key.trimming("ios_") data.removeValue(forKey: arg.key) let dataVersion = NSString(string: key).floatValue if dataVersion <= deviceVersion, let val = arg.value as? ThemeModel, - (baseModel == nil || (baseModel!.1 < dataVersion)) { + baseModel == nil || (baseModel!.1 < dataVersion) { baseModel = (val, dataVersion) } } - + if baseModel != nil { data += baseModel!.0 } return data } - + // Defaults fileprivate static func getDefaults(type: ThemesType, keyName: String? = nil, styleName: String? = nil) -> Any? { guard let key = keyName else { return nil } @@ -337,7 +337,7 @@ extension ThemesManager { // If view-component has super's style, use it as base component and merge its own style superCom += viewComponent superCom.removeValue(forKey: ThemeKey.superComponent.rawValue) - + // Merged result return getOSVersion(model: superCom) } @@ -356,7 +356,7 @@ extension ThemesManager { case .appearance, .link: return nil } - + var actualComponents: Any? // If component of specifc type is not found, search for "default" style let components: Any? = superBlock?(key) ?? superBlock?(ThemeStyle.defaultStyle) @@ -365,7 +365,7 @@ extension ThemesManager { let currentComponent = components as? ThemeModel, let superType = currentComponent[ThemeKey.superComponent] as? String, let superComponents = superBlock?(superType) as? ThemeModel { - + // Merge super's style with current theme actualComponents = superComponents + currentComponent } diff --git a/Sources/CoreComponents/Contollers/CollectionViewControllerProtocol.swift b/Sources/CoreComponents/Contollers/CollectionViewControllerProtocol.swift index a767474..ee4abb1 100644 --- a/Sources/CoreComponents/Contollers/CollectionViewControllerProtocol.swift +++ b/Sources/CoreComponents/Contollers/CollectionViewControllerProtocol.swift @@ -12,7 +12,10 @@ import CoreUtility import Foundation import UIKit -private var kAOCollectionVC = "k.FT.AO.CollectionViewController" +private enum AOAssociatedKey { + @nonobjc static var collectionVC = Int8(0) +// private var kAOCollectionVC = "k.FT.AO.CollectionViewController" +} public protocol CollectionViewControllerProtocol: ViewControllerProtocol { var flowLayout: UICollectionViewLayout { get } @@ -32,11 +35,11 @@ public extension CollectionViewControllerProtocol { func sectionInset() -> UIEdgeInsets { .zero } - + var flowLayout: UICollectionViewLayout { UICollectionViewLayout() } - + var collectionView: UICollectionView { get { collectionViewController.collectionView @@ -45,9 +48,9 @@ public extension CollectionViewControllerProtocol { setupCoreCollectionView(newValue) } } - + var collectionViewController: UICollectionViewController { - guard let collection = AssociatedObject.getAssociated(self, key: &kAOCollectionVC) else { + guard let collection = AssociatedObject.getAssociated(self, key: &AOAssociatedKey.collectionVC) else { return setupCoreCollectionVC() } return collection @@ -55,13 +58,13 @@ public extension CollectionViewControllerProtocol { } private extension CollectionViewControllerProtocol { - + @discardableResult func setupCoreCollectionVC(_ collectionView: UICollectionView? = nil) -> UICollectionViewController { // Load Base view setupCoreView() - + // Create tableView based on user provided style let collectionVC = UICollectionViewController(collectionViewLayout: flowLayout) // had to reset vc's collectionView @@ -72,21 +75,21 @@ private extension CollectionViewControllerProtocol { if let collectionView = collectionView { setupCoreCollectionView(collectionView, controller: collectionVC) } - + // Add as child view controller self.addChild(collectionVC) self.mainView?.pin(view: collectionVC.collectionView, edgeOffsets: .zero) - - AssociatedObject.setAssociated(self, value: collectionVC, key: &kAOCollectionVC) - + + AssociatedObject.setAssociated(self, value: collectionVC, key: &AOAssociatedKey.collectionVC) + return collectionVC } - + func setupCoreCollectionView(_ localView: UICollectionView, controller: UICollectionViewController? = nil) { let collectionVC = controller ?? collectionViewController collectionVC.collectionView.removeSubviews() collectionVC.collectionView = localView - + // Add as child view controller self.mainView?.pin(view: localView, edgeOffsets: .zero) } diff --git a/Sources/CoreComponents/Contollers/ScrollViewControllerProtocol.swift b/Sources/CoreComponents/Contollers/ScrollViewControllerProtocol.swift index ee0a3b9..30d7e0a 100644 --- a/Sources/CoreComponents/Contollers/ScrollViewControllerProtocol.swift +++ b/Sources/CoreComponents/Contollers/ScrollViewControllerProtocol.swift @@ -12,17 +12,20 @@ import CoreUtility import Foundation import UIKit -private var kAOScrollVC = "k.FT.AO.ScrollViewController" +private enum ScrollViewAssociatedKey { + @nonobjc static var scrollVC = Int8(0) + // private var scrollVC = "k.FT.AO.ScrollViewController" +} public protocol ScrollViewControllerProtocol: ViewControllerProtocol { var scrollView: UIScrollView { get } } public extension ScrollViewControllerProtocol { - + var scrollView: UIScrollView { get { - guard let scroll = AssociatedObject.getAssociated(self, key: &kAOScrollVC) else { + guard let scroll = AssociatedObject.getAssociated(self, key: &ScrollViewAssociatedKey.scrollVC) else { return self.setupScrollView() } return scroll @@ -34,25 +37,25 @@ public extension ScrollViewControllerProtocol { } private extension ScrollViewControllerProtocol { - + @discardableResult func setupScrollView(_ local: UIScrollView = UIScrollView() ) -> UIScrollView { - + // Load Base view setupCoreView() - - if let scroll: UIScrollView = AssociatedObject.getAssociated(self, key: &kAOScrollVC) { + + if let scroll: UIScrollView = AssociatedObject.getAssociated(self, key: &ScrollViewAssociatedKey.scrollVC) { scroll.removeSubviews() - AssociatedObject.resetAssociated(self, key: &kAOScrollVC) + AssociatedObject.resetAssociated(self, key: &ScrollViewAssociatedKey.scrollVC) } - + if local.superview == nil { self.mainView?.pin(view: local, edgeOffsets: .zero) local.setupContentView(local.contentView) } - - AssociatedObject.setAssociated(self, value: local, key: &kAOScrollVC) - + + AssociatedObject.setAssociated(self, value: local, key: &ScrollViewAssociatedKey.scrollVC) + return local } } diff --git a/Sources/CoreComponents/Contollers/TableViewControllerProtocol.swift b/Sources/CoreComponents/Contollers/TableViewControllerProtocol.swift index 9f9b586..e89558e 100644 --- a/Sources/CoreComponents/Contollers/TableViewControllerProtocol.swift +++ b/Sources/CoreComponents/Contollers/TableViewControllerProtocol.swift @@ -17,13 +17,13 @@ public protocol TableViewControllerProtocol: ViewControllerProtocol { var tableView: UITableView { get } var tableViewController: UITableViewController { get set } var tableViewEdgeOffsets: UIEdgeInsets { get } - + func getCoreTableViewController() -> UITableViewController } // MARK: AssociatedKey private extension AssociatedKey { - static var kAOTableVC = "k.FT.AO.TableViewController" + static var kAOTableVC = Int8(0) // "k.FT.AO.TableViewController" } public extension TableViewControllerProtocol { @@ -31,13 +31,13 @@ public extension TableViewControllerProtocol { var tableStyle: UITableView.Style { .plain } var tableViewEdgeOffsets: UIEdgeInsets { .zero } var tableView: UITableView { self.tableViewController.tableView } - + func getCoreTableViewController() -> UITableViewController { let controller = UITableViewController(style: self.tableStyle) controller.tableView.estimatedRowHeight = UITableView.automaticDimension return controller } - + var tableViewController: UITableViewController { get { guard let table = AssociatedObject.getAssociated(self, key: &AssociatedKey.kAOTableVC) else { @@ -49,7 +49,7 @@ public extension TableViewControllerProtocol { setupCoreTableViewController(newValue) } } - + @discardableResult private func setupCoreTableViewController(_ controller: UITableViewController? = nil) -> UITableViewController { // Load Base view @@ -76,12 +76,12 @@ public extension UITableView { self.tableHeaderView = view // UIView.embededView(view: view) self.resizeSubviews(view: self.tableHeaderView) } - + func setTableFooterView(view: UIView?) { self.tableFooterView = view // UIView.embededView(view: view) self.resizeSubviews(view: self.tableFooterView) } - + private func resizeSubviews(view: UIView?) { view?.layoutIfNeeded() view?.frame.size = view?.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) ?? .zero diff --git a/Sources/CoreComponents/Contollers/UIViewController+Extension.swift b/Sources/CoreComponents/Contollers/UIViewController+Extension.swift index 8ea0138..331b408 100644 --- a/Sources/CoreComponents/Contollers/UIViewController+Extension.swift +++ b/Sources/CoreComponents/Contollers/UIViewController+Extension.swift @@ -14,7 +14,7 @@ extension UIViewController { var isBaseViewAdded: Bool { // If baseView is not added, then retun false - return (self.baseView?.superview != self.view && self.view != self.baseView) + (self.baseView?.superview != self.view && self.view != self.baseView) } // MARK: Utility @@ -25,20 +25,20 @@ extension UIViewController { } return self.baseView?.mainPinnedView } - + // MARK: Navigation Bar // default - left Button Actopm public var kleftButtonAction: Selector { #selector(UIViewController.leftButtonAction) } - + public var kRightButtonAction: Selector { #selector(UIViewController.rightButtonAction) } func configureBarButton(button: UIBarButtonItem?, defaultAction action: Selector) { guard let button = button else { return } - + if button.action == nil { button.action = action } @@ -46,7 +46,7 @@ extension UIViewController { button.target = self } } - + // MARK: Dissmiss Self model func self_dismissSelf(_ animated: Bool = true) { if navigationController != nil, navigationController?.viewControllers.first != self { @@ -71,12 +71,12 @@ extension UIViewController { self.view.addGestureRecognizer(touchAction) } } - + @objc public func endEditing() { self.view.endEditing(true) } - + // MARK: Keyboard Notifications // Registering for keyboard notification. public func registerKeyboardNotifications() { @@ -84,18 +84,18 @@ extension UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: self) NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide(_:)), name: UIResponder.keyboardDidHideNotification, object: self) } - + public func unregisterKeyboardNotifications() { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil) } - + /* UIKeyboardWillShow. */ @objc func keyboardWillShow(_ notification: Notification?) { // Optional Protocol implementation: intentionally empty } - + /* UIKeyboardDidHide. */ @objc func keyboardDidHide(_ notification: Notification?) { @@ -108,10 +108,10 @@ extension UIViewController { public var defaultAlertAction: UIAlertAction { UIAlertAction(title: "Ok", style: .default, handler: nil) } - + public func showAlert( title: String? = nil, message: String? = nil, action: UIAlertAction? = nil, actions: [UIAlertAction]? = nil) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - + // Add actions to alertView if let action = action { alert.addAction(action) @@ -126,12 +126,12 @@ extension UIViewController { // present alertView present(alert, animated: true, completion: nil) } - + // MARK: Activity indicator public func showActivityIndicator() { LoadingIndicator.show() } - + public func hideActivityIndicator(_ completionBlock: LoadingIndicator.CompletionBlock? = nil) { DispatchQueue.main.async { LoadingIndicator.hide(completionBlock) diff --git a/Sources/CoreComponents/Contollers/ViewControllerProtocol.swift b/Sources/CoreComponents/Contollers/ViewControllerProtocol.swift index aef9ada..6492631 100644 --- a/Sources/CoreComponents/Contollers/ViewControllerProtocol.swift +++ b/Sources/CoreComponents/Contollers/ViewControllerProtocol.swift @@ -13,7 +13,7 @@ import UIKit public protocol ViewControllerProtocol where Self: UIViewController { var modelStack: AnyObject? { get set } - + // Setup View func setupCoreView() // MARK: Navigation Bar @@ -40,10 +40,10 @@ public protocol ViewControllerProtocol where Self: UIViewController { } private extension AssociatedKey { - static var baseView = "baseView" - static var screenIdentifier = "screenIdentifier" - static var modelStack = "modelStack" - static var completionBlock = "completionBlock" + static var baseView = Int8(0) // "baseView" + static var screenIdentifier = Int8(1) // "screenIdentifier" + static var modelStack = Int8(2) // "modelStack" + static var completionBlock = Int8(3) // "completionBlock" } extension UIViewController: ViewControllerProtocol { @@ -56,7 +56,7 @@ extension UIViewController: ViewControllerProtocol { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.baseView) } } - + @IBOutlet public var topPinnedView: UIView? { get { @@ -66,7 +66,7 @@ extension UIViewController: ViewControllerProtocol { self.baseView?.topPinnedView = newValue } } - + @IBOutlet public var bottomPinnedView: UIView? { get { @@ -76,7 +76,7 @@ extension UIViewController: ViewControllerProtocol { self.baseView?.bottomPinnedView = newValue } } - + // Unquie Identifier for eachScreen public var screenIdentifier: String? { get { @@ -86,7 +86,7 @@ extension UIViewController: ViewControllerProtocol { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.screenIdentifier) } } - + // modelData that can be passed from previous controller public var modelStack: AnyObject? { get { @@ -96,37 +96,37 @@ extension UIViewController: ViewControllerProtocol { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.modelStack) } } - + // Setup baseView's topLayoutGuide by sending true in subControllers if needed @objc open func topSafeAreaLayoutGuide() -> Bool { true } - + @objc open func horizontalSafeAreaLayoutGuide() -> Bool { true } // Will dismiss Keyboard by tapping on any non-interative part of the view. @objc open func shouldDissmissKeyboardOnTap() -> Bool { true } - + public func setupCoreView() { if self.view == self.baseView { return } guard let rootView = self.view else { return } var isValidBaseView = false - + // Make it as Views RooView, if forget to map it in IB. if self.baseView != rootView, (rootView as? FTView) != nil { self.baseView = rootView as? FTView isValidBaseView = true } - + self.view = self.baseView // Setup baseView's topLayoutGuide & bottomLayoutGuide setupLayoutGuide() // To Dismiss keyboard on tap in view setupKeyboardTapRecognizer() - + // Reset rootView if !isValidBaseView { rootView.removeFromSuperview() @@ -144,18 +144,18 @@ extension UIViewController { self.navigationItem.leftBarButtonItem = leftButton self.navigationItem.rightBarButtonItem = rightButton } - + // MARK: Dissmiss Self model public func dismissSelf(_ animated: Bool = true) { self_dismissSelf(animated) } - + // MARK: default Nav-bar button actions @IBAction open func leftButtonAction() { dismissSelf() } - + @IBAction open func rightButtonAction() { // Optional Protocol implementation: intentionally empty @@ -198,7 +198,7 @@ private extension UIViewController { local?.bottomAnchor.constraint(equalTo: self.bottomLayoutGuide.topAnchor, constant: 0.0).isActive = true } } - + func setupTopSafeAreaLayoutGuide(_ local: UIView) { if #available(iOS 11.0, *) { local.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0.0).isActive = true diff --git a/Sources/CoreComponents/Contollers/WebViewControllerProtocol.swift b/Sources/CoreComponents/Contollers/WebViewControllerProtocol.swift index 2cd6f65..be5d057 100644 --- a/Sources/CoreComponents/Contollers/WebViewControllerProtocol.swift +++ b/Sources/CoreComponents/Contollers/WebViewControllerProtocol.swift @@ -12,7 +12,9 @@ import CoreUtility import UIKit import WebKit -private var kContentVC = "k.FT.AO.ContentViewController" +private extension AssociatedKey { + static var kContentVC = Int8(0) // "k.FT.AO.ContentViewController" +} public protocol WebViewControllerProtocol: ViewControllerProtocol { var contentView: WKWebView { get } @@ -20,7 +22,7 @@ public protocol WebViewControllerProtocol: ViewControllerProtocol { public extension WebViewControllerProtocol { var contentView: WKWebView { - get { AssociatedObject.getAssociated(self, key: &kContentVC) { self.setupContentView() }! } + get { AssociatedObject.getAssociated(self, key: &AssociatedKey.kContentVC) { self.setupContentView() }! } set { setupContentView(newValue) } } } @@ -33,14 +35,14 @@ private extension WebViewControllerProtocol { } // Load Base view setupCoreView() - if let scroll: WKWebView = AssociatedObject.getAssociated(self, key: &kContentVC) { + if let scroll: WKWebView = AssociatedObject.getAssociated(self, key: &AssociatedKey.kContentVC) { scroll.removeFromSuperview() - AssociatedObject.resetAssociated(self, key: &kContentVC) + AssociatedObject.resetAssociated(self, key: &AssociatedKey.kContentVC) } if local.superview == nil { self.mainView?.pin(view: local, edgeOffsets: .zero) } - AssociatedObject.setAssociated(self, value: local, key: &kContentVC) + AssociatedObject.setAssociated(self, value: local, key: &AssociatedKey.kContentVC) return local } } @@ -49,14 +51,14 @@ internal class WebViewControllerViewDelegate: NSObject, UIScrollViewDelegate { // MARK: - Shared delegate static var shared = WebViewControllerViewDelegate() var shouldHideNav = false - + var navigationController: UINavigationController? { UIWindow.topViewController?.navigationController } // MARK: - UIScrollViewDelegate func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { nil } - + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { @@ -64,7 +66,7 @@ internal class WebViewControllerViewDelegate: NSObject, UIScrollViewDelegate { self.setBarStatus(hidden: velocity.y > 0) } } - + func setBarStatus(hidden: Bool) { self.navigationController?.setNavigationBarHidden(hidden, animated: true) guard self.navigationController?.toolbarItems?.isEmpty ?? false else { return } diff --git a/Sources/CoreComponents/CoreComponents/UIBarButtonItem+Extension.swift b/Sources/CoreComponents/CoreComponents/UIBarButtonItem+Extension.swift index 3a2ea6d..db2165d 100644 --- a/Sources/CoreComponents/CoreComponents/UIBarButtonItem+Extension.swift +++ b/Sources/CoreComponents/CoreComponents/UIBarButtonItem+Extension.swift @@ -8,25 +8,25 @@ import UIKit public extension UIBarButtonItem { - + convenience init?(title: String? = nil, image: UIImage? = nil, action: Selector? = nil, itemType: UIBarButtonItem.SystemItem = .done, target: Any? = nil) { - + guard title != nil, image != nil else { self.init(barButtonSystemItem: itemType, target: target, action: action) return } - + let button = UIButton(type: .custom) button.titleLabel?.text = title button.imageView?.image = image if let buttonAction = action { button.addTarget(target, action: buttonAction, for: .touchUpInside) } - + self.init(customView: button) } } diff --git a/Sources/CoreComponents/CoreComponents/UIScrollView+Extension.swift b/Sources/CoreComponents/CoreComponents/UIScrollView+Extension.swift index 6b9a71b..f6ab340 100644 --- a/Sources/CoreComponents/CoreComponents/UIScrollView+Extension.swift +++ b/Sources/CoreComponents/CoreComponents/UIScrollView+Extension.swift @@ -15,7 +15,7 @@ import UIKit extension UIScrollView { private static let aoCollectionViewController = AssociatedObject(policy: .OBJC_ASSOCIATION_ASSIGN) - + @IBOutlet public weak var contentView: UIView! { get { @@ -34,7 +34,7 @@ extension UIScrollView { UIScrollView.aoCollectionViewController[self]?.removeFromSuperview() addContentView(view) } - + // Add selfSizing-View to subView @discardableResult func addContentView(_ view: UIView) -> UIView { diff --git a/Sources/CoreComponents/CoreComponents/UISegmentedControl+Extension.swift b/Sources/CoreComponents/CoreComponents/UISegmentedControl+Extension.swift index 212997f..7d73004 100644 --- a/Sources/CoreComponents/CoreComponents/UISegmentedControl+Extension.swift +++ b/Sources/CoreComponents/CoreComponents/UISegmentedControl+Extension.swift @@ -20,14 +20,14 @@ public extension UISegmentedControl { get { UISegmentedControl.aoHandler[self] } set { UISegmentedControl.aoHandler[self] = newValue } } - + convenience init(items: [Any], completionHandler: SegmentedHandler? ) { self.init(items: items) selectedSegmentIndex = 0 handler = completionHandler addTarget(self, action: #selector(UISegmentedControl.segmentedControlIndexChanged), for: .valueChanged) } - + @objc func segmentedControlIndexChanged() { if let completionBlock = UISegmentedControl.aoHandler[self] { diff --git a/Sources/CoreComponents/CoreComponents/WKWebView+Extension.swift b/Sources/CoreComponents/CoreComponents/WKWebView+Extension.swift index c220cfa..65b253e 100644 --- a/Sources/CoreComponents/CoreComponents/WKWebView+Extension.swift +++ b/Sources/CoreComponents/CoreComponents/WKWebView+Extension.swift @@ -17,7 +17,7 @@ public protocol WebViewContentProtocol { func pickerColor(textColor: UIColor, backgroundColor: UIColor) func fontSize(_ size: Float) func fontFamily(_ fontName: String?) - + func getTextSize(_ block: ((Float?) -> Void)?) func getHTMLColor(_ block: ((_ textColor: UIColor?, _ backgroungColor: UIColor?) -> Void)?) func getContentFontFamily(_ block: (([String]) -> Void)?) @@ -36,13 +36,13 @@ extension WKWebView { """ public static let getDocumentBody = "document.getElementsByTagName('body')[0]" public static let styleAppleFamily = "-apple-system\";" - + public static let styleFontFamily = ".style.fontFamily" public static let textSizeAdjust = ".style.webkitTextSizeAdjust" public static let backgroundColor = ".style.backgroundColor" public static let textColor = ".style.color" } - + private func removeGestureInWKContentView(_ view: [UIView]) { for subScrollView in view where type(of: subScrollView) == NSClassFromString("WKContentView") { for gesture in subScrollView.gestureRecognizers ?? [] { @@ -50,16 +50,16 @@ extension WKWebView { } } } - + public func setHideNavigationOnScroll(hide: Bool) { WebViewControllerViewDelegate.shared.shouldHideNav = hide } - + public func setScrollEnabled(enabled: Bool) { self.scrollView.isScrollEnabled = enabled self.scrollView.panGestureRecognizer.isEnabled = enabled self.scrollView.bounces = enabled - + for subview in self.subviews { if let subview = subview as? UIScrollView { subview.isScrollEnabled = enabled @@ -69,33 +69,33 @@ extension WKWebView { removeGestureInWKContentView(subview.subviews) } } - + @discardableResult public func loadHTMLBody(_ string: String, baseURL: URL? = nil) -> WKNavigation? { let body = Constants.loadHTMLBody.replacingOccurrences(of: Constants.bodyPlaceHolder, with: string) return self.loadHTMLString(body, baseURL: baseURL) } - + public func setContentFontSize(_ size: Float) { if size >= 10 { let js = Constants.getDocumentBody + Constants.textSizeAdjust + "= '\(size)%'" self.insertCSSString(jsString: js) } } - + public func setContentColor(textColor: UIColor? = nil, backgroundColor: UIColor? = nil) { - + if let bgHex = backgroundColor?.hexString() { let bgJS = Constants.getDocumentBody + Constants.backgroundColor + "= '\(bgHex)';" self.insertCSSString(jsString: bgJS) } - + if let fontHex = textColor?.hexString() { let fontJS = Constants.getDocumentBody + Constants.textColor + "= '\(fontHex)';" self.insertCSSString(jsString: fontJS) } } - + public func setContentFontFamily(_ fontName: String?) { // base document style var css = Constants.getDocumentBody + Constants.styleFontFamily + "= \"" @@ -117,11 +117,11 @@ extension WKWebView: WebViewContentProtocol { public func pickerColor(textColor: UIColor, backgroundColor: UIColor) { setContentColor(textColor: textColor, backgroundColor: backgroundColor) } - + public func fontSize(_ size: Float) { setContentFontSize(size) } - + public func fontFamily(_ fontName: String?) { setContentFontFamily(fontName) } @@ -136,7 +136,7 @@ public extension WKWebView { } } } - + func getHTMLColor(_ block: ((_ textColor: UIColor?, _ backgroungColor: UIColor?) -> Void)?) { guard block != nil else { return } var bgColor: UIColor? @@ -149,14 +149,14 @@ public extension WKWebView { block?(textColor, bgColor) } } - + // bg color evaluateJavaScript(regBG ?? "") { obj, _ in regBG = nil bgColor = self.getColor(obj as? String) eval() } - + // text color evaluateJavaScript(regText ?? "") { obj, _ in regText = nil @@ -164,7 +164,7 @@ public extension WKWebView { eval() } } - + func getContentFontFamily(_ block: (([String]) -> Void)?) { guard block != nil else { return } evaluateJavaScript(Constants.getDocumentBody + Constants.styleFontFamily) { obj, _ in @@ -173,7 +173,7 @@ public extension WKWebView { block?(fonts) } } - + // Converts string 'rgb(i, i, i)' into 'UIColor' private func getColor(_ color: String?) -> UIColor? { if var color = color { @@ -182,7 +182,7 @@ public extension WKWebView { } return nil } - + func insertCSSString(jsString: String) { self.evaluateJavaScript(jsString, completionHandler: nil) } diff --git a/Sources/CoreComponents/CustomComponents/FTView.swift b/Sources/CoreComponents/CustomComponents/FTView.swift index 500b8b6..34c4cd4 100644 --- a/Sources/CoreComponents/CustomComponents/FTView.swift +++ b/Sources/CoreComponents/CustomComponents/FTView.swift @@ -13,10 +13,10 @@ import Foundation import UIKit open class FTView: UIView { - + // Set as BaseView, so that "pin'ning" subViews wont alter the base position public var rootView = UIView() - + // Top portion of view @IBOutlet public var topPinnedView: UIView? { @@ -29,7 +29,7 @@ open class FTView: UIView { // On init'coder, mainView will be nil, but on loadView it will be allocated. // So using lazy, to make sure, mainView will not be nil, when accessed. private lazy var localMainPinnedView = UIView() - + @IBOutlet public var mainPinnedView: UIView! { get { @@ -49,7 +49,7 @@ open class FTView: UIView { } var isLoadedFromInterface = false - + public required init?(coder aDecoder: NSCoder) { isLoadedFromInterface = true super.init(coder: aDecoder) @@ -61,7 +61,7 @@ open class FTView: UIView { super.init(frame: frame) self.setupView() } - + func setupView() { self.mainPinnedView.backgroundColor = .clear self.rootView.backgroundColor = .clear @@ -72,7 +72,7 @@ open class FTView: UIView { self.pin(view: rootView, priority: kLayoutPriorityRequiredLow) self.restConstraints() } - + func restConstraints() { // Will be nil, when loaded from IB if self.rootView.superview == nil { @@ -86,7 +86,7 @@ open class FTView: UIView { self.topPinnedView?.removeAllConstraints() self.mainPinnedView.removeAllConstraints() self.bottomPinnedView?.removeAllConstraints() - + var viewArray = [UIView]() // Embed in Temp view to auto-size the view layout if let topPinnedView = self.topPinnedView { @@ -100,7 +100,7 @@ open class FTView: UIView { let tempView = UIView.embedView(contentView: bottomPinnedView) viewArray.append(tempView) } - + // Pin : Top and Sides and Bottom pinToRootView(viewArray: viewArray) // Pin : MainView to margin @@ -120,13 +120,13 @@ extension FTView { super.addSubview(view) } } - + func pinToRootView(viewArray: [UIView]) { // Pin : Top and Side - margin of the firstView to Root if let firstView = viewArray.first { rootView.pin(view: firstView, edgeInsets: [.top, .horizontal]) } - + // Make all subViews of sameSize, to auto-size the view layout if viewArray.count > 1 { rootView.stackView( @@ -135,7 +135,7 @@ extension FTView { edgeInsets: [.leadingMargin, .trailingMargin, .equalWidth] ) } - + // Pin : BottomMargin of the lastView to Root if let lastView = viewArray.last { rootView.pin(view: lastView, edgeInsets: .bottom) diff --git a/Sources/CoreComponents/LoadingIndicator/LoadingIndicator.swift b/Sources/CoreComponents/LoadingIndicator/LoadingIndicator.swift index 70ecc0d..16fbfbf 100755 --- a/Sources/CoreComponents/LoadingIndicator/LoadingIndicator.swift +++ b/Sources/CoreComponents/LoadingIndicator/LoadingIndicator.swift @@ -58,13 +58,13 @@ public class LoadingIndicator: UIView { private var (animated, canUpdated) = (true, false) private var title: String? private var speed = 1 - + private var config = LoaderConfig() { didSet { self.loadingView?.config = config } } - + @objc func rotated(notification: NSNotification) { let loader = LoadingIndicator.sharedInstance let height: CGFloat = UIScreen.main.bounds.size.height @@ -73,17 +73,17 @@ public class LoadingIndicator: UIView { loader.center = center loader.baseView?.frame = UIScreen.main.bounds } - + override public var frame: CGRect { didSet { self.update() } } - + public static func show(_ animated: Bool = true) { self.show(title: self.sharedInstance.config.title, animated: animated) } - + public static func show(title: String?, animated: Bool = true) { // Will fail if window is not allocated guard let currentWindow: UIWindow = UIApplication.shared.keyWindow else { return } @@ -92,56 +92,56 @@ public class LoadingIndicator: UIView { loader.animated = animated loader.title = title loader.update() - + NotificationCenter.default.addObserver( loader, selector: #selector( loader.rotated(notification:) ), name: UIDevice.orientationDidChangeNotification, object: nil ) - + let height: CGFloat = UIScreen.main.bounds.size.height let width: CGFloat = UIScreen.main.bounds.size.width let center = CGPoint(x: width / 2.0, y: height / 2.0) - + loader.center = center - + if loader.superview == nil { loader.baseView = UIView(frame: currentWindow.bounds) loader.baseView?.backgroundColor = loader.config.foregroundColor.withAlphaComponent(loader.config.foregroundAlpha) - + currentWindow.addSubview(loader.baseView!) currentWindow.addSubview(loader) loader.start() } } - + public static func hide(_ completion: CompletionBlock? = nil) { let loader = LoadingIndicator.sharedInstance NotificationCenter.default.removeObserver(loader) loader.stop(completion) } - + public static func setConfig(config: LoaderConfig) { let loader = LoadingIndicator.sharedInstance loader.config = config } - + func frameForSpinner() -> CGRect { let loadingViewSize = self.frame.size.width - (loaderSpinnerMarginSide * 2) - + if self.title == nil { let yOffset = (self.frame.size.height - loadingViewSize) / 2 return CGRect(origin: CGPoint(x: loaderSpinnerMarginSide, y: yOffset), size: CGSize(width: loadingViewSize, height: loadingViewSize)) } return CGRect(origin: CGPoint(x: loaderSpinnerMarginSide, y: loaderSpinnerMarginTop), size: CGSize(width: loadingViewSize, height: loadingViewSize)) } - + override init(frame: CGRect) { super.init(frame: frame) self.setup() } - + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } @@ -153,7 +153,7 @@ private extension LoadingIndicator { self.alpha = 0 self.update() } - + func start() { self.loadingView?.start() if self.animated { @@ -165,7 +165,7 @@ private extension LoadingIndicator { self.alpha = 1 } } - + func stop(_ completion: CompletionBlock? = nil) { if self.animated { UIView.animate( @@ -185,7 +185,7 @@ private extension LoadingIndicator { self.loadingView?.stop(completion) } } - + func update() { self.backgroundColor = self.config.backgroundColor self.layer.cornerRadius = self.config.cornerRadius @@ -197,7 +197,7 @@ private extension LoadingIndicator { else { self.loadingView?.frame = self.frameForSpinner() } - + let labelFrame = CGRect( origin: CGPoint(x: loaderTitleMargin, y: loaderSpinnerMarginTop + loadingViewSize), size: CGSize(width: self.frame.width - loaderTitleMargin * 2, height: 42.0) @@ -213,7 +213,7 @@ private extension LoadingIndicator { else { self.titleLabel?.frame = labelFrame } - + self.titleLabel?.font = self.config.titleTextFont self.titleLabel?.textColor = self.config.titleTextColor self.titleLabel?.text = self.title @@ -223,33 +223,33 @@ private extension LoadingIndicator { // MARK: LoadingView private class LoadingView: UIView { - + private var speed: Int? private var lineWidth: Float? private var lineTintColor: UIColor? private var backgroundLayer: CAShapeLayer? private var isSpinning: Bool? - + var config = LoaderConfig() { didSet { self.update() } } - + override init(frame: CGRect) { super.init(frame: frame) self.setup() } - + required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + // MARK: Setup loading view func setup() { self.backgroundColor = UIColor.clear self.lineWidth = fmaxf(Float(self.frame.size.width) * 0.025, 1) - + self.backgroundLayer = CAShapeLayer() self.backgroundLayer?.strokeColor = self.config.spinnerColor.cgColor self.backgroundLayer?.fillColor = self.backgroundColor?.cgColor @@ -257,44 +257,44 @@ private class LoadingView: UIView { self.backgroundLayer?.lineWidth = CGFloat(self.lineWidth!) self.layer.addSublayer(self.backgroundLayer!) } - + func update() { self.frame = CGRect(x: 0, y: 0, width: config.size, height: config.size) self.lineWidth = self.config.spinnerLineWidth self.speed = self.config.speed - + self.backgroundLayer?.lineWidth = CGFloat(self.lineWidth!) self.backgroundLayer?.strokeColor = self.config.spinnerColor.cgColor } - + // MARK: Draw Circle override func draw(_ rect: CGRect) { self.backgroundLayer?.frame = self.bounds } - + func drawBackgroundCircle(partial: Bool) { let startAngle = CGFloat.pi / CGFloat(2.0) var endAngle: CGFloat = (2.0 * CGFloat.pi) + startAngle - + let center = CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2) let radius = (CGFloat(self.bounds.size.width) - CGFloat(self.lineWidth!)) / CGFloat(2.0) - + let processBackgroundPath = UIBezierPath() processBackgroundPath.lineWidth = CGFloat(self.lineWidth!) - + if partial { endAngle = (1.8 * CGFloat.pi) + startAngle } - + processBackgroundPath.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true) self.backgroundLayer?.path = processBackgroundPath.cgPath } - + // MARK: Start and stop spinning func start() { self.isSpinning? = true self.drawBackgroundCircle(partial: true) - + let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotationAnimation.toValue = NSNumber(value: Double.pi * 2.0) rotationAnimation.duration = 1 @@ -302,10 +302,10 @@ private class LoadingView: UIView { rotationAnimation.repeatCount = HUGE self.backgroundLayer?.add(rotationAnimation, forKey: "rotationAnimation") } - + func stop(_ completion: LoadingIndicator.CompletionBlock? = nil) { self.drawBackgroundCircle(partial: false) - + self.backgroundLayer?.removeAllAnimations() self.isSpinning? = false completion?(true) diff --git a/Sources/CoreUtility/AppBundle/BundleURLScheme.swift b/Sources/CoreUtility/AppBundle/BundleURLScheme.swift index 92917bc..fc8bc44 100644 --- a/Sources/CoreUtility/AppBundle/BundleURLScheme.swift +++ b/Sources/CoreUtility/AppBundle/BundleURLScheme.swift @@ -10,18 +10,17 @@ import Foundation import UIKit open class BundleURLScheme { - + public static var osVersion: Float { NSString(string: UIDevice.current.systemVersion).floatValue } - + public static func isRegisteredURLScheme(scheme: String) -> Bool { if let bundleURLTypes = Bundle.main.infoDictionary?["CFBundleURLTypes"] as? NSArray { for urlType in bundleURLTypes { if let urlSchemes = (urlType as? NSDictionary)?.value(forKey: "CFBundleURLSchemes") as? NSArray, - urlSchemes.contains(scheme) - { + urlSchemes.contains(scheme) { return true } } diff --git a/Sources/CoreUtility/ClassReflection/InstanceMethodSwizzling.swift b/Sources/CoreUtility/ClassReflection/InstanceMethodSwizzling.swift index 73d88de..c51a56d 100644 --- a/Sources/CoreUtility/ClassReflection/InstanceMethodSwizzling.swift +++ b/Sources/CoreUtility/ClassReflection/InstanceMethodSwizzling.swift @@ -18,7 +18,7 @@ public let instanceMethodSwizzling: (AnyClass, Selector, Selector) -> Void = { k method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!) ) - + if didAddMethod { class_replaceMethod( klass, @@ -29,5 +29,5 @@ public let instanceMethodSwizzling: (AnyClass, Selector, Selector) -> Void = { k } else { method_exchangeImplementations(originalMethod!, swizzledMethod!) - } + } } diff --git a/Sources/CoreUtility/ClassReflection/Reflection.swift b/Sources/CoreUtility/ClassReflection/Reflection.swift index 5a0c28e..9a64eb6 100644 --- a/Sources/CoreUtility/ClassReflection/Reflection.swift +++ b/Sources/CoreUtility/ClassReflection/Reflection.swift @@ -17,14 +17,14 @@ public protocol ReflectionProtocol { - returns: The string representation of the class (name of the bundle dot name of the class) */ static func classNameAsString(_ obj: Any) -> String? - + /** Get the swift Class type from a string - parameter className: The string representation of the class (name of the bundle dot name of the class) - returns: The Class type */ static func swiftClassTypeFromString(_ className: String) -> AnyClass? - + /** Get the swift Class from a string - parameter className: The string representation of the class (name of the bundle dot name of the class) diff --git a/Sources/CoreUtility/Extensions/ActionBlock.swift b/Sources/CoreUtility/Extensions/ActionBlock.swift index e4985f3..021dc27 100644 --- a/Sources/CoreUtility/Extensions/ActionBlock.swift +++ b/Sources/CoreUtility/Extensions/ActionBlock.swift @@ -14,7 +14,7 @@ public typealias ActionWithObjectBlock = (_ object: AnyObject?) -> Swift.Void public extension UIControl { private struct AssociatedKey { - static var actionBlockTapped = "actionBlockTapped" + static var actionBlockTapped = Int8(0) // "actionBlockTapped" } func addTapActionBlock(_ actionBlock: @escaping ActionBlock) { diff --git a/Sources/CoreUtility/Extensions/AttributedLabelProtocol.swift b/Sources/CoreUtility/Extensions/AttributedLabelProtocol.swift index 746cc09..b749bbd 100644 --- a/Sources/CoreUtility/Extensions/AttributedLabelProtocol.swift +++ b/Sources/CoreUtility/Extensions/AttributedLabelProtocol.swift @@ -26,15 +26,15 @@ protocol AttributedLabelProtocol where Self: UILabel { } private extension AssociatedKey { - static var textContainer = "textContainer" - static var layoutManager = "layoutManager" - static var styleProperties = "styleProperties" - - static var linkRanges = "linkRanges" - static var islinkDetectionEnabled = "islinkDetectionEnabled" - static var isLinkUnderLineEnabled = "isLinkUnderLineEnabled" - static var linkHandler = "linkHandler" - static var tapGestureRecognizer = "tapGestureRecognizer" + static var textContainer = Int8(0) // "textContainer" + static var layoutManager = Int8(1) // "layoutManager" + static var styleProperties = Int8(2) // "styleProperties" + + static var linkRanges = Int8(3) // "linkRanges" + static var islinkDetectionEnabled = Int8(4) // "islinkDetectionEnabled" + static var isLinkUnderLineEnabled = Int8(5) // "isLinkUnderLineEnabled" + static var linkHandler = Int8(6) // "linkHandler" + static var tapGestureRecognizer = Int8(7) // "tapGestureRecognizer" } extension UILabel: AttributedLabelProtocol { @@ -42,18 +42,18 @@ extension UILabel: AttributedLabelProtocol { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.linkRanges) } set { AssociatedObject<[LinkHandlerModel]>.setAssociated(self, value: newValue, key: &AssociatedKey.linkRanges) } } - + // LabelThemeProtocol public var islinkDetectionEnabled: Bool { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.islinkDetectionEnabled) { true }! } set { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.islinkDetectionEnabled) } } - + public var isLinkUnderLineEnabled: Bool { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.isLinkUnderLineEnabled) { false }! } set { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.isLinkUnderLineEnabled) } } - + public var linkHandler: LabelLinkHandler? { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.linkHandler) } set { @@ -62,15 +62,15 @@ extension UILabel: AttributedLabelProtocol { self.addGestureRecognizer(self.tapGestureRecognizer) } } - + public var tapGestureRecognizer: UITapGestureRecognizer { AssociatedObject.getAssociated(self, key: &AssociatedKey.tapGestureRecognizer) { UILabel.tapGesture(targer: self) }! } - + public var layoutManager: NSLayoutManager { AssociatedObject.getAssociated(self, key: &AssociatedKey.layoutManager) { NSLayoutManager() }! } - + public var textContainer: NSTextContainer { let local: NSTextContainer = AssociatedObject.getAssociated(self, key: &AssociatedKey.textContainer) { self.getTextContainer() }! local.lineFragmentPadding = 0.0 @@ -81,12 +81,12 @@ extension UILabel: AttributedLabelProtocol { local.size = self.bounds.size return local } - + public var styleProperties: AttributedDictionary { get { AssociatedObject.getAssociated(self, key: &AssociatedKey.styleProperties) { self.defaultStyleProperties }! } set { AssociatedObject.setAssociated(self, value: newValue, key: &AssociatedKey.styleProperties) } } - + public var htmlText: String { get { "" } set { updateWithHtmlString(text: newValue) } @@ -102,18 +102,18 @@ extension UILabel: OptionalLayoutSubview { updateWithHtmlString(text: newValue) } } - + private static func tapGesture(targer: AnyObject?) -> UITapGestureRecognizer { UITapGestureRecognizer(target: targer, action: #selector(UILabel.tapGestureRecognized(_:))) } - + @objc func tapGestureRecognized(_ gesture: UITapGestureRecognizer) { if self == gesture.view, let link = self.didTapAttributedText(gesture)?.first { self.linkHandler?(link) } } - + fileprivate var offsetXDivisor: CGFloat { switch self.textAlignment { case .center: return 0.5 @@ -121,19 +121,19 @@ extension UILabel: OptionalLayoutSubview { default: return 0.0 } } - + private func getTextContainer() -> NSTextContainer { let container = NSTextContainer() container.replaceLayoutManager(self.layoutManager) self.layoutManager.addTextContainer(container) return container } - + private var defaultStyleProperties: AttributedDictionary { let paragrahStyle = NSMutableParagraphStyle() paragrahStyle.alignment = self.textAlignment paragrahStyle.lineBreakMode = self.lineBreakMode - + var properties: AttributedDictionary = [ .paragraphStyle: paragrahStyle, .backgroundColor: self.backgroundColor ?? UIColor.clear @@ -154,13 +154,13 @@ extension UILabel { let att = text?.htmlAttributedString() self.updateTextWithAttributedString(attributedString: att) } - + func updateTextContainerSize() { var localSize = self.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) localSize.width = min(localSize.width, self.preferredMaxLayoutWidth) self.frame = CGRect(origin: frame.origin, size: localSize) } - + func updateTextWithAttributedString(attributedString: NSAttributedString?) { if let attributedString = attributedString { let sanitizedString = self.sanitizeAttributedString(attributedString: attributedString) @@ -172,22 +172,22 @@ extension UILabel { } layoutTextView() } - + func updateLinkInText(attributedString: NSMutableAttributedString) { self.linkRanges = LinkHandlerModel.appendLink(attributedString: attributedString) } - + // MARK: Container SetUp func layoutTextView() { updateTextContainerSize() layoutView() } - + public func didTapAttributedText(_ gesture: UIGestureRecognizer) -> [LinkHandlerModel]? { let indexOfCharacter = layoutManager.indexOfCharacter(self, touchLocation: gesture.location(in: self)) return self.linkRanges?.filter { $0.linkRange.contains(indexOfCharacter) } } - + // MARK: Text Sanitizing func sanitizeAttributedString(attributedString: NSAttributedString) -> NSMutableAttributedString { guard attributedString.length != 0 else { return attributedString.mutableString() } @@ -207,7 +207,7 @@ extension NSLayoutManager { func indexOfCharacter(_ label: UILabel, touchLocation: CGPoint) -> Int { let textContainer = label.textContainer let textStorage = NSTextStorage(attributedString: label.attributedText ?? NSAttributedString(string: "")) - textStorage.addLayoutManager(self) + textStorage.addLayoutManager(self) let (labelSize, textBoundingBox) = (label.bounds.size, self.usedRect(for: textContainer)) let offsetX = (labelSize.width - textBoundingBox.size.width) * label.offsetXDivisor - textBoundingBox.origin.x let offsetY = (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y diff --git a/Sources/CoreUtility/Extensions/ConfigurableCell+Extension.swift b/Sources/CoreUtility/Extensions/ConfigurableCell+Extension.swift index 86232de..ed93038 100644 --- a/Sources/CoreUtility/Extensions/ConfigurableCell+Extension.swift +++ b/Sources/CoreUtility/Extensions/ConfigurableCell+Extension.swift @@ -24,7 +24,7 @@ fileprivate extension UIView { let errorString = "Unable to dequeue cell of type \(type) with reuse identifier '\(defaultReuseIdentifier)'" return .invalidReuseIdentifier(message: errorString) } - + static func loadFromNibError(nibName: String) -> ViewLoadingError { let errorString = "\(self) has not been initialized properly from nib \(nibName)" return .loadNibInitialized(message: errorString) @@ -40,18 +40,18 @@ public extension UIView { static var defaultReuseIdentifier: String { "\(self)ID" } - + // Retruns true if nib file is avaialble static var hasNib: Bool { Bundle(for: self).path(forResource: defaultNibName, ofType: "nib") != nil } - + // Get view based on once's class name static func getNib(nibName: String? = nil, bundle: Bundle? = nil) -> UINib { let nibName = nibName ?? defaultNibName return UINib(nibName: nibName, bundle: bundle ?? Bundle(for: self)) } - + // Retruns first view from the nib file static func loadNibFromBundle(_ nibName: String? = nil, bundle: Bundle? = nil, @@ -72,12 +72,12 @@ public extension UITableViewCell { static func registerClass(for tableView: UITableView, reuseIdentifier: String? = nil) { tableView.register(self, forCellReuseIdentifier: reuseIdentifier ?? defaultReuseIdentifier) } - + static func registerNib(for tableView: UITableView, reuseIdentifier: String? = nil, bundle: Bundle? = nil) { let nib = Self.getNib(bundle: bundle) tableView.register(nib, forCellReuseIdentifier: reuseIdentifier ?? defaultReuseIdentifier) } - + // dequeue cell for the class static func dequeue(from tableView: UITableView, for indexPath: IndexPath, identifier: String? = nil) throws -> T { let identifier = identifier ?? defaultReuseIdentifier @@ -93,12 +93,12 @@ public extension UICollectionViewCell { static func registerClass(for collectionView: UICollectionView, reuseIdentifier: String? = nil) { collectionView.register(self, forCellWithReuseIdentifier: reuseIdentifier ?? defaultReuseIdentifier) } - + static func registerNib(for collectionView: UICollectionView, reuseIdentifier: String? = nil, bundle: Bundle? = nil) { let nib = Self.getNib(bundle: bundle) collectionView.register(nib, forCellWithReuseIdentifier: reuseIdentifier ?? defaultReuseIdentifier) } - + // dequeue cell for the class static func dequeue(from collectionView: UICollectionView, for indexPath: IndexPath, reuseIdentifier: String? = nil) throws -> T { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier ?? defaultReuseIdentifier, for: indexPath) as? T else { @@ -118,7 +118,7 @@ public extension UICollectionReusableView { let identifier = reuseIdentifer(reuseIdentifier: reuseIdentifier, elementKind: elementKind) collectionView.register(self, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: identifier) } - + // register a class for the collectionView's SupplementaryViewOfKind static func registerNib(for collectionView: UICollectionView, forSupplementaryViewOfKind elementKind: String, @@ -128,7 +128,7 @@ public extension UICollectionReusableView { let identifier = reuseIdentifer(reuseIdentifier: reuseIdentifier, elementKind: elementKind) collectionView.register(nib, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: identifier) } - + // dequeue cell for the class static func dequeue(from collectionView: UICollectionView, ofKind elementKind: String, for indexPath: IndexPath, reuseIdentifier: String? = nil) throws -> T { let identifier = reuseIdentifer(reuseIdentifier: reuseIdentifier, elementKind: elementKind) diff --git a/Sources/CoreUtility/Extensions/Dictionary+Extension.swift b/Sources/CoreUtility/Extensions/Dictionary+Extension.swift index 0ddb75d..f506905 100644 --- a/Sources/CoreUtility/Extensions/Dictionary+Extension.swift +++ b/Sources/CoreUtility/Extensions/Dictionary+Extension.swift @@ -20,11 +20,11 @@ public extension Dictionary { // Operator '+' Overloading public func + (left: [K: V], right: [K: V]) -> [K: V] { var computedValue = left - + for (k, v) in right { var superObject: [K: V]? = left[k] as? [K: V] let subObject = v as? [K: V] - + if subObject != nil, superObject != nil { superObject! += subObject! computedValue[k]! = (superObject as? V)! @@ -33,7 +33,7 @@ public func + (left: [K: V], right: [K: V]) -> [K: V] { computedValue[k] = v } } - + return computedValue } diff --git a/Sources/CoreUtility/Extensions/LinkDetectionProtocol.swift b/Sources/CoreUtility/Extensions/LinkDetectionProtocol.swift index d4abfb6..32ae3fb 100644 --- a/Sources/CoreUtility/Extensions/LinkDetectionProtocol.swift +++ b/Sources/CoreUtility/Extensions/LinkDetectionProtocol.swift @@ -12,22 +12,22 @@ import UIKit public protocol LinkDetectionProtocol { // Get list of LinkDetection from the String static func getURLLinkRanges(_ text: String) -> [LinkHandlerModel] - + // Get list of LinkDetection from the NSAttributedString static func getURLLinkRanges(_ text: NSAttributedString) -> [LinkHandlerModel] - + /* * Eg.) Hi #wellcome thanks. * "#wellcome" is the detected-link */ static func getHashTagRanges(_ text: String) -> [LinkHandlerModel] - + /* * Eg.) Hi #wellcome thanks. * "#wellcome" is the detected-link */ static func appendLink(attributedString: NSMutableAttributedString) -> [LinkHandlerModel] - + // TODO: Themes static func getStyleProperties(forLink link: LinkHandlerModel) -> AttributedDictionary } @@ -38,17 +38,17 @@ public class LinkHandlerModel { case url case hashTag } - + public var linkType: LinkType public var linkRange: NSRange public var linkURL: URL - + init(linkType: LinkType, linkRange: NSRange, linkURL: URL) { self.linkType = linkType self.linkRange = linkRange self.linkURL = linkURL } - + public var description: String { "(Type: \(self.linkType), Range: -location \(self.linkRange.location), -length \(self.linkRange.length), URL: \(self.linkURL))" } @@ -61,12 +61,12 @@ extension LinkHandlerModel: LinkDetectionProtocol { public extension LinkDetectionProtocol { // Get list of LinkDetection from the String static func getURLLinkRanges(_ text: String) -> [LinkHandlerModel] { - + var rangeOfURL = [LinkHandlerModel]() - + let types: NSTextCheckingResult.CheckingType = [ .link, .phoneNumber] let detector = try? NSDataDetector(types: types.rawValue) - + let range = text.nsRange() detector?.enumerateMatches(in: text, options: [], range: range) { result, _, _ in if @@ -76,10 +76,10 @@ public extension LinkDetectionProtocol { rangeOfURL.append(dec) } } - + return rangeOfURL } - + // Get list of LinkDetection from the NSAttributedString static func getURLLinkRanges(_ text: NSAttributedString) -> [LinkHandlerModel] { let searchKey = NSAttributedString.Key("NSLink") @@ -94,18 +94,18 @@ public extension LinkDetectionProtocol { } } } - + return rangeOfURL } - + /* * Eg.) Hi #wellcome thanks. * "#wellcome" is the detected-link */ static func getHashTagRanges(_ text: String) -> [LinkHandlerModel] { - + var rangeOfURL = [LinkHandlerModel]() - + // Hi #wellcome thanks. // Here, "#wellcome" is retuned text.enumerate(pattern: "(? [LinkHandlerModel] { - + var links = [LinkHandlerModel]() - + // HTTP links let urlLinks = Self.getURLLinkRanges(attributedString) links.insert(contentsOf: urlLinks, at: 0) - + links.forEach { link in let att = getStyleProperties(forLink: link) attributedString.addAttributes(att, range: link.linkRange) } - + // Hash Tags let hashLinks = Self.getHashTagRanges(attributedString.string) links.insert(contentsOf: hashLinks, at: 0) - + hashLinks.forEach { link in let att = getStyleProperties(forLink: link) attributedString.addAttributes(att, range: link.linkRange) } - + return links } - + // TODO: Themes static func getStyleProperties(forLink link: LinkHandlerModel) -> AttributedDictionary { var properties: AttributedDictionary = [ diff --git a/Sources/CoreUtility/Extensions/String+Extension.swift b/Sources/CoreUtility/Extensions/String+Extension.swift index b643e55..bb7df96 100644 --- a/Sources/CoreUtility/Extensions/String+Extension.swift +++ b/Sources/CoreUtility/Extensions/String+Extension.swift @@ -39,7 +39,7 @@ public extension String { } return false } - + func stripHTML() -> String { self.replacingOccurrences(of: RegularExpression.htmlFormat, with: "", options: .regularExpression, range: nil) } @@ -50,7 +50,7 @@ public extension String { guard let data = data(using: .utf8, allowLossyConversion: true) else { return NSMutableAttributedString() } - + do { return try NSMutableAttributedString( data: data, @@ -76,14 +76,14 @@ public extension String { func hexColor() -> UIColor? { UIColor.hexColor(self) } - + // MARK: String Size // Remove's 'sting' from self -> and retruns new 'String' // Original value is not affected func trimming(_ string: String) -> String { self.replacingOccurrences(of: string, with: "") } - + @discardableResult mutating func trimming(_ strings: [String]) -> String { strings.forEach { str in @@ -91,7 +91,7 @@ public extension String { } return self } - + // Remove prefix "string" mutating func trimPrefix(_ string: String) { while self.hasPrefix(string) { @@ -100,7 +100,7 @@ public extension String { } } } - + func trimingPrefix(_ string: String) -> String { var obj = String(self) while obj.hasPrefix(string) { @@ -110,24 +110,24 @@ public extension String { } return obj } - + // Range func nsRange(from: Int = 0) -> NSRange { NSRange(location: from, length: self.count) } - + // Get subString within the 'range' func substring(with range: NSRange) -> String? { (self as NSString).substring(with: range) as String? } - + // Get subString 'from-index' to 'to-index' func substring(from: Int, to: Int) -> String? { let range = NSRange(location: from, length: to - from) let substring = self.substring(with: range) return substring } - + // Verify if self contains a subString func contains(_ find: String) -> Bool { self.range(of: find) != nil @@ -140,25 +140,25 @@ public extension String { guard !self.isEmpty else { return .zero } - + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = lineBreakMode - + let attributes: AttributedDictionary = [ .font: font, .paragraphStyle: paragraphStyle ] - + let attributedString = NSAttributedString(string: self, attributes: attributes) let size = attributedString.boundingRect( with: constrainedSize, options: [.usesDeviceMetrics, .usesLineFragmentOrigin, .usesFontLeading], context: nil ).size - + return ceil(size: size) } - + // MARK: JSON // Loading Data from given Path func jsonContentAtPath() throws -> T? { @@ -199,7 +199,7 @@ public extension String { } return Bundle.main.resourceURL?.appendingPathComponent(value) } - + func bundle() -> Bundle? { if let bundleURL = self.bundleURL() { return Bundle(url: bundleURL) @@ -213,7 +213,7 @@ public extension NSAttributedString { func nsRange(from: Int = 0) -> NSRange { NSRange(location: from, length: self.length) } - + func mutableString() -> NSMutableAttributedString { if let value = self.mutableCopy() as? NSMutableAttributedString { return value diff --git a/Sources/CoreUtility/Extensions/UIColor+Extension.swift b/Sources/CoreUtility/Extensions/UIColor+Extension.swift index 4cb7d7f..0bf1e4c 100644 --- a/Sources/CoreUtility/Extensions/UIColor+Extension.swift +++ b/Sources/CoreUtility/Extensions/UIColor+Extension.swift @@ -41,13 +41,13 @@ public extension UIColor { convenience init(red: UInt64, green: UInt64, blue: UInt64, a: CGFloat = 1.0) { self.init(red: CGFloat(red) / 255, green: CGFloat(green) / 255, blue: CGFloat(blue) / 255, alpha: a) } - + // MARK: rgb: UInt64, a: CGFloat // UIColor(rgb: 13_158_600, a: 0.5) --> "#C8C8C87F" convenience init(rgb: UInt64, a: CGFloat = 1.0) { self.init(red: (rgb >> 16 & 0xFF), green: (rgb >> 8 & 0xFF), blue: (rgb >> 0 & 0xFF), a: a) } - + // UIColor(red: 200, green: 200, blue: 200, a: 0.5) --> "#C8C8C8" func hexString() -> String { var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 0.0, 0.0) @@ -55,14 +55,14 @@ public extension UIColor { let rgb: Int = (Int)(r * 255) << 16 | (Int)(g * 255) << 8 | (Int)(b * 255) << 0 return String(format: "#%06x", rgb) } - + // MARK: rgba: UInt64 // UIColor(rgb: 13_158_600) --> "#C8C8C8FF" convenience init(rgba: UInt64) { let (r, g, b, a) = (rgba >> 24 & 0xFF, rgba >> 16 & 0xFF, rgba >> 8 & 0xFF, rgba >> 0 & 0xFF) self.init(red: r, green: g, blue: b, a: CGFloat(a) / 255) } - + // UIColor(red: 200, green: 200, blue: 200, a: 1.0) --> "#C8C8C8FF" // UIColor(red: 200, green: 200, blue: 200, a: 0.5) --> "#C8C8C87F" func hexAlphaString() -> String { @@ -71,7 +71,7 @@ public extension UIColor { let rgba: UInt64 = (UInt64)(r * 255) << 24 | (UInt64)(g * 255) << 16 | (UInt64)(b * 255) << 8 | (UInt64)(a * 255) return String(format: "#%08x", rgba) } - + // MARK: hexString: String: "#C8C8C87F" // "#C8C8C87F" --> UIColor(red: 200, green: 200, blue: 200, a: 0.5) static func hexColor(_ hexString: String) -> UIColor? { @@ -94,19 +94,19 @@ public extension UIColor { return nil } } - + func generateImage(opacity: CGFloat = 1, contextSize: CGSize = CGSize(width: 1, height: 1), contentsScale: CGFloat = CGFloat.greatestFiniteMagnitude) -> UIImage? { let rect = CGRect(origin: .zero, size: contextSize) - + if contentsScale == CGFloat.greatestFiniteMagnitude { UIGraphicsBeginImageContext(contextSize) } else { UIGraphicsBeginImageContextWithOptions(contextSize, false, contentsScale) } - + if let context = UIGraphicsGetCurrentContext() { context.setFillColor(self.cgColor) context.setAlpha(opacity) @@ -125,7 +125,7 @@ public extension UIImage { } return nil } - + func getColor(a: CGFloat = -10) -> UIColor? { guard let pixelData = self.cgImage?.dataProvider?.data, let data = CFDataGetBytePtr(pixelData) else { return nil } @@ -135,7 +135,7 @@ public extension UIImage { let green = data[(pixelInfo + 1)] let blue = data[pixelInfo + 2] let alpha = data[pixelInfo + 3] - + return UIColor(red: UInt64(red), green: UInt64(green), blue: UInt64(blue), a: (a > 0 ? a : CGFloat(alpha)) / 255.0) } } diff --git a/Sources/CoreUtility/Extensions/UIView+Extension.swift b/Sources/CoreUtility/Extensions/UIView+Extension.swift index cc1deef..5dd63e3 100644 --- a/Sources/CoreUtility/Extensions/UIView+Extension.swift +++ b/Sources/CoreUtility/Extensions/UIView+Extension.swift @@ -26,7 +26,7 @@ public extension UIView { // Retrun contentView return contentView } - + // Add 'contentView' as subView and pin the View to all edges static func embedView(contentView: UIView) -> UIView { let local = self.init() @@ -36,14 +36,14 @@ public extension UIView { local.pin(view: contentView, edgeInsets: [.equalWidth, .top]) return local } - + // Remove all subViews func removeSubviews() { for subview in subviews { subview.removeFromSuperview() } } - + // Remove all of its Constraints func removeAllConstraints() { var cont: [NSLayoutConstraint] = self.constraints diff --git a/Sources/CoreUtility/Extensions/UIView+Utiltiy.swift b/Sources/CoreUtility/Extensions/UIView+Utiltiy.swift index a864793..b8e942a 100644 --- a/Sources/CoreUtility/Extensions/UIView+Utiltiy.swift +++ b/Sources/CoreUtility/Extensions/UIView+Utiltiy.swift @@ -15,63 +15,63 @@ public extension UIView { self.layer.borderWidth = borderWidth self.layer.borderColor = color.cgColor } - + // calculate - systemLayoutSizeFitting func compressedSize(_ size: CGSize = .zero, _ offSet: CGSize = .zero, widthPriority: UILayoutPriority = .required, heightPriority: UILayoutPriority = .fittingSizeLevel ) -> CGSize { - + if size == .zero { let compressedSize = self.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) return compressedSize } - + let size = self.systemLayoutSizeFitting( size, withHorizontalFittingPriority: widthPriority, verticalFittingPriority: heightPriority ) - + return CGSize(width: size.width - offSet.width, height: size.height - offSet.height) } - + // Find subView based on output type func findInSubView() -> T? { for val in self.subviews.compactMap({ $0 }) { if val is T { return val as? T } - else if !val.subviews.isEmpty, let subType: T? = val.findInSubView() { + else if !val.subviews.isEmpty, let subType: T? = val.findInSubView() { return subType } } return nil } - + // Find all UIImageView with 'height <= 1' func findShadowImage() -> [UIImageView]? { var imgs: [UIImageView] = [] if self is UIImageView && self.bounds.height <= 1, let selfImamge = self as? UIImageView { return [selfImamge] } - + for subview in self.subviews { if let imageViews = subview.findShadowImage(), !imageViews.isEmpty { imgs.append(contentsOf: imageViews) } } - + return imgs } - + // Remove all UIImageView's with 'height <= 1' func hideShadowImage() { self.findShadowImage()?.forEach { shadowImageView in shadowImageView.isHidden = true } } - + // MARK: Container SetUp func layoutView() { setNeedsUpdateConstraints() diff --git a/Sources/CoreUtility/Extensions/URL+Download.swift b/Sources/CoreUtility/Extensions/URL+Download.swift index b301791..0dc5d03 100644 --- a/Sources/CoreUtility/Extensions/URL+Download.swift +++ b/Sources/CoreUtility/Extensions/URL+Download.swift @@ -9,7 +9,7 @@ import Foundation import UIKit -public extension URL { +public extension URL { /** Download Image from async in background - parameter allowCache: Catches image localy based on URL @@ -24,7 +24,7 @@ public extension URL { comletionHandler?(image) return } - + let updateCache: ((UIImage?) -> Void) = { image in guard let image = image else { // remove cached image @@ -37,7 +37,7 @@ public extension URL { // After Image download compeltion comletionHandler?(image) } - + // Setup URLSession let task = session.dataTask(with: self) { data, response, error in // validate for proper data and imageType diff --git a/Sources/CoreUtility/Generic/AssociatedObject.swift b/Sources/CoreUtility/Generic/AssociatedObject.swift index 1fe6e1a..b4f2c77 100644 --- a/Sources/CoreUtility/Generic/AssociatedObject.swift +++ b/Sources/CoreUtility/Generic/AssociatedObject.swift @@ -10,22 +10,23 @@ import Foundation import UIKit public enum AssociatedKey { - public static var defaultKey = "AssociatedKey.defaultKey" + @nonobjc static var defaultKey = Int8(0) +// public static var defaultKey = "AssociatedKey.defaultKey" } // Generic way of storing values on runtime public final class AssociatedObject { - + private var aoPolicy: objc_AssociationPolicy public init(policy aoPolicy: objc_AssociationPolicy) { self.aoPolicy = aoPolicy } - + public convenience init() { self.init(policy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - + public subscript(instance: AnyObject) -> T? { get { objc_getAssociatedObject(instance, Unmanaged.passUnretained(self).toOpaque()) as? T @@ -51,7 +52,7 @@ public final class AssociatedObject { } return getValue(instance, key: &AssociatedKey.defaultKey, defaultValueBlock: defaultValue) } - + private static func getValue(_ instance: Any, key: UnsafeRawPointer, defaultValueBlock: (() -> T)? = nil) -> T? { guard let value = objc_getAssociatedObject(instance, key) as? T else { if let defaultValue: T = defaultValueBlock?() { @@ -60,10 +61,10 @@ public final class AssociatedObject { } return nil } - + return value } - + // reset Associated public static func resetAssociated(_ instance: Any, key: UnsafeRawPointer? = nil) { if let key = key { diff --git a/Sources/CoreUtility/Generic/FlattenIterator.swift b/Sources/CoreUtility/Generic/FlattenIterator.swift index 6423821..837f178 100644 --- a/Sources/CoreUtility/Generic/FlattenIterator.swift +++ b/Sources/CoreUtility/Generic/FlattenIterator.swift @@ -22,10 +22,10 @@ extension Dictionary: FlattenIterator { } public extension FlattenIterator { - + @discardableResult mutating func stripNilElements() -> Self { - + // Sub-Elements let stripSubElements = { (_ val: inout Any) -> Any in if var asJson = val as? [String: Any?] { @@ -36,7 +36,7 @@ public extension FlattenIterator { } return val } - + // swiftlint:disable force_unwrapping // swiftlint:disable force_cast // Dic @@ -49,7 +49,7 @@ public extension FlattenIterator { } // Array else if var dicArray = self as? [Any?] { - dicArray = dicArray.filter({ $0 != nil }) + dicArray = dicArray.filter { $0 != nil } self = dicArray.map { val -> Any in var value = val return stripSubElements(&value!) @@ -57,7 +57,7 @@ public extension FlattenIterator { } // swiftlint:enable force_unwrapping // swiftlint:enable force_cast - + return self } } diff --git a/Sources/CoreUtility/LayoutConstraint/ViewLayoutConstraint.swift b/Sources/CoreUtility/LayoutConstraint/ViewLayoutConstraint.swift index a90d1c7..1772290 100644 --- a/Sources/CoreUtility/LayoutConstraint/ViewLayoutConstraint.swift +++ b/Sources/CoreUtility/LayoutConstraint/ViewLayoutConstraint.swift @@ -30,19 +30,19 @@ public extension UIEdgeInsets { init(_ left: CGFloat, _ top: CGFloat, _ right: CGFloat, _ bottom: CGFloat) { self.init(top: top, left: left, bottom: bottom, right: right) } - + init(_ padding: CGFloat) { self.init(top: padding, left: padding, bottom: -padding, right: -padding) } } public struct EdgeInsets: OptionSet { - + public let rawValue: UInt - + public init(rawValue: UInt) { self.rawValue = rawValue } public init(_ rawValue: UInt) { self.rawValue = rawValue } - + // View Margin public static let none = EdgeInsets(1 << 0) public static let top = EdgeInsets(1 << 1) @@ -52,7 +52,7 @@ public struct EdgeInsets: OptionSet { public static let horizontal: EdgeInsets = [.left, .right] public static let vertical: EdgeInsets = [.top, .bottom] public static let all: EdgeInsets = [.horizontal, .vertical] - + // Stacking View - Margin public static let leadingMargin = EdgeInsets(1 << 5) public static let trailingMargin = EdgeInsets(1 << 6) @@ -62,7 +62,7 @@ public struct EdgeInsets: OptionSet { public static let centerYMargin = EdgeInsets(1 << 10) public static let centerMargin: EdgeInsets = [.centerXMargin, .centerYMargin] public static let autoMargin = EdgeInsets(1 << 11) - + // Private // fileprivate static let AllLayoutMargin: EdgeInsets = [.LeadingMargin, .TrailingMargin, // .TopMargin, .BottomMargin, @@ -84,7 +84,7 @@ public struct EdgeInsets: OptionSet { // Remove inValid Constrains mutating func stanitize(forDirection direction: LayoutDirection) { self.remove(.all) - + if direction == .topToBottom { self.remove([.topMargin, .bottomMargin]) self.remove(.centerYMargin) @@ -101,7 +101,7 @@ public struct EdgeInsets: OptionSet { self.insert(.centerYMargin) } } - + // autoMargin, needs to have equalSize to adjust sizing if self.contains(.autoMargin) { // FIXIT: Update to remove .EqualSize @@ -112,32 +112,32 @@ public struct EdgeInsets: OptionSet { } public protocol ViewConstrains: AnyObject { - + func pin(view: UIView, withEdgeOffsets edgeOffsets: UIEdgeInsets?, withEdgeInsets edgeInsets: EdgeInsets?, withLayoutPriority priority: UILayoutPriority?, addToSubView: Bool? ) - + func stackView(views: [UIView], layoutDirection direction: LayoutDirection, spacing: CGFloat, edgeInsets: EdgeInsets?, layoutPriority priority: UILayoutPriority?) - + func addSizeConstraint(_ width: CGFloat, _ height: CGFloat) - + // auto-size to self height and width func addSelfSizing() - + // Incudes Negative screen offset func resizeToFitSubviews() - + func setViewSize(_ size: CGSize, createConstraint: Bool, relation: NSLayoutConstraint.Relation) - + func setViewHeight(_ height: CGFloat, createConstraint: Bool, relation: NSLayoutConstraint.Relation) - + func setViewWidth(_ width: CGFloat, createConstraint: Bool, relation: NSLayoutConstraint.Relation) } @@ -157,7 +157,7 @@ private extension UIView { func hasSameBaseView(_ view: UIView) -> Bool { self.superview?.subviews.contains(view) ?? false } - + func validPriority(_ priority: UILayoutPriority) -> UILayoutPriority { if Int(priority.rawValue) < 0 { return UILayoutPriority(rawValue: 1) @@ -167,109 +167,109 @@ private extension UIView { } return priority } - + func pinEdges(view: UIView, edgeOffsets: UIEdgeInsets, edgeInsets: EdgeInsets, priority: UILayoutPriority) -> [NSLayoutConstraint] { var localConstraint = [NSLayoutConstraint]() - + // Left if edgeInsets.contains(.left) { let constraint = self.pin(\UIView.leftAnchor, toView: view, priority: priority, constant: edgeOffsets.left) localConstraint.append(constraint) } - + // Right if edgeInsets.contains(.right) { let constraint = self.pin(\UIView.rightAnchor, toView: view, priority: priority, constant: edgeOffsets.right) localConstraint.append(constraint) } - + // Top if edgeInsets.contains(.top) { let constraint = self.pin(\UIView.topAnchor, toView: view, priority: priority, constant: edgeOffsets.top) localConstraint.append(constraint) } - + // Bottom if edgeInsets.contains(.bottom) { let constraint = self.pin(\UIView.bottomAnchor, toView: view, priority: priority, constant: edgeOffsets.bottom) localConstraint.append(constraint) } - + return localConstraint } - + func pinMargin(view: UIView, edgeOffsets: UIEdgeInsets, edgeInsets: EdgeInsets, priority: UILayoutPriority) -> [NSLayoutConstraint] { - + var localConstraint = [NSLayoutConstraint]() - + // Top Margin if edgeInsets.contains(.topLayoutMargin) { let constraintTop = self.pin(\UIView.topAnchor, toView: view, priority: priority) localConstraint.append(constraintTop) - + let constraintBottom = self.pin(\UIView.bottomAnchor, toView: view, priority: priority) localConstraint.append(constraintBottom) } - + // Left Margin if edgeInsets.contains(.leftLayoutMargin) { let constraint = self.pin(\UIView.leftAnchor, toView: view, priority: priority, constant: edgeOffsets.left) localConstraint.append(constraint) - + let constraintRight = self.pin(\UIView.rightAnchor, toView: view, priority: priority, constant: edgeOffsets.right) localConstraint.append(constraintRight) } - + // CenterXMargin if edgeInsets.contains(.centerXMargin) { let constraint = self.pin(\UIView.centerXAnchor, toView: view, priority: priority) localConstraint.append(constraint) } - + // CenterYMargin if edgeInsets.contains(.centerYMargin) { let constraint = self.pin(\UIView.centerYAnchor, toView: view, priority: priority) localConstraint.append(constraint) } - + return localConstraint } - + func pinAutoMargin(view: UIView, edgeOffsets: UIEdgeInsets, edgeInsets: EdgeInsets, priority: UILayoutPriority) -> [NSLayoutConstraint] { var localConstraint = [NSLayoutConstraint]() - + // Right-Left Direction AutoMargin if edgeInsets.contains(.leftRightMargin) { let constraint = view.leftAnchor.constraint(equalTo: self.rightAnchor, constant: edgeOffsets.left) constraint.priority = priority localConstraint.append(constraint) } - + // Top-Bottom Direction AutoMargin if edgeInsets.contains(.topBottomMargin) { let constraint = view.topAnchor.constraint(equalTo: self.bottomAnchor, constant: edgeOffsets.bottom) constraint.priority = priority localConstraint.append(constraint) } - + return localConstraint } - + func pinStackViewMargin(view: UIView, edgeInsets: EdgeInsets, priority: UILayoutPriority) -> [NSLayoutConstraint] { var localConstraint = [NSLayoutConstraint]() - + // Leading if edgeInsets.contains(.leadingMargin) { let constraint = self.pin(\UIView.leadingAnchor, toView: view, priority: priority) localConstraint.append(constraint) } - + // Traling if edgeInsets.contains(.trailingMargin) { let constraint = self.pin(\UIView.trailingAnchor, toView: view, priority: priority) localConstraint.append(constraint) } - + // TopMargin if edgeInsets.contains(.topMargin) { let constraint = NSLayoutConstraint( @@ -284,7 +284,7 @@ private extension UIView { constraint.priority = priority localConstraint.append(constraint) } - + // BottomMargin if edgeInsets.contains(.bottomMargin) { let constraint = NSLayoutConstraint( @@ -299,61 +299,61 @@ private extension UIView { constraint.priority = priority localConstraint.append(constraint) } - + // Equal Height if edgeInsets.contains(.equalHeight) { let constraint = self.pin(\UIView.heightAnchor, toView: view, priority: priority, constant: 0) localConstraint.append(constraint) } - + // Equal Width if edgeInsets.contains(.equalWidth) { let constraint = self.pin(\UIView.widthAnchor, toView: view, priority: priority, constant: 0) localConstraint.append(constraint) } - + return localConstraint } } public extension UIView { - + func pin(view: UIView, edgeOffsets: UIEdgeInsets = .zero, edgeInsets: EdgeInsets = .all, priority: UILayoutPriority = .required, addToSubView: Bool = true) { - + var localConstraint = [NSLayoutConstraint]() let priority = validPriority(priority) let hasSameBase = self.hasSameBaseView(view) - + if addToSubView && !self.subviews.contains(view) && !hasSameBase { self.addSubview(view) } view.translatesAutoresizingMaskIntoConstraints = false - + // pin Edges: Left, Right, Top, Bottom let edges = pinEdges(view: view, edgeOffsets: edgeOffsets, edgeInsets: edgeInsets, priority: priority) localConstraint.append(contentsOf: edges) - + // pin Margin: Top, Bottom, Center-X&Y, let margins = pinMargin(view: view, edgeOffsets: edgeOffsets, edgeInsets: edgeInsets, priority: priority) localConstraint.append(contentsOf: margins) - + // pin Margin: Right-Left, Top-Bottom let autoMargin = pinAutoMargin(view: view, edgeOffsets: edgeOffsets, edgeInsets: edgeInsets, priority: priority) localConstraint.append(contentsOf: autoMargin) - + // Auto Sizing if edgeInsets.contains(.autoSize) { view.addSelfSizing() } - + // Applicable for only Stacking, only if hasSameBase let stackingMargin = pinStackViewMargin(view: view, edgeInsets: edgeInsets, priority: priority) localConstraint.append(contentsOf: stackingMargin) - + // Add created Constraints to view if self.subviews.contains(view) || !addToSubView { self.addConstraints(localConstraint) @@ -362,7 +362,7 @@ public extension UIView { self.superview?.addConstraints(localConstraint) } } - + func stackView(views: [UIView], layoutDirection direction: LayoutDirection = .topToBottom, spacing: CGFloat = 0, @@ -371,7 +371,7 @@ public extension UIView { ) { // Add views to subView, if not present views.filter { !self.subviews.contains($0) }.forEach(addSubview(_:)) - + // Pin each view to self with Default-padding, with lower priority. // Used of .AutoMargin edgeInsets, // If any view is removed, this will reset the remaningViews to defaults @@ -379,7 +379,7 @@ public extension UIView { // Fix to let priorityL = priority.rawValue - Float(views.count + index) let defaultOffset = UIEdgeInsets(0) - + if direction == .topToBottom { self.pin(view: view, edgeOffsets: defaultOffset, edgeInsets: .topLayoutMargin, priority: UILayoutPriority(rawValue: priorityL)) } @@ -387,20 +387,20 @@ public extension UIView { self.pin(view: view, edgeOffsets: defaultOffset, edgeInsets: .leftLayoutMargin, priority: UILayoutPriority(rawValue: priorityL)) } } - + // Pin each view to nextView with zeroOffSet-spacing, with lower priority. // Used of .AutoMargin edgeInsets, // If any view is removed, this will reset the remaningViews to defaults let pinPairSubViews = { (index: Int, offSet: UIEdgeInsets, subEdgeInsets: EdgeInsets) in - + let pos = index + 1 var localIndex = 0 - + while pos + localIndex < views.count { let locallastView = views[localIndex] let preview = views[localIndex + pos] localIndex += 1 - + let priorityS = priority.rawValue - Float(pos) locallastView.pin( view: preview, @@ -410,15 +410,15 @@ public extension UIView { ) } } - + // For Each View var localEdgeInsets: EdgeInsets = edgeInsets localEdgeInsets.stanitize(forDirection: direction) var lastView: UIView? - + var offSet: UIEdgeInsets = .zero - + if direction == .topToBottom { offSet = UIEdgeInsets(0, 0, 0, -spacing) localEdgeInsets.update(with: .topBottomMargin) @@ -427,54 +427,54 @@ public extension UIView { offSet = UIEdgeInsets(spacing, 0, 0, 0) localEdgeInsets.update(with: .leftRightMargin) } - + views.enumerated().forEach { index, view in - + view.translatesAutoresizingMaskIntoConstraints = false if lastView != nil { lastView?.pin(view: view, edgeOffsets: offSet, edgeInsets: localEdgeInsets, priority: priority) } - + lastView = view - + // TODO: Simplfy the logic if localEdgeInsets.contains(.autoMargin) { // Pin each view to self with Default-padding. pinEachViewToSuperView(index, view) - + // Pin each view to nextView with offSet-padding. pinPairSubViews(index, offSet, localEdgeInsets) } } } - + // Sets height & width of the View // Negative values will skip setting constraints // If invoked after ".autoSize or addSelfSizing", the view will not auto-resize the width & height. func addSizeConstraint(_ width: CGFloat = -10, _ height: CGFloat = -10, relation: NSLayoutConstraint.Relation = .equal) { self.viewLayoutConstraint.autoSizing = true self.translatesAutoresizingMaskIntoConstraints = false - + if width >= 0 { self.setViewWidth(width, createConstraint: true, relation: relation) } - + if height >= 0 { self.setViewHeight(height, createConstraint: true, relation: relation) } } - + // Auto-size to self height and width func addSelfSizing() { self.viewLayoutConstraint.autoSizing = true self.translatesAutoresizingMaskIntoConstraints = false - + self.setViewWidth(self.layoutMargins.left - self.layoutMargins.right) self.setViewHeight(self.layoutMargins.top - self.layoutMargins.bottom) self.sizeToFit() } - + // Incudes Negative screen offset func resizeToFitSubviews() { let reducedSize = self.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) @@ -484,16 +484,16 @@ public extension UIView { self.setViewSize(reducedSize, relation: .equal) } } - + // MARK: AssociatedObject for view Layout constraints fileprivate static let aoLayoutConstraint = AssociatedObject() - + var viewLayoutConstraint: ViewLayoutConstraint { get { if let storedLayouts = UIView.aoLayoutConstraint[self] { return storedLayouts } - + self.viewLayoutConstraint = ViewLayoutConstraint() return self.viewLayoutConstraint } @@ -501,18 +501,18 @@ public extension UIView { UIView.aoLayoutConstraint[self] = newValue } } - + func setViewSize(_ size: CGSize, createConstraint: Bool = false, relation: NSLayoutConstraint.Relation = .greaterThanOrEqual) { self.setViewHeight(size.height, createConstraint: createConstraint, relation: relation) self.setViewWidth(size.width, createConstraint: createConstraint, relation: relation) } - + func setViewHeight(_ height: CGFloat, createConstraint: Bool = false, relation: NSLayoutConstraint.Relation = .greaterThanOrEqual) { let con = self.viewLayoutConstraint if createConstraint || con.autoSizing { if con.constraintHeight == nil || (createConstraint && con.constraintHeight?.relation != relation) { con.heightDimension = self.heightAnchor - + switch relation { case .equal: con.constraintHeight = self.heightAnchor.constraint(equalToConstant: height) @@ -528,7 +528,7 @@ public extension UIView { } } } - + func setViewWidth(_ width: CGFloat, createConstraint: Bool = false, relation: NSLayoutConstraint.Relation = .greaterThanOrEqual) { let con = self.viewLayoutConstraint if createConstraint || con.autoSizing { diff --git a/Sources/NetworkLayer/Cache/UserCacheManager.swift b/Sources/NetworkLayer/Cache/UserCacheManager.swift index 70bbf2b..6d47504 100644 --- a/Sources/NetworkLayer/Cache/UserCacheManager.swift +++ b/Sources/NetworkLayer/Cache/UserCacheManager.swift @@ -34,7 +34,7 @@ public enum UserCacheType { public protocol UserCacheProtocol { // Will Setup userCache static var sharedInstance: UserCacheManager { get } - + // Application level cache, reset when app relaunches var appCache: JSON { get } // session cache, clears when user logout @@ -71,11 +71,11 @@ public class UserCacheManager: UserCacheProtocol { }() // Application level cache, reset when app relaunches - public fileprivate (set) var appCache = JSON() + public fileprivate(set) var appCache = JSON() // session cache, clears when user logout - public fileprivate (set) var userCache: JSON? + public fileprivate(set) var userCache: JSON? // Image cache, clears when user logout - public fileprivate (set) var imageCache = NSCache() + public fileprivate(set) var imageCache = NSCache() public func setupUserSession() { // Setup local cache @@ -150,9 +150,9 @@ private extension UserCacheProtocol { // keychainAccessiblity: KeychainItemAccessibility = .whenUnlockedThisDeviceOnly @discardableResult static func setCacheObject(_ data: AnyObject?, forKey keyType: T, cacheType: UserCacheType) -> Bool where T: Hashable { - + let key: String = (keyType as? String) ?? String(describing: keyType) - + // USER SESSION LEVEL if cacheType == .user { UserCacheManager.sharedInstance.userCache?[key] = data diff --git a/Sources/NetworkLayer/NetworkLayer/NetworkMananger.swift b/Sources/NetworkLayer/NetworkLayer/NetworkMananger.swift index 66ed8e8..ed71259 100644 --- a/Sources/NetworkLayer/NetworkLayer/NetworkMananger.swift +++ b/Sources/NetworkLayer/NetworkLayer/NetworkMananger.swift @@ -27,7 +27,7 @@ public class NetworkMananger { // Check for stubData fileprivate static var isMockDataModel = false - + public static var isMockData: Bool { get { isMockDataModel && mockBundle != nil @@ -40,7 +40,7 @@ public class NetworkMananger { // Stub data bundle, used by ServiceClient static var mockBundle: Bundle? - public static var mockBundleResource: URL? = nil { + public static var mockBundleResource: URL? { didSet { if let bundle = mockBundleResource { mockBundle = Bundle(url: bundle) @@ -71,7 +71,7 @@ public class NetworkMananger { // MARK: Model Schema var modelSchema = JSON() - + // MARK: Logger public static var enableConsoleLogging: Bool { get { @@ -140,8 +140,7 @@ extension NetworkMananger { if let resourcePath = Bundle.main.path(forResource: self.serviceBindingRulesName, ofType: nil), let content: JSON = NSMutableDictionary(contentsOfFile: resourcePath) as? JSON, - JSONSerialization.isValidJSONObject(content) - { + JSONSerialization.isValidJSONObject(content) { self.sharedInstance.serviceRuels += content } } diff --git a/Sources/NetworkLayer/NetworkLayer/RequestObject.swift b/Sources/NetworkLayer/NetworkLayer/RequestObject.swift index f728e8d..7775be4 100644 --- a/Sources/NetworkLayer/NetworkLayer/RequestObject.swift +++ b/Sources/NetworkLayer/NetworkLayer/RequestObject.swift @@ -36,7 +36,7 @@ public enum ReqeustType: String, Codable { guard let model = model else { return nil } - + switch self { case .POST: return model.jsonModelData() diff --git a/Sources/NetworkLayer/NetworkLayer/ServiceClient.swift b/Sources/NetworkLayer/NetworkLayer/ServiceClient.swift index 35f535d..38907f6 100644 --- a/Sources/NetworkLayer/NetworkLayer/ServiceClient.swift +++ b/Sources/NetworkLayer/NetworkLayer/ServiceClient.swift @@ -22,9 +22,9 @@ private enum LogConstants: String { // MARK: AssociatedKey private extension AssociatedKey { - static var ServiceRequest = "ServiceRequest" - static var ResponseData = "ResponseData" - static var ModelData = "ModelData" + static var ServiceRequest = Int8(0) // "ServiceRequest" + static var ResponseData = Int8(1) // "ResponseData" + static var ModelData = Int8(2) // "ModelData" } // MARK: Service Status @@ -92,7 +92,7 @@ public protocol ServiceClient: ServiceRulesProtocol { // // // /This is for doing SSL pinning // var security: HTTPSecurity? - + static func make(modelStack: ServiceModel?, completionHandler: ServiceCompletionBlock?) } @@ -129,7 +129,7 @@ public extension ServiceClient { ftLog("responseData is nil") return nil } - + guard let dataModel = responseStackType as? ServiceModel.Type, var responseModelData: ServiceModel = try? dataModel.makeModel(json: data), !responseModelData.queryItems().isEmpty else { @@ -145,7 +145,7 @@ public extension ServiceClient { // Logging return nil } - + do { // try parsing response model: configured by `ServiceClient` model if let responseStack = try self.responseType()?.makeModel(json: data) { @@ -324,13 +324,13 @@ extension ServiceClient { } } - let handler: URLSessionCompletionBlock = { (data: Data?, response: URLResponse?, error: Error?) -> Void in + let handler: URLSessionCompletionBlock = { (data: Data?, response: URLResponse?, error: Error?) in let request = self.serviceRequest // Log Resposne logError(request, error) // Service Rules self.fireAfter(data: data, response: response, error: error) - + // Stub if NetworkMananger.isMockData, self.mockDataHandler(completionHandler) != nil { return @@ -373,15 +373,14 @@ extension ServiceClient { ftLog(self.serviceName, ": is data stubbed.") if let path: String = NetworkMananger.mockBundle?.path(forResource: self.serviceName, ofType: "json"), - let data = try? path.dataAtPath() - { + let data = try? path.dataAtPath() { let model = self.processResponseData(data: data) DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(2)) { completionHandler?(ServiceStatus.success(self, (model != nil) ? 200 : 500)) } return model } - + return nil } } diff --git a/Sources/NetworkLayer/NetworkLayer/ServiceModel.swift b/Sources/NetworkLayer/NetworkLayer/ServiceModel.swift index 92d4740..365f750 100644 --- a/Sources/NetworkLayer/NetworkLayer/ServiceModel.swift +++ b/Sources/NetworkLayer/NetworkLayer/ServiceModel.swift @@ -62,7 +62,7 @@ public extension ServiceModel { let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) return try self.makeModel(json: data) } - + throw JsonParserError.invalidJSON } @@ -78,7 +78,7 @@ public extension ServiceModel { let data: Data = try JSONEncoder().encode(self) return try? data.jsonContent() as? JSON } - + func jsonString() -> String? { if var jsn: JSON = try? self.jsonModel() { jsn.stripNilElements() diff --git a/Tests/CoreComponentsTests/ScrollViewControllerProtocolTests.swift b/Tests/CoreComponentsTests/ScrollViewControllerProtocolTests.swift index 01f5af4..a313b3a 100644 --- a/Tests/CoreComponentsTests/ScrollViewControllerProtocolTests.swift +++ b/Tests/CoreComponentsTests/ScrollViewControllerProtocolTests.swift @@ -13,7 +13,7 @@ import CoreUtility import UIKit import XCTest -fileprivate final class MockScrollViewController: UIViewController, ScrollViewControllerProtocol { +private final class MockScrollViewController: UIViewController, ScrollViewControllerProtocol { // Optional Protocol implementation: intentionally empty } diff --git a/Tests/CoreComponentsTests/TableViewControllerProtocolTests.swift b/Tests/CoreComponentsTests/TableViewControllerProtocolTests.swift index 6b1ab17..2262283 100644 --- a/Tests/CoreComponentsTests/TableViewControllerProtocolTests.swift +++ b/Tests/CoreComponentsTests/TableViewControllerProtocolTests.swift @@ -12,15 +12,15 @@ import CoreUtility #endif import XCTest -fileprivate final class MockTableViewHeader: UIView { +private final class MockTableViewHeader: UIView { // Temp class extending Protocol for testing } -fileprivate final class MockTableViewController: UIViewController, TableViewControllerProtocol { +private final class MockTableViewController: UIViewController, TableViewControllerProtocol { // Temp class extending Protocol for testing } -fileprivate final class MockCustomTableViewController: UIViewController, TableViewControllerProtocol { +private final class MockCustomTableViewController: UIViewController, TableViewControllerProtocol { var tableStyle: UITableView.Style = .grouped var tableViewEdgeOffsets: UIEdgeInsets = .init(40, 40, 40, 40) } diff --git a/Tests/CoreComponentsTests/ViewControllerProtocolTests.swift b/Tests/CoreComponentsTests/ViewControllerProtocolTests.swift index f2c0b92..83bb113 100644 --- a/Tests/CoreComponentsTests/ViewControllerProtocolTests.swift +++ b/Tests/CoreComponentsTests/ViewControllerProtocolTests.swift @@ -28,9 +28,9 @@ extension MockModelStack: Equatable { } private final class MockViewContoller: UIViewController { - private (set) var isKeyboardWillShowCalled = false - private (set) var isKeyboardDidHideCalled = false - private (set) var isAlertViewPresented = false + private(set) var isKeyboardWillShowCalled = false + private(set) var isKeyboardDidHideCalled = false + private(set) var isAlertViewPresented = false override func keyboardWillShow(_ notification: Notification?) { isKeyboardWillShowCalled = true diff --git a/Tests/CoreUtilityTests/AssociatedObjectTests.swift b/Tests/CoreUtilityTests/AssociatedObjectTests.swift index bff4962..79ea9a5 100644 --- a/Tests/CoreUtilityTests/AssociatedObjectTests.swift +++ b/Tests/CoreUtilityTests/AssociatedObjectTests.swift @@ -7,7 +7,7 @@ // #if canImport(CoreUtility) -import CoreUtility +@testable import CoreUtility #endif import XCTest diff --git a/Tests/CoreUtilityTests/ConfigurableCellTests.swift b/Tests/CoreUtilityTests/ConfigurableCellTests.swift index 14f8efa..8260647 100644 --- a/Tests/CoreUtilityTests/ConfigurableCellTests.swift +++ b/Tests/CoreUtilityTests/ConfigurableCellTests.swift @@ -11,7 +11,7 @@ import CoreUtility #endif import XCTest -fileprivate final class MockViewCellWithoutNib: UIView { +private final class MockViewCellWithoutNib: UIView { } final class ConfigurableCellTests: XCTestCase { diff --git a/Tests/NetworkLayerTests/CollectionViewProtocolTests.swift b/Tests/NetworkLayerTests/CollectionViewProtocolTests.swift index 231d035..abee3a5 100644 --- a/Tests/NetworkLayerTests/CollectionViewProtocolTests.swift +++ b/Tests/NetworkLayerTests/CollectionViewProtocolTests.swift @@ -13,7 +13,7 @@ #endif import XCTest -fileprivate final class MockCollectionViewController: UIViewController, CollectionViewControllerProtocol { +private final class MockCollectionViewController: UIViewController, CollectionViewControllerProtocol { // Mock: object implementation for testing } diff --git a/Tests/NetworkLayerTests/WebViewControllerProtocolTests.swift b/Tests/NetworkLayerTests/WebViewControllerProtocolTests.swift index c445ab2..aa1d41e 100644 --- a/Tests/NetworkLayerTests/WebViewControllerProtocolTests.swift +++ b/Tests/NetworkLayerTests/WebViewControllerProtocolTests.swift @@ -14,7 +14,7 @@ import WebKit import XCTest -fileprivate final class MockKWebViewController: UIViewController, WebViewControllerProtocol { +private final class MockKWebViewController: UIViewController, WebViewControllerProtocol { // Mock: object implementation for testing } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index a7c48be..964ae09 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -16,7 +16,8 @@ lane :lint do swiftlint( mode: :lint, output_file: 'reports/swiftlint.txt', - config_file: '.swiftlint.yml' + config_file: '.swiftlint.yml', + ignore_exit_status: true ) end