├── .gitignore ├── LICENSE.md ├── Package.swift ├── README.md ├── ScreenShots ├── demo.gif ├── demo.mp4 └── screenShot1.png ├── Sources └── TFManager │ ├── TFManager.swift │ ├── TextRules.swift │ ├── TextRulesRepo.swift │ ├── Validatable.swift │ ├── ValidatableField.swift │ └── ValidationResult.swift ├── TFManager.podspec ├── TFManagerDemo ├── Podfile ├── Podfile.lock ├── Pods │ ├── Headers │ │ └── Public │ │ │ └── TFManager │ │ │ ├── TFManager-umbrella.h │ │ │ └── TFManager.modulemap │ ├── Local Podspecs │ │ └── TFManager.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Pods-TFManagerDemo │ │ ├── Pods-TFManagerDemo-acknowledgements.markdown │ │ ├── Pods-TFManagerDemo-acknowledgements.plist │ │ ├── Pods-TFManagerDemo-dummy.m │ │ ├── Pods-TFManagerDemo-umbrella.h │ │ ├── Pods-TFManagerDemo.debug.xcconfig │ │ ├── Pods-TFManagerDemo.modulemap │ │ └── Pods-TFManagerDemo.release.xcconfig │ │ └── TFManager │ │ ├── TFManager-dummy.m │ │ ├── TFManager-prefix.pch │ │ ├── TFManager-umbrella.h │ │ ├── TFManager.debug.xcconfig │ │ ├── TFManager.modulemap │ │ └── TFManager.release.xcconfig ├── TFManagerDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── TFManagerDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── TFManagerDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── CustomValidatableField.swift │ ├── Info.plist │ └── ViewController.swift └── Tests └── TFManagerTests └── TFManagerTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to deal 4 | in the Software without restriction, including without limitation the rights 5 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "TFManager", 8 | platforms: [ 9 | .iOS(.v11), 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, and make them visible to other packages. 13 | .library( 14 | name: "TFManager", 15 | targets: ["TFManager"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 24 | .target( 25 | name: "TFManager", 26 | dependencies: []), 27 | .testTarget( 28 | name: "TFManagerTests", 29 | dependencies: ["TFManager"]), 30 | ], 31 | swiftLanguageVersions: [.v5] 32 | ) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # TFManager 6 | 7 | Let's say you have multiple `UITextField`s to get data from users. You need to handle each field keyboard's return key and add an accessory view to the keyboard for navigating through fields. TFManager will do this for you in just one line of code! 8 | And if you want more you can add validation rules to the text fields and check if they're valid or not. 9 | 10 | [![iOS](https://img.shields.io/badge/iOS-11.0-blue.svg)](https://developer.apple.com/iOS) 11 | [![SPM](https://img.shields.io/badge/SPM-compatible-red?style=flat-square)](https://developer.apple.com/documentation/swift_packages/package/) 12 | [![MIT](https://img.shields.io/badge/licenses-MIT-red.svg)](https://opensource.org/licenses/MIT) 13 | 14 | ## Navigate 15 | 16 | - [Installation](#installation) 17 | - [Swift Package Manager](#swift-package-manager) 18 | - [CocoaPods](#cocoapods) 19 | - [Manually](#manually) 20 | - [Basic Usage](#basic-usage) 21 | - [Validate All Fields](#validate-all-fields) 22 | - [Rules](#rules) 23 | - [Contact](#contact) 24 | - [License](#license) 25 | 26 | ## Installation 27 | 28 | Ready for use on iOS and iPadOS 11+. 29 | 30 | ### Swift Package Manager 31 | 32 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies. 33 | 34 | Once you have your Swift package set up, adding as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. 35 | 36 | ```swift 37 | dependencies: [ 38 | .package(url: "https://github.com/abspr/TFManager", .upToNextMajor(from: "1.1.0")) 39 | ] 40 | ``` 41 | 42 | ### CocoaPods: 43 | 44 | [CocoaPods](https://cocoapods.org) is a dependency manager. For usage and installation instructions, visit their website. To integrate using CocoaPods, specify it in your `Podfile`: 45 | 46 | ```ruby 47 | pod 'TFManager' 48 | ``` 49 | 50 | ### Manually 51 | 52 | If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/TFManager` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. 53 | 54 | ## Basic Usage 55 | 56 | 1. Create an instance of TFManager in your `viewController` 57 | ```swift 58 | var fieldsManager = TFManager() 59 | ``` 60 | 2. Add your `textFields` to it: 61 | ```swift 62 | fieldsManager.add([nameField, mailField, ageField]) 63 | ``` 64 | 3. There is no more steps 😯 65 | 66 | ## Validate All Fields 67 | You can add rules to your `UITextField`s and ask `TFManager` to apply validation to all of child fields: 68 | 69 | 1. Change your textField class to `ValidatableField`. 70 | 71 | 72 | 2. Then you can call `validate()` method on your `TFManager` instance. 73 | ```swift 74 | let result = fieldsManager.validate() 75 | result.forEach { (invalidField, validationResult) in 76 | invalidField.textColor = .systemRed 77 | print(validationResult.message) 78 | } 79 | ``` 80 | 81 | 💡 You can set `TFManager`'s delegate and use its methods to get notified which textField is become active or its text is changing: 82 | ```swift 83 | fieldsManager.delegate = self 84 | 85 | extension ViewController: TFManagerDelegate { 86 | func textDidChange(_ textField: UITextField, validationResult: ValidationResult?) { 87 | guard let validationResult = validationResult else { return } 88 | textField.textColor = validationResult.isValid ? .label : .systemRed 89 | } 90 | } 91 | ``` 92 | 93 | 💡 You also can subclass `ValidatableField` and customize your textField. 94 | Override `didFailValidation(_:)` and `didPass()` methods to handle valid/invalid states (eg: show/hide the error label) 95 | 96 | ## Rules 97 | `TFManager` comes with set of rules (`TextRulesSet`) and you can add them to any `ValidatableField`: 98 | ```swift 99 | ageField.rulesRepo.add(TextRulesSet.numbersOnly()) 100 | ageField.rulesRepo.add(TextRulesSet.minLenght(1)) 101 | ageField.rulesRepo.add(TextRulesSet.maxLenght(2)) 102 | ``` 103 | 104 | 💡 You can have your own rules too. Just create a `struct` and implement `TextRule`: 105 | 106 | ```swift 107 | struct YourRule: TextRule { 108 | var message: String 109 | 110 | func validate(_ text: String) -> Bool { 111 | // code 112 | } 113 | } 114 | ``` 115 | 116 | ## Contact 117 | email : [hosein@me.com](mailto:hosein@me.com) 118 | 119 | ## License 120 | TFManager is available under the MIT license. See the [LICENSE](LICENSE) file for more info. 121 | -------------------------------------------------------------------------------- /ScreenShots/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abspr/TFManager/31c969fdbc515274c029cfa17b570c9260b7de7f/ScreenShots/demo.gif -------------------------------------------------------------------------------- /ScreenShots/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abspr/TFManager/31c969fdbc515274c029cfa17b570c9260b7de7f/ScreenShots/demo.mp4 -------------------------------------------------------------------------------- /ScreenShots/screenShot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abspr/TFManager/31c969fdbc515274c029cfa17b570c9260b7de7f/ScreenShots/screenShot1.png -------------------------------------------------------------------------------- /Sources/TFManager/TFManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Form.swift 3 | // TFManager 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Sets of methods you can use to notify how users are interacting with the textFields. 11 | public protocol TFManagerDelegate: AnyObject { 12 | 13 | /// to notify which `UITextField` was last responder. 14 | func focusChanged(from textField: UITextField) 15 | 16 | /// to notify which `UITextField` became first responder. 17 | func focusChanged(to textField: UITextField) 18 | 19 | /// Will call when text changed from a `UITextField` with its validation result. 20 | func textDidChange(_ textField: UITextField, validationResult: ValidationResult?) 21 | 22 | /// Will call when no more `UITextField` is first responder. 23 | func didEndEditing(_ manager: TFManager) 24 | } 25 | 26 | public extension TFManagerDelegate { 27 | func focusChanged(from textField: UITextField) { } 28 | func focusChanged(to textField: UITextField) { } 29 | func textDidChange(_ textField: UITextField, validationResult: ValidationResult?) { } 30 | func didEndEditing(_ manager: TFManager) { } 31 | } 32 | 33 | 34 | /// Groups textFields together and handle their navigation. 35 | open class TFManager: NSObject { 36 | 37 | 38 | /// Use this to get notified how users are interacting with the textFields 39 | public weak var delegate: TFManagerDelegate? 40 | 41 | private var items: [UITextField] = [] 42 | 43 | private lazy var prevButton: UIBarButtonItem = { 44 | let button = UIBarButtonItem() 45 | button.style = .plain 46 | button.target = self 47 | button.action = #selector(goToPreviousField) 48 | if #available(iOS 13.0, *) { 49 | button.image = UIImage(systemName: "chevron.backward") 50 | } else { 51 | button.title = "Previous" 52 | } 53 | return button 54 | }() 55 | 56 | private lazy var nextButton: UIBarButtonItem = { 57 | let button = UIBarButtonItem() 58 | button.style = .plain 59 | button.target = self 60 | if #available(iOS 13.0, *) { 61 | button.image = UIImage(systemName: "chevron.right") 62 | } else { 63 | button.title = "Next" 64 | } 65 | button.action = #selector(goToNextField) 66 | return button 67 | }() 68 | 69 | private lazy var doneButton: UIBarButtonItem = { 70 | let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneAction)) 71 | return button 72 | }() 73 | 74 | private lazy var toolbar = UIToolbar() 75 | private var activeField: UITextField? 76 | private var hasToolbar: Bool = true 77 | 78 | // MARK: - Init and setup 79 | 80 | public override init() { 81 | super.init() 82 | } 83 | 84 | private func setupToolbar() { 85 | let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 86 | toolbar.setItems([prevButton, nextButton, space, doneButton], animated: false) 87 | toolbar.sizeToFit() 88 | } 89 | 90 | private func configFields() { 91 | for (index, field) in items.enumerated() { 92 | field.addTarget(self, action: #selector(fieldTextChanged(_:)), for: .editingChanged) 93 | field.addTarget(self, action: #selector(fieldBeginEditing(_:)), for: .editingDidBegin) 94 | field.addTarget(self, action: #selector(returnDidPress(_:)), for: .editingDidEndOnExit) 95 | field.addTarget(self, action: #selector(fieldEndsEditing(_:)), for: .editingDidEnd) 96 | let isLastField = index == items.count - 1 97 | field.returnKeyType = isLastField ? .done : .next 98 | } 99 | } 100 | 101 | // MARK: - Public 102 | 103 | 104 | /// Adds array of textFields in order of their appearance. 105 | /// - Parameters: 106 | /// - fields: Array of `UITextFields` or `Validable` field. 107 | /// - includesBar: Whether keyboard should have accessory bar or not. 108 | open func add(_ fields: [UITextField], includesBar: Bool = true) { 109 | items = fields 110 | hasToolbar = includesBar 111 | configFields() 112 | if hasToolbar { setupToolbar() } 113 | } 114 | 115 | // MARK: - Actions 116 | 117 | @objc 118 | private func goToNextField() { 119 | guard let activeField = activeField else { return } 120 | guard let lastField = items.last, activeField != lastField else { return } 121 | guard let activeFieldIndex = items.firstIndex(where: { $0 == activeField }) else { return } 122 | if items.indices.contains(activeFieldIndex + 1) { 123 | items[activeFieldIndex + 1].becomeFirstResponder() 124 | } 125 | } 126 | 127 | @objc 128 | private func goToPreviousField() { 129 | guard let activeField = activeField else { return } 130 | guard let firstField = items.first, activeField != firstField else { return } 131 | guard let activeFieldIndex = items.firstIndex(where: { $0 == activeField }) else { return } 132 | if items.indices.contains(activeFieldIndex - 1) { 133 | items[activeFieldIndex - 1].becomeFirstResponder() 134 | } 135 | } 136 | 137 | @objc 138 | private func doneAction() { 139 | activeField?.resignFirstResponder() 140 | delegate?.didEndEditing(self) 141 | } 142 | 143 | @objc 144 | private func fieldTextChanged(_ textField: UITextField) { 145 | delegate?.textDidChange(textField, validationResult: (textField as? ValidatableField)?.validate()) 146 | } 147 | 148 | @objc 149 | private func fieldBeginEditing(_ textField: UITextField) { 150 | activeField = textField 151 | delegate?.focusChanged(to: textField) 152 | guard hasToolbar else { return } 153 | activeField?.inputAccessoryView = toolbar 154 | nextButton.isEnabled = !(textField == items.last) 155 | prevButton.isEnabled = !(textField == items.first) 156 | } 157 | 158 | @objc 159 | private func fieldEndsEditing(_ textField: UITextField) { 160 | delegate?.focusChanged(from: textField) 161 | } 162 | 163 | @objc 164 | private func returnDidPress(_ textField: UITextField) { 165 | if textField == items.last { 166 | doneAction() 167 | } else { 168 | goToNextField() 169 | } 170 | } 171 | 172 | // MARK: - 173 | 174 | /// Loop through all textFields in the manager and returns the result. 175 | /// - Returns: Array of tuples containing textFields and validation result. 176 | public func validate() -> [(textField: UITextField, result: ValidationResult)] { 177 | var validationResult = [(textField: UITextField, result: ValidationResult)]() 178 | for item in items { 179 | guard let validatableItem = item as? Validatable else { continue } 180 | let result = validatableItem.validate() 181 | if !result.isValid { 182 | validationResult.append((item, result)) 183 | } 184 | } 185 | return validationResult 186 | } 187 | 188 | 189 | 190 | } 191 | -------------------------------------------------------------------------------- /Sources/TFManager/TextRules.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValidationRules.swift 3 | // MyKuyaClient 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Conform to this if you want to make your own rules beside `RulesSet` 11 | public protocol TextRule { 12 | 13 | /// Message that will be returned if any `Validatable` field does not pass validation. 14 | var message: String { get set } 15 | 16 | /// Implement this for your rule logic 17 | /// - Returns: `true` if `text` is valid. 18 | func validate(_ text: String) -> Bool 19 | } 20 | 21 | /// Set of rules. You can use them in any `Validable` field. 22 | public struct TextRulesSet { 23 | 24 | /// Email validation. If you don't set `errorMessage` default error message will be returned. 25 | public static func mail(_ errorMessage: String? = nil) -> TextRule { 26 | if let message = errorMessage { return Mail(message) } 27 | else { return Mail(RulesErrorMessages.mail) } 28 | } 29 | 30 | /// Zipcode validation. If you don't set `errorMessage` default error message will be returned. 31 | public static func zipcode(_ errorMessage: String? = nil) -> TextRule { 32 | if let message = errorMessage { return ZipCode(message) } 33 | else { return ZipCode(RulesErrorMessages.zipCode) } 34 | } 35 | 36 | /// Characters count validation. If you don't set `errorMessage` default error message will be returned. 37 | public static func exactLenght(_ lenght: Int, _ errorMessage: String? = nil) -> TextRule { 38 | if let message = errorMessage { return ExactLenght(lenght: lenght, message) } 39 | else { return ExactLenght(lenght: lenght, RulesErrorMessages.exactLenght(lenght)) } 40 | } 41 | 42 | /// Minimum characters count validation. If you don't set `errorMessage` default error message will be returned. 43 | public static func minLenght(_ lenght: Int, _ errorMessage: String? = nil) -> TextRule { 44 | if let message = errorMessage { return MinLenght(minLenght: lenght, message) } 45 | else { return MinLenght(minLenght: lenght, RulesErrorMessages.minLenght(lenght)) } 46 | } 47 | 48 | /// Maximum characters count validation. If you don't set `errorMessage` default error message will be returned. 49 | public static func maxLenght(_ lenght: Int, _ errorMessage: String? = nil) -> TextRule { 50 | if let message = errorMessage { return MaxLenght(maxLenght: lenght, message) } 51 | else { return MaxLenght(maxLenght: lenght, RulesErrorMessages.maxLenght(lenght)) } 52 | } 53 | 54 | /// If text is only white spaces will not pass validation. If you don't set `errorMessage` default error message will be returned. 55 | public static func notEmpty(_ errorMessage: String? = nil) -> TextRule { 56 | if let message = errorMessage { return notEmpty(message) } 57 | else { return NotEmpty(RulesErrorMessages.notEmpty) } 58 | } 59 | 60 | /// Validates if text cotains only numbers. If you don't set `errorMessage` default error message will be returned. 61 | public static func numbersOnly(_ errorMessage: String? = nil) -> TextRule { 62 | if let message = errorMessage { return NumbersOnly(message) } 63 | else { return NumbersOnly(RulesErrorMessages.numbersOnly) } 64 | } 65 | 66 | } 67 | 68 | /// Implement this on your object and pass the regex and use `validate(_:)` for validation. 69 | public protocol RegexRuleInterface { 70 | var regex: String { get } 71 | func validate(_ text: String) -> Bool 72 | } 73 | 74 | extension RegexRuleInterface { 75 | func validate(_ text: String) -> Bool { 76 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 77 | return predicate.evaluate(with: text) 78 | } 79 | } 80 | 81 | fileprivate struct RulesErrorMessages { 82 | static var mail: String = "Email is not valid." 83 | static var zipCode: String = "Zipcode is not valid." 84 | static var notEmpty: String = "Space only text is not valid." 85 | static var numbersOnly: String = "You must enter only numbers." 86 | static func exactLenght(_ lenght: Int) -> String { "Must be exactly \(lenght) characters long." } 87 | static func minLenght(_ lenght: Int) -> String { "Must be at least \(lenght) characters long." } 88 | static func maxLenght(_ lenght: Int) -> String { "Must be maximum of \(lenght) characters long." } 89 | } 90 | 91 | fileprivate struct Mail: TextRule, RegexRuleInterface { 92 | var regex: String { "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" } 93 | var message: String 94 | 95 | init(_ errorMessage: String) { message = errorMessage } 96 | } 97 | 98 | fileprivate struct ZipCode: TextRule, RegexRuleInterface { 99 | var regex: String { "\\d{5}(-\\d{4})?" } 100 | var message: String 101 | 102 | init(_ errorMessage: String) { message = errorMessage } 103 | } 104 | 105 | fileprivate struct ExactLenght: TextRule { 106 | var message: String 107 | var lenght: Int 108 | 109 | init(lenght: Int, _ errorMessage: String) { 110 | self.lenght = lenght 111 | message = errorMessage 112 | } 113 | 114 | func validate(_ text: String) -> Bool { 115 | text.count == lenght 116 | } 117 | } 118 | 119 | fileprivate struct MinLenght: TextRule { 120 | var message: String 121 | var min: Int 122 | 123 | init(minLenght: Int, _ errorMessage: String) { 124 | self.min = minLenght 125 | message = errorMessage 126 | } 127 | 128 | func validate(_ text: String) -> Bool { 129 | text.count >= min 130 | } 131 | } 132 | 133 | fileprivate struct MaxLenght: TextRule { 134 | var message: String 135 | var max: Int 136 | 137 | init(maxLenght: Int, _ errorMessage: String) { 138 | self.max = maxLenght 139 | message = errorMessage 140 | } 141 | 142 | func validate(_ text: String) -> Bool { 143 | text.count <= max 144 | } 145 | } 146 | 147 | 148 | fileprivate struct NotEmpty: TextRule { 149 | var message: String 150 | 151 | init(_ errorMessage: String) { 152 | message = errorMessage 153 | } 154 | 155 | func validate(_ text: String) -> Bool { 156 | text.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 157 | } 158 | } 159 | 160 | fileprivate struct NumbersOnly: TextRule { 161 | var message: String 162 | 163 | init(_ errorMessage: String) { 164 | message = errorMessage 165 | } 166 | 167 | func validate(_ text: String) -> Bool { 168 | var isValid = true 169 | text.unicodeScalars.forEach { scalar in 170 | if !CharacterSet.decimalDigits.contains(scalar) { 171 | isValid = false 172 | return 173 | } 174 | } 175 | return isValid 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Sources/TFManager/TextRulesRepo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RulesRepo.swift 3 | // TFManager 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import Foundation 9 | 10 | /// An object holds all rules. 11 | public class TextRulesRepo: NSObject { 12 | 13 | /// Array of rules 14 | public var rules: [TextRule] = [] 15 | 16 | /// If sets to `true` it will pass validation if `text` is `nil` 17 | public var ignoreNil: Bool = false 18 | 19 | /// Add rule using this. You can use `RulesSet` for preset rules. 20 | /// - Parameter rule: Use `RulesSet` for preset or extent it for your own rules. 21 | public func add(_ rule: TextRule) { 22 | rules.append(rule) 23 | } 24 | 25 | /// Remove all rules set before using `add(_:)` method. 26 | public func removeAllRules() { 27 | rules.removeAll() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/TFManager/Validatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validatable.swift 3 | // TFManager 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Adds validation to any object using `TextRulesSet` or any `TextRule`. 11 | public protocol Validatable: AnyObject { 12 | 13 | /// Set `TextRulesRepo()` to it. 14 | var rulesRepo: TextRulesRepo { get set } 15 | 16 | /// Return `text` that will be validated. 17 | var textToValidate: String? { get } 18 | 19 | /// Don't implement this unless you know what you're doing. 20 | /// - Returns: Result of validation 21 | func validate() -> ValidationResult 22 | 23 | /// Will call when validation fails. 24 | func validationDidFail(_ rule: TextRule) 25 | 26 | /// Will call when validation pass. 27 | func validationDidPass() 28 | } 29 | 30 | public extension Validatable { 31 | func validate() -> ValidationResult { 32 | if rulesRepo.ignoreNil && textToValidate?.isEmpty ?? true { 33 | validationDidPass() 34 | return ValidationResult(isValid: true, message: nil) 35 | } 36 | for rule in rulesRepo.rules { 37 | if !rule.validate(textToValidate!) { 38 | validationDidFail(rule) 39 | return ValidationResult(isValid: false, message: rule.message) 40 | } 41 | } 42 | validationDidPass() 43 | return ValidationResult(isValid: true, message: nil) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/TFManager/ValidatableField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValidatableField.swift 3 | // MyKuyaClient 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import UIKit 9 | 10 | /// A subclass of `UITextField` supports validation. 11 | /// 12 | /// You can add rules like this: 13 | /// ```swift 14 | /// yourField.rulesRepo.add(TextRulesSet.mail) 15 | /// ``` 16 | /// --- 17 | /// If you use your custom `UITextField` make sure subclass from `ValidatableField` and if you have a custom `UIControl` it should conforms to `Validatable` protocol. 18 | /// 19 | open class ValidatableField: UITextField, Validatable { 20 | 21 | /// You add or remove rules using this property. Use `RulesSet` for some default rules or add your own using `Rule` protocol. 22 | public var rulesRepo = TextRulesRepo() 23 | 24 | /// Returns `text` property of `UITextField` 25 | public var textToValidate: String? { text } 26 | 27 | /// Will call when validation pass. Override this if you want adjust the UI. 28 | open func validationDidPass() { } 29 | 30 | /// Will call when validation fails. Override this if you want adjust the UI. 31 | open func validationDidFail(_ rule: TextRule) { } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/TFManager/ValidationResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValidationResult.swift 3 | // TFManager 4 | // 5 | // Created by Hosein Abbaspour on 4/17/22. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Result object used in `Validatable` and `TFManager` 11 | public struct ValidationResult { 12 | 13 | /// Boolean determines whether validatoin did pass or not. 14 | public var isValid: Bool 15 | 16 | /// holds error message. 17 | public var message: String? 18 | } 19 | -------------------------------------------------------------------------------- /TFManager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'TFManager' 3 | s.version = '1.1.0' 4 | s.summary = 'Add validations to your text fields, Group them together and navigate through them via return button and accessory view.' 5 | s.homepage = 'https://github.com/abspr/TFManager' 6 | s.license = { :type => 'MIT', :file => 'LICENSE.md' } 7 | s.author = { 'Hosein Abbaspour' => 'hosein@me.com' } 8 | s.source = { :git => 'https://github.com/abspr/TFManager.git', :tag => s.version.to_s } 9 | s.ios.deployment_target = '11.0' 10 | s.swift_version = '5.0' 11 | s.source_files = 'Sources/TFManager/**/*' 12 | end 13 | -------------------------------------------------------------------------------- /TFManagerDemo/Podfile: -------------------------------------------------------------------------------- 1 | # platform :ios, '11.0' 2 | 3 | target 'TFManagerDemo' do 4 | pod 'TFManager', :path => '../' 5 | end 6 | -------------------------------------------------------------------------------- /TFManagerDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TFManager (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - TFManager (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | TFManager: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | TFManager: 55ad13b1da90fd1b20a0619141b78597a1aff028 13 | 14 | PODFILE CHECKSUM: e7b52e5382700e488554835ce8f8e75061a770d4 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Headers/Public/TFManager/TFManager-umbrella.h: -------------------------------------------------------------------------------- 1 | ../../../Target Support Files/TFManager/TFManager-umbrella.h -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Headers/Public/TFManager/TFManager.modulemap: -------------------------------------------------------------------------------- 1 | ../../../Target Support Files/TFManager/TFManager.modulemap -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Local Podspecs/TFManager.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TFManager", 3 | "version": "1.0.0", 4 | "summary": "Add validations to your text fields, Group them together and navigate through them via return button and its bar.", 5 | "homepage": "https://github.com/abspr/TFManager", 6 | "license": { 7 | "type": "MIT", 8 | "file": "LICENSE.md" 9 | }, 10 | "authors": { 11 | "Hosein Abbaspour": "hosein@me.com" 12 | }, 13 | "source": { 14 | "git": "https://github.com/abspr/TFManager.git", 15 | "tag": "1.0.0" 16 | }, 17 | "platforms": { 18 | "ios": "11.0" 19 | }, 20 | "swift_versions": "5.0", 21 | "source_files": "Sources/TFManager/**/*", 22 | "swift_version": "5.0" 23 | } 24 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TFManager (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - TFManager (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | TFManager: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | TFManager: 55ad13b1da90fd1b20a0619141b78597a1aff028 13 | 14 | PODFILE CHECKSUM: e7b52e5382700e488554835ce8f8e75061a770d4 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2E4E1BC0FA2B16A54BE9892CD00B0189 /* Pods-TFManagerDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DB62B4016E95F19040951C337E29B0CD /* Pods-TFManagerDemo-dummy.m */; }; 11 | 3BE6A74A55EED856490FC04B3EBE7D79 /* TFManager-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 783144D7FF8526824A8F0C0344187A30 /* TFManager-dummy.m */; }; 12 | 99A0010E1EA47E10C4BA60B276858626 /* Pods-TFManagerDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = BAB75F19DDF145D3916826DD31904B2F /* Pods-TFManagerDemo-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; 13 | F728198398EE72E7D208D7EBC52D453B /* TFManager-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB1782F0FD82901074E3C0BC896AB07 /* TFManager-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; 14 | FEED888A280C1A13003CC779 /* TextRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8886280C1A13003CC779 /* TextRules.swift */; }; 15 | FEED888D280C1A13003CC779 /* ValidatableField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8889280C1A13003CC779 /* ValidatableField.swift */; }; 16 | FEED8893280C42A8003CC779 /* ValidationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8892280C42A8003CC779 /* ValidationResult.swift */; }; 17 | FEED889A280C651D003CC779 /* TextRulesRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8898280C651D003CC779 /* TextRulesRepo.swift */; }; 18 | FEED889B280C651D003CC779 /* Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8899280C651D003CC779 /* Validatable.swift */; }; 19 | FEED889D280C6A94003CC779 /* TFManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED889C280C6A94003CC779 /* TFManager.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 7EFC3569592A10DF350B01597190F899 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = CD81A75480014BFEDC9DFC4E65A6E7F3; 28 | remoteInfo = TFManager; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 0B23AD29F011DD9A864681BCC078692D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 34 | 1A86B28083A75235F332EEDA3E152644 /* TFManager-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TFManager-prefix.pch"; sourceTree = ""; }; 35 | 238CF0E6F5C6A27680C5414BD4BD3DBC /* LICENSE.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 36 | 23C082F039C9DFE23D7681F4DDE0E01B /* TFManager.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TFManager.release.xcconfig; sourceTree = ""; }; 37 | 2F1BF0EEE5A38CDDD4780F8CA0A417B7 /* Pods-TFManagerDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TFManagerDemo-acknowledgements.plist"; sourceTree = ""; }; 38 | 4BB1782F0FD82901074E3C0BC896AB07 /* TFManager-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TFManager-umbrella.h"; sourceTree = ""; }; 39 | 783144D7FF8526824A8F0C0344187A30 /* TFManager-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TFManager-dummy.m"; sourceTree = ""; }; 40 | 8935851A1BBEBA808373A73581C8982C /* Pods-TFManagerDemo */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "Pods-TFManagerDemo"; path = "libPods-TFManagerDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 42 | 9E1B2F49A7163041CA9EE1D4861697A7 /* TFManager.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = TFManager.modulemap; sourceTree = ""; }; 43 | A462A73D57E94F85C299E1066173F374 /* Pods-TFManagerDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-TFManagerDemo.modulemap"; sourceTree = ""; }; 44 | B9514A8AF6538723F3C370A67FBD05D3 /* Pods-TFManagerDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TFManagerDemo.release.xcconfig"; sourceTree = ""; }; 45 | BAB75F19DDF145D3916826DD31904B2F /* Pods-TFManagerDemo-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TFManagerDemo-umbrella.h"; sourceTree = ""; }; 46 | BAC1E02C753EF0BF3EFAEBBCB8BA2B90 /* TFManager */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = TFManager; path = libTFManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | BB344007B48900F883DB2A88EA751FE2 /* TFManager.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = TFManager.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 48 | DB62B4016E95F19040951C337E29B0CD /* Pods-TFManagerDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TFManagerDemo-dummy.m"; sourceTree = ""; }; 49 | EA0E5F5FE9090A11B99650864B7B5826 /* TFManager.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TFManager.debug.xcconfig; sourceTree = ""; }; 50 | F04B090EC8CD990ECF6998F6EBFAE897 /* Pods-TFManagerDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TFManagerDemo.debug.xcconfig"; sourceTree = ""; }; 51 | F9D7432D264BD371F5E6DC5DD81A061C /* Pods-TFManagerDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TFManagerDemo-acknowledgements.markdown"; sourceTree = ""; }; 52 | FEED8886280C1A13003CC779 /* TextRules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TextRules.swift; path = Sources/TFManager/TextRules.swift; sourceTree = ""; }; 53 | FEED8889280C1A13003CC779 /* ValidatableField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ValidatableField.swift; path = Sources/TFManager/ValidatableField.swift; sourceTree = ""; }; 54 | FEED8892280C42A8003CC779 /* ValidationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ValidationResult.swift; path = Sources/TFManager/ValidationResult.swift; sourceTree = ""; }; 55 | FEED8898280C651D003CC779 /* TextRulesRepo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TextRulesRepo.swift; path = Sources/TFManager/TextRulesRepo.swift; sourceTree = ""; }; 56 | FEED8899280C651D003CC779 /* Validatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Validatable.swift; path = Sources/TFManager/Validatable.swift; sourceTree = ""; }; 57 | FEED889C280C6A94003CC779 /* TFManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TFManager.swift; path = Sources/TFManager/TFManager.swift; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 2CE49C43812EE1203A87E2FDB022DB71 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | F9904E3A0ECCF773F3D1F9D091EC0EDE /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 124D8A1E5CA26A4FC17963685B4A959B /* TFManager */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | FEED8889280C1A13003CC779 /* ValidatableField.swift */, 82 | FEED8886280C1A13003CC779 /* TextRules.swift */, 83 | FEED8898280C651D003CC779 /* TextRulesRepo.swift */, 84 | FEED8899280C651D003CC779 /* Validatable.swift */, 85 | FEED8892280C42A8003CC779 /* ValidationResult.swift */, 86 | FEED889C280C6A94003CC779 /* TFManager.swift */, 87 | 20D4CA4A69957CBEECA7A0C8A311B69B /* Pod */, 88 | 85375CAA6D558A5246C4E25201021A44 /* Support Files */, 89 | ); 90 | name = TFManager; 91 | path = ../..; 92 | sourceTree = ""; 93 | }; 94 | 20D4CA4A69957CBEECA7A0C8A311B69B /* Pod */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 238CF0E6F5C6A27680C5414BD4BD3DBC /* LICENSE.md */, 98 | 0B23AD29F011DD9A864681BCC078692D /* README.md */, 99 | BB344007B48900F883DB2A88EA751FE2 /* TFManager.podspec */, 100 | ); 101 | name = Pod; 102 | sourceTree = ""; 103 | }; 104 | 85375CAA6D558A5246C4E25201021A44 /* Support Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 9E1B2F49A7163041CA9EE1D4861697A7 /* TFManager.modulemap */, 108 | 783144D7FF8526824A8F0C0344187A30 /* TFManager-dummy.m */, 109 | 1A86B28083A75235F332EEDA3E152644 /* TFManager-prefix.pch */, 110 | 4BB1782F0FD82901074E3C0BC896AB07 /* TFManager-umbrella.h */, 111 | EA0E5F5FE9090A11B99650864B7B5826 /* TFManager.debug.xcconfig */, 112 | 23C082F039C9DFE23D7681F4DDE0E01B /* TFManager.release.xcconfig */, 113 | ); 114 | name = "Support Files"; 115 | path = "TFManagerDemo/Pods/Target Support Files/TFManager"; 116 | sourceTree = ""; 117 | }; 118 | 956431126E9404B4A162D19EE4482FE4 /* Targets Support Files */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | D9FB1881AFBCFFC41657F780F485B27F /* Pods-TFManagerDemo */, 122 | ); 123 | name = "Targets Support Files"; 124 | sourceTree = ""; 125 | }; 126 | C89C0E7E224E221A0B0BDE79D6867B06 /* Products */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 8935851A1BBEBA808373A73581C8982C /* Pods-TFManagerDemo */, 130 | BAC1E02C753EF0BF3EFAEBBCB8BA2B90 /* TFManager */, 131 | ); 132 | name = Products; 133 | sourceTree = ""; 134 | }; 135 | CF1408CF629C7361332E53B88F7BD30C = { 136 | isa = PBXGroup; 137 | children = ( 138 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, 139 | E431F6A4C69B86FCDEF8B4A4B1AEC85B /* Development Pods */, 140 | D89477F20FB1DE18A04690586D7808C4 /* Frameworks */, 141 | C89C0E7E224E221A0B0BDE79D6867B06 /* Products */, 142 | 956431126E9404B4A162D19EE4482FE4 /* Targets Support Files */, 143 | ); 144 | sourceTree = ""; 145 | }; 146 | D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | ); 150 | name = Frameworks; 151 | sourceTree = ""; 152 | }; 153 | D9FB1881AFBCFFC41657F780F485B27F /* Pods-TFManagerDemo */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | A462A73D57E94F85C299E1066173F374 /* Pods-TFManagerDemo.modulemap */, 157 | F9D7432D264BD371F5E6DC5DD81A061C /* Pods-TFManagerDemo-acknowledgements.markdown */, 158 | 2F1BF0EEE5A38CDDD4780F8CA0A417B7 /* Pods-TFManagerDemo-acknowledgements.plist */, 159 | DB62B4016E95F19040951C337E29B0CD /* Pods-TFManagerDemo-dummy.m */, 160 | BAB75F19DDF145D3916826DD31904B2F /* Pods-TFManagerDemo-umbrella.h */, 161 | F04B090EC8CD990ECF6998F6EBFAE897 /* Pods-TFManagerDemo.debug.xcconfig */, 162 | B9514A8AF6538723F3C370A67FBD05D3 /* Pods-TFManagerDemo.release.xcconfig */, 163 | ); 164 | name = "Pods-TFManagerDemo"; 165 | path = "Target Support Files/Pods-TFManagerDemo"; 166 | sourceTree = ""; 167 | }; 168 | E431F6A4C69B86FCDEF8B4A4B1AEC85B /* Development Pods */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 124D8A1E5CA26A4FC17963685B4A959B /* TFManager */, 172 | ); 173 | name = "Development Pods"; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXHeadersBuildPhase section */ 179 | 0BB1D660B5AEB21312AB76207BD47C49 /* Headers */ = { 180 | isa = PBXHeadersBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | F728198398EE72E7D208D7EBC52D453B /* TFManager-umbrella.h in Headers */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | E5EFE24AA39012EDEC3379615784EEFE /* Headers */ = { 188 | isa = PBXHeadersBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 99A0010E1EA47E10C4BA60B276858626 /* Pods-TFManagerDemo-umbrella.h in Headers */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXHeadersBuildPhase section */ 196 | 197 | /* Begin PBXNativeTarget section */ 198 | 4B7845337CCC99A5EFDE1A7FCD68657B /* Pods-TFManagerDemo */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = 9A3BF051BED8F224F60D7A71BD297391 /* Build configuration list for PBXNativeTarget "Pods-TFManagerDemo" */; 201 | buildPhases = ( 202 | E5EFE24AA39012EDEC3379615784EEFE /* Headers */, 203 | 6CC364AA7191348DADAD58CD03B1FB9E /* Sources */, 204 | F9904E3A0ECCF773F3D1F9D091EC0EDE /* Frameworks */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | E672C2FE2E613CBBCB4A593BF0F82CB5 /* PBXTargetDependency */, 210 | ); 211 | name = "Pods-TFManagerDemo"; 212 | productName = "Pods-TFManagerDemo"; 213 | productReference = 8935851A1BBEBA808373A73581C8982C /* Pods-TFManagerDemo */; 214 | productType = "com.apple.product-type.library.static"; 215 | }; 216 | CD81A75480014BFEDC9DFC4E65A6E7F3 /* TFManager */ = { 217 | isa = PBXNativeTarget; 218 | buildConfigurationList = 5B5492EFB9DAE9B6F311C5090CFC5FBD /* Build configuration list for PBXNativeTarget "TFManager" */; 219 | buildPhases = ( 220 | 0BB1D660B5AEB21312AB76207BD47C49 /* Headers */, 221 | 57A21997667930B0551D17E0B51A8A36 /* Sources */, 222 | 2CE49C43812EE1203A87E2FDB022DB71 /* Frameworks */, 223 | 5B4CA9C8C45F5997EACDDED3BAB25C1C /* Copy generated compatibility header */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | ); 229 | name = TFManager; 230 | productName = TFManager; 231 | productReference = BAC1E02C753EF0BF3EFAEBBCB8BA2B90 /* TFManager */; 232 | productType = "com.apple.product-type.library.static"; 233 | }; 234 | /* End PBXNativeTarget section */ 235 | 236 | /* Begin PBXProject section */ 237 | BFDFE7DC352907FC980B868725387E98 /* Project object */ = { 238 | isa = PBXProject; 239 | attributes = { 240 | LastSwiftUpdateCheck = 1240; 241 | LastUpgradeCheck = 1240; 242 | }; 243 | buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; 244 | compatibilityVersion = "Xcode 13.0"; 245 | developmentRegion = en; 246 | hasScannedForEncodings = 0; 247 | knownRegions = ( 248 | Base, 249 | en, 250 | ); 251 | mainGroup = CF1408CF629C7361332E53B88F7BD30C; 252 | productRefGroup = C89C0E7E224E221A0B0BDE79D6867B06 /* Products */; 253 | projectDirPath = ""; 254 | projectRoot = ""; 255 | targets = ( 256 | 4B7845337CCC99A5EFDE1A7FCD68657B /* Pods-TFManagerDemo */, 257 | CD81A75480014BFEDC9DFC4E65A6E7F3 /* TFManager */, 258 | ); 259 | }; 260 | /* End PBXProject section */ 261 | 262 | /* Begin PBXShellScriptBuildPhase section */ 263 | 5B4CA9C8C45F5997EACDDED3BAB25C1C /* Copy generated compatibility header */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputFileListPaths = ( 269 | ); 270 | inputPaths = ( 271 | "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h", 272 | "${PODS_ROOT}/Headers/Public/TFManager/TFManager.modulemap", 273 | "${PODS_ROOT}/Headers/Public/TFManager/TFManager-umbrella.h", 274 | ); 275 | name = "Copy generated compatibility header"; 276 | outputFileListPaths = ( 277 | ); 278 | outputPaths = ( 279 | "${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap", 280 | "${BUILT_PRODUCTS_DIR}/TFManager-umbrella.h", 281 | "${BUILT_PRODUCTS_DIR}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h", 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | shellPath = /bin/sh; 285 | shellScript = "COMPATIBILITY_HEADER_PATH=\"${BUILT_PRODUCTS_DIR}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h\"\nMODULE_MAP_PATH=\"${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap\"\n\nditto \"${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h\" \"${COMPATIBILITY_HEADER_PATH}\"\nditto \"${PODS_ROOT}/Headers/Public/TFManager/TFManager.modulemap\" \"${MODULE_MAP_PATH}\"\nditto \"${PODS_ROOT}/Headers/Public/TFManager/TFManager-umbrella.h\" \"${BUILT_PRODUCTS_DIR}\"\nprintf \"\\n\\nmodule ${PRODUCT_MODULE_NAME}.Swift {\\n header \\\"${COMPATIBILITY_HEADER_PATH}\\\"\\n requires objc\\n}\\n\" >> \"${MODULE_MAP_PATH}\"\n"; 286 | }; 287 | /* End PBXShellScriptBuildPhase section */ 288 | 289 | /* Begin PBXSourcesBuildPhase section */ 290 | 57A21997667930B0551D17E0B51A8A36 /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | FEED888A280C1A13003CC779 /* TextRules.swift in Sources */, 295 | FEED888D280C1A13003CC779 /* ValidatableField.swift in Sources */, 296 | FEED8893280C42A8003CC779 /* ValidationResult.swift in Sources */, 297 | FEED889A280C651D003CC779 /* TextRulesRepo.swift in Sources */, 298 | FEED889B280C651D003CC779 /* Validatable.swift in Sources */, 299 | 3BE6A74A55EED856490FC04B3EBE7D79 /* TFManager-dummy.m in Sources */, 300 | FEED889D280C6A94003CC779 /* TFManager.swift in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | 6CC364AA7191348DADAD58CD03B1FB9E /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | 2E4E1BC0FA2B16A54BE9892CD00B0189 /* Pods-TFManagerDemo-dummy.m in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXSourcesBuildPhase section */ 313 | 314 | /* Begin PBXTargetDependency section */ 315 | E672C2FE2E613CBBCB4A593BF0F82CB5 /* PBXTargetDependency */ = { 316 | isa = PBXTargetDependency; 317 | name = TFManager; 318 | target = CD81A75480014BFEDC9DFC4E65A6E7F3 /* TFManager */; 319 | targetProxy = 7EFC3569592A10DF350B01597190F899 /* PBXContainerItemProxy */; 320 | }; 321 | /* End PBXTargetDependency section */ 322 | 323 | /* Begin XCBuildConfiguration section */ 324 | 659406831D93AF04D72954803296B5EF /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | baseConfigurationReference = 23C082F039C9DFE23D7681F4DDE0E01B /* TFManager.release.xcconfig */; 327 | buildSettings = { 328 | CLANG_ENABLE_OBJC_WEAK = NO; 329 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 331 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 332 | GCC_PREFIX_HEADER = "Target Support Files/TFManager/TFManager-prefix.pch"; 333 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 334 | MODULEMAP_FILE = Headers/Public/TFManager/TFManager.modulemap; 335 | OTHER_LDFLAGS = ""; 336 | OTHER_LIBTOOLFLAGS = ""; 337 | PRIVATE_HEADERS_FOLDER_PATH = ""; 338 | PRODUCT_MODULE_NAME = TFManager; 339 | PRODUCT_NAME = TFManager; 340 | PUBLIC_HEADERS_FOLDER_PATH = ""; 341 | SDKROOT = iphoneos; 342 | SKIP_INSTALL = YES; 343 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 344 | SWIFT_VERSION = 5.0; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | VALIDATE_PRODUCT = YES; 347 | }; 348 | name = Release; 349 | }; 350 | 70FF5C2B6ED61B9672378BDFCC7CDBBE /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | baseConfigurationReference = F04B090EC8CD990ECF6998F6EBFAE897 /* Pods-TFManagerDemo.debug.xcconfig */; 353 | buildSettings = { 354 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 355 | CLANG_ENABLE_OBJC_WEAK = NO; 356 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 357 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 358 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 359 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 360 | MACH_O_TYPE = staticlib; 361 | MODULEMAP_FILE = "Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.modulemap"; 362 | OTHER_LDFLAGS = ""; 363 | OTHER_LIBTOOLFLAGS = ""; 364 | PODS_ROOT = "$(SRCROOT)"; 365 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 366 | SDKROOT = iphoneos; 367 | SKIP_INSTALL = YES; 368 | TARGETED_DEVICE_FAMILY = "1,2"; 369 | }; 370 | name = Debug; 371 | }; 372 | 903A0004D3E6651EFD5D2E16214D101B /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ALWAYS_SEARCH_USER_PATHS = NO; 376 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 380 | CLANG_CXX_LIBRARY = "libc++"; 381 | CLANG_ENABLE_MODULES = YES; 382 | CLANG_ENABLE_OBJC_ARC = YES; 383 | CLANG_ENABLE_OBJC_WEAK = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 399 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 400 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 401 | CLANG_WARN_STRICT_PROTOTYPES = YES; 402 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 403 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 404 | CLANG_WARN_UNREACHABLE_CODE = YES; 405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 406 | COPY_PHASE_STRIP = NO; 407 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 408 | ENABLE_NS_ASSERTIONS = NO; 409 | ENABLE_STRICT_OBJC_MSGSEND = YES; 410 | GCC_C_LANGUAGE_STANDARD = gnu11; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_PREPROCESSOR_DEFINITIONS = ( 413 | "POD_CONFIGURATION_RELEASE=1", 414 | "$(inherited)", 415 | ); 416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 418 | GCC_WARN_UNDECLARED_SELECTOR = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 420 | GCC_WARN_UNUSED_FUNCTION = YES; 421 | GCC_WARN_UNUSED_VARIABLE = YES; 422 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 423 | MTL_ENABLE_DEBUG_INFO = NO; 424 | MTL_FAST_MATH = YES; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | STRIP_INSTALLED_PRODUCT = NO; 427 | SWIFT_COMPILATION_MODE = wholemodule; 428 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 429 | SWIFT_VERSION = 5.0; 430 | SYMROOT = "${SRCROOT}/../build"; 431 | }; 432 | name = Release; 433 | }; 434 | B4EFE046ACF8F37157F6E322C7FCFC28 /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 439 | CLANG_ANALYZER_NONNULL = YES; 440 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 442 | CLANG_CXX_LIBRARY = "libc++"; 443 | CLANG_ENABLE_MODULES = YES; 444 | CLANG_ENABLE_OBJC_ARC = YES; 445 | CLANG_ENABLE_OBJC_WEAK = YES; 446 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 447 | CLANG_WARN_BOOL_CONVERSION = YES; 448 | CLANG_WARN_COMMA = YES; 449 | CLANG_WARN_CONSTANT_CONVERSION = YES; 450 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 452 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INFINITE_RECURSION = YES; 456 | CLANG_WARN_INT_CONVERSION = YES; 457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 459 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 462 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 463 | CLANG_WARN_STRICT_PROTOTYPES = YES; 464 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 465 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 466 | CLANG_WARN_UNREACHABLE_CODE = YES; 467 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 468 | COPY_PHASE_STRIP = NO; 469 | DEBUG_INFORMATION_FORMAT = dwarf; 470 | ENABLE_STRICT_OBJC_MSGSEND = YES; 471 | ENABLE_TESTABILITY = YES; 472 | GCC_C_LANGUAGE_STANDARD = gnu11; 473 | GCC_DYNAMIC_NO_PIC = NO; 474 | GCC_NO_COMMON_BLOCKS = YES; 475 | GCC_OPTIMIZATION_LEVEL = 0; 476 | GCC_PREPROCESSOR_DEFINITIONS = ( 477 | "POD_CONFIGURATION_DEBUG=1", 478 | "DEBUG=1", 479 | "$(inherited)", 480 | ); 481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 483 | GCC_WARN_UNDECLARED_SELECTOR = YES; 484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 485 | GCC_WARN_UNUSED_FUNCTION = YES; 486 | GCC_WARN_UNUSED_VARIABLE = YES; 487 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 488 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 489 | MTL_FAST_MATH = YES; 490 | ONLY_ACTIVE_ARCH = YES; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | STRIP_INSTALLED_PRODUCT = NO; 493 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 494 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 495 | SWIFT_VERSION = 5.0; 496 | SYMROOT = "${SRCROOT}/../build"; 497 | }; 498 | name = Debug; 499 | }; 500 | E494B44C036E414DB15F3B033A3E0A4C /* Release */ = { 501 | isa = XCBuildConfiguration; 502 | baseConfigurationReference = B9514A8AF6538723F3C370A67FBD05D3 /* Pods-TFManagerDemo.release.xcconfig */; 503 | buildSettings = { 504 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 505 | CLANG_ENABLE_OBJC_WEAK = NO; 506 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 507 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 508 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 509 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 510 | MACH_O_TYPE = staticlib; 511 | MODULEMAP_FILE = "Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.modulemap"; 512 | OTHER_LDFLAGS = ""; 513 | OTHER_LIBTOOLFLAGS = ""; 514 | PODS_ROOT = "$(SRCROOT)"; 515 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 516 | SDKROOT = iphoneos; 517 | SKIP_INSTALL = YES; 518 | TARGETED_DEVICE_FAMILY = "1,2"; 519 | VALIDATE_PRODUCT = YES; 520 | }; 521 | name = Release; 522 | }; 523 | FBAD7089A6BB07D69093E449DA8F4FC3 /* Debug */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = EA0E5F5FE9090A11B99650864B7B5826 /* TFManager.debug.xcconfig */; 526 | buildSettings = { 527 | CLANG_ENABLE_OBJC_WEAK = NO; 528 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 529 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 530 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 531 | GCC_PREFIX_HEADER = "Target Support Files/TFManager/TFManager-prefix.pch"; 532 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 533 | MODULEMAP_FILE = Headers/Public/TFManager/TFManager.modulemap; 534 | OTHER_LDFLAGS = ""; 535 | OTHER_LIBTOOLFLAGS = ""; 536 | PRIVATE_HEADERS_FOLDER_PATH = ""; 537 | PRODUCT_MODULE_NAME = TFManager; 538 | PRODUCT_NAME = TFManager; 539 | PUBLIC_HEADERS_FOLDER_PATH = ""; 540 | SDKROOT = iphoneos; 541 | SKIP_INSTALL = YES; 542 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 543 | SWIFT_VERSION = 5.0; 544 | TARGETED_DEVICE_FAMILY = "1,2"; 545 | }; 546 | name = Debug; 547 | }; 548 | /* End XCBuildConfiguration section */ 549 | 550 | /* Begin XCConfigurationList section */ 551 | 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { 552 | isa = XCConfigurationList; 553 | buildConfigurations = ( 554 | B4EFE046ACF8F37157F6E322C7FCFC28 /* Debug */, 555 | 903A0004D3E6651EFD5D2E16214D101B /* Release */, 556 | ); 557 | defaultConfigurationIsVisible = 0; 558 | defaultConfigurationName = Release; 559 | }; 560 | 5B5492EFB9DAE9B6F311C5090CFC5FBD /* Build configuration list for PBXNativeTarget "TFManager" */ = { 561 | isa = XCConfigurationList; 562 | buildConfigurations = ( 563 | FBAD7089A6BB07D69093E449DA8F4FC3 /* Debug */, 564 | 659406831D93AF04D72954803296B5EF /* Release */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | 9A3BF051BED8F224F60D7A71BD297391 /* Build configuration list for PBXNativeTarget "Pods-TFManagerDemo" */ = { 570 | isa = XCConfigurationList; 571 | buildConfigurations = ( 572 | 70FF5C2B6ED61B9672378BDFCC7CDBBE /* Debug */, 573 | E494B44C036E414DB15F3B033A3E0A4C /* Release */, 574 | ); 575 | defaultConfigurationIsVisible = 0; 576 | defaultConfigurationName = Release; 577 | }; 578 | /* End XCConfigurationList section */ 579 | }; 580 | rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; 581 | } 582 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## TFManager 5 | 6 | Copyright (c) 2022 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | Generated by CocoaPods - https://cocoapods.org 24 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2022 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 32 | THE SOFTWARE. 33 | 34 | License 35 | MIT 36 | Title 37 | TFManager 38 | Type 39 | PSGroupSpecifier 40 | 41 | 42 | FooterText 43 | Generated by CocoaPods - https://cocoapods.org 44 | Title 45 | 46 | Type 47 | PSGroupSpecifier 48 | 49 | 50 | StringsTable 51 | Acknowledgements 52 | Title 53 | Acknowledgements 54 | 55 | 56 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TFManagerDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TFManagerDemo 5 | @end 6 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_TFManagerDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_TFManagerDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" "${PODS_CONFIGURATION_BUILD_DIR}/TFManager" /usr/lib/swift 5 | OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/TFManager/TFManager.modulemap" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"TFManager" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/TFManager/TFManager.modulemap" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | SWIFT_INCLUDE_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/TFManager" 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.modulemap: -------------------------------------------------------------------------------- 1 | module Pods_TFManagerDemo { 2 | umbrella header "Pods-TFManagerDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" "${PODS_CONFIGURATION_BUILD_DIR}/TFManager" /usr/lib/swift 5 | OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/TFManager/TFManager.modulemap" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"TFManager" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/TFManager/TFManager.modulemap" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | SWIFT_INCLUDE_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/TFManager" 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_TFManager : NSObject 3 | @end 4 | @implementation PodsDummy_TFManager 5 | @end 6 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double TFManagerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char TFManagerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/TFManager 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -import-underlying-module -Xcc -fmodule-map-file="${SRCROOT}/${MODULEMAP_FILE}" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager.modulemap: -------------------------------------------------------------------------------- 1 | module TFManager { 2 | umbrella header "TFManager-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /TFManagerDemo/Pods/Target Support Files/TFManager/TFManager.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/TFManager 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -import-underlying-module -Xcc -fmodule-map-file="${SRCROOT}/${MODULEMAP_FILE}" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4467FBC4BFD7879C607F2372 /* libPods-TFManagerDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90B17154B1F770548A212660 /* libPods-TFManagerDemo.a */; }; 11 | FEE3EC64281575F700869C74 /* CustomValidatableField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE3EC63281575F700869C74 /* CustomValidatableField.swift */; }; 12 | FEED8866280AC68B003CC779 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8865280AC68B003CC779 /* AppDelegate.swift */; }; 13 | FEED886A280AC68B003CC779 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEED8869280AC68B003CC779 /* ViewController.swift */; }; 14 | FEED886D280AC68B003CC779 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FEED886B280AC68B003CC779 /* Main.storyboard */; }; 15 | FEED886F280AC68C003CC779 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FEED886E280AC68C003CC779 /* Assets.xcassets */; }; 16 | FEED8872280AC68C003CC779 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FEED8870280AC68C003CC779 /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 65B12019D66E3DDA0B5D9DA9 /* Pods-TFManagerDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TFManagerDemo.debug.xcconfig"; path = "Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.debug.xcconfig"; sourceTree = ""; }; 21 | 90B17154B1F770548A212660 /* libPods-TFManagerDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TFManagerDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 933BA34010181A29A6C41E45 /* Pods-TFManagerDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TFManagerDemo.release.xcconfig"; path = "Target Support Files/Pods-TFManagerDemo/Pods-TFManagerDemo.release.xcconfig"; sourceTree = ""; }; 23 | FEE3EC63281575F700869C74 /* CustomValidatableField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomValidatableField.swift; sourceTree = ""; }; 24 | FEED8862280AC68B003CC779 /* TFManagerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TFManagerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | FEED8865280AC68B003CC779 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | FEED8869280AC68B003CC779 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | FEED886C280AC68B003CC779 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | FEED886E280AC68C003CC779 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | FEED8871280AC68C003CC779 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | FEED8873280AC68C003CC779 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | FEED885F280AC68B003CC779 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 4467FBC4BFD7879C607F2372 /* libPods-TFManagerDemo.a in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 447EB5A4D91C507AF4C9625D /* Frameworks */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 90B17154B1F770548A212660 /* libPods-TFManagerDemo.a */, 49 | ); 50 | name = Frameworks; 51 | sourceTree = ""; 52 | }; 53 | AA3A5136B78AB5C490EC7E9F /* Pods */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 65B12019D66E3DDA0B5D9DA9 /* Pods-TFManagerDemo.debug.xcconfig */, 57 | 933BA34010181A29A6C41E45 /* Pods-TFManagerDemo.release.xcconfig */, 58 | ); 59 | path = Pods; 60 | sourceTree = ""; 61 | }; 62 | FEED8859280AC68B003CC779 = { 63 | isa = PBXGroup; 64 | children = ( 65 | FEED8864280AC68B003CC779 /* TFManagerDemo */, 66 | FEED8863280AC68B003CC779 /* Products */, 67 | AA3A5136B78AB5C490EC7E9F /* Pods */, 68 | 447EB5A4D91C507AF4C9625D /* Frameworks */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | FEED8863280AC68B003CC779 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | FEED8862280AC68B003CC779 /* TFManagerDemo.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | FEED8864280AC68B003CC779 /* TFManagerDemo */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | FEED8865280AC68B003CC779 /* AppDelegate.swift */, 84 | FEED8869280AC68B003CC779 /* ViewController.swift */, 85 | FEE3EC63281575F700869C74 /* CustomValidatableField.swift */, 86 | FEED886B280AC68B003CC779 /* Main.storyboard */, 87 | FEED886E280AC68C003CC779 /* Assets.xcassets */, 88 | FEED8870280AC68C003CC779 /* LaunchScreen.storyboard */, 89 | FEED8873280AC68C003CC779 /* Info.plist */, 90 | ); 91 | path = TFManagerDemo; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | FEED8861280AC68B003CC779 /* TFManagerDemo */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = FEED8876280AC68C003CC779 /* Build configuration list for PBXNativeTarget "TFManagerDemo" */; 100 | buildPhases = ( 101 | 4F28EE05C8229C6798CC27AE /* [CP] Check Pods Manifest.lock */, 102 | FEED885E280AC68B003CC779 /* Sources */, 103 | FEED885F280AC68B003CC779 /* Frameworks */, 104 | FEED8860280AC68B003CC779 /* Resources */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = TFManagerDemo; 111 | productName = TFManagerDemo; 112 | productReference = FEED8862280AC68B003CC779 /* TFManagerDemo.app */; 113 | productType = "com.apple.product-type.application"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | FEED885A280AC68B003CC779 /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | BuildIndependentTargetsInParallel = 1; 122 | LastSwiftUpdateCheck = 1320; 123 | LastUpgradeCheck = 1320; 124 | TargetAttributes = { 125 | FEED8861280AC68B003CC779 = { 126 | CreatedOnToolsVersion = 13.2.1; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = FEED885D280AC68B003CC779 /* Build configuration list for PBXProject "TFManagerDemo" */; 131 | compatibilityVersion = "Xcode 13.0"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = FEED8859280AC68B003CC779; 139 | productRefGroup = FEED8863280AC68B003CC779 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | FEED8861280AC68B003CC779 /* TFManagerDemo */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | FEED8860280AC68B003CC779 /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | FEED8872280AC68C003CC779 /* LaunchScreen.storyboard in Resources */, 154 | FEED886F280AC68C003CC779 /* Assets.xcassets in Resources */, 155 | FEED886D280AC68B003CC779 /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | 4F28EE05C8229C6798CC27AE /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputFileListPaths = ( 168 | ); 169 | inputPaths = ( 170 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 171 | "${PODS_ROOT}/Manifest.lock", 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputFileListPaths = ( 175 | ); 176 | outputPaths = ( 177 | "$(DERIVED_FILE_DIR)/Pods-TFManagerDemo-checkManifestLockResult.txt", 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | shellPath = /bin/sh; 181 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 182 | showEnvVarsInLog = 0; 183 | }; 184 | /* End PBXShellScriptBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | FEED885E280AC68B003CC779 /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | FEED886A280AC68B003CC779 /* ViewController.swift in Sources */, 192 | FEED8866280AC68B003CC779 /* AppDelegate.swift in Sources */, 193 | FEE3EC64281575F700869C74 /* CustomValidatableField.swift in Sources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXSourcesBuildPhase section */ 198 | 199 | /* Begin PBXVariantGroup section */ 200 | FEED886B280AC68B003CC779 /* Main.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | FEED886C280AC68B003CC779 /* Base */, 204 | ); 205 | name = Main.storyboard; 206 | sourceTree = ""; 207 | }; 208 | FEED8870280AC68C003CC779 /* LaunchScreen.storyboard */ = { 209 | isa = PBXVariantGroup; 210 | children = ( 211 | FEED8871280AC68C003CC779 /* Base */, 212 | ); 213 | name = LaunchScreen.storyboard; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXVariantGroup section */ 217 | 218 | /* Begin XCBuildConfiguration section */ 219 | FEED8874280AC68C003CC779 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | CLANG_ANALYZER_NONNULL = YES; 224 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_ENABLE_OBJC_WEAK = YES; 230 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 231 | CLANG_WARN_BOOL_CONVERSION = YES; 232 | CLANG_WARN_COMMA = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INFINITE_RECURSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 242 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 243 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 246 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 247 | CLANG_WARN_STRICT_PROTOTYPES = YES; 248 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 249 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 250 | CLANG_WARN_UNREACHABLE_CODE = YES; 251 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 252 | COPY_PHASE_STRIP = NO; 253 | DEBUG_INFORMATION_FORMAT = dwarf; 254 | ENABLE_STRICT_OBJC_MSGSEND = YES; 255 | ENABLE_TESTABILITY = YES; 256 | GCC_C_LANGUAGE_STANDARD = gnu11; 257 | GCC_DYNAMIC_NO_PIC = NO; 258 | GCC_NO_COMMON_BLOCKS = YES; 259 | GCC_OPTIMIZATION_LEVEL = 0; 260 | GCC_PREPROCESSOR_DEFINITIONS = ( 261 | "DEBUG=1", 262 | "$(inherited)", 263 | ); 264 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 265 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 266 | GCC_WARN_UNDECLARED_SELECTOR = YES; 267 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 268 | GCC_WARN_UNUSED_FUNCTION = YES; 269 | GCC_WARN_UNUSED_VARIABLE = YES; 270 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 271 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 272 | MTL_FAST_MATH = YES; 273 | ONLY_ACTIVE_ARCH = YES; 274 | SDKROOT = iphoneos; 275 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 276 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 277 | }; 278 | name = Debug; 279 | }; 280 | FEED8875280AC68C003CC779 /* Release */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | ALWAYS_SEARCH_USER_PATHS = NO; 284 | CLANG_ANALYZER_NONNULL = YES; 285 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_MODULES = YES; 289 | CLANG_ENABLE_OBJC_ARC = YES; 290 | CLANG_ENABLE_OBJC_WEAK = YES; 291 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 292 | CLANG_WARN_BOOL_CONVERSION = YES; 293 | CLANG_WARN_COMMA = YES; 294 | CLANG_WARN_CONSTANT_CONVERSION = YES; 295 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 296 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 297 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN_ENUM_CONVERSION = YES; 300 | CLANG_WARN_INFINITE_RECURSION = YES; 301 | CLANG_WARN_INT_CONVERSION = YES; 302 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 303 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 307 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 308 | CLANG_WARN_STRICT_PROTOTYPES = YES; 309 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 310 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 311 | CLANG_WARN_UNREACHABLE_CODE = YES; 312 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 315 | ENABLE_NS_ASSERTIONS = NO; 316 | ENABLE_STRICT_OBJC_MSGSEND = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu11; 318 | GCC_NO_COMMON_BLOCKS = YES; 319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 321 | GCC_WARN_UNDECLARED_SELECTOR = YES; 322 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 323 | GCC_WARN_UNUSED_FUNCTION = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 326 | MTL_ENABLE_DEBUG_INFO = NO; 327 | MTL_FAST_MATH = YES; 328 | SDKROOT = iphoneos; 329 | SWIFT_COMPILATION_MODE = wholemodule; 330 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 331 | VALIDATE_PRODUCT = YES; 332 | }; 333 | name = Release; 334 | }; 335 | FEED8877280AC68C003CC779 /* Debug */ = { 336 | isa = XCBuildConfiguration; 337 | baseConfigurationReference = 65B12019D66E3DDA0B5D9DA9 /* Pods-TFManagerDemo.debug.xcconfig */; 338 | buildSettings = { 339 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 340 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 341 | CODE_SIGN_STYLE = Automatic; 342 | CURRENT_PROJECT_VERSION = 1; 343 | GENERATE_INFOPLIST_FILE = YES; 344 | INFOPLIST_FILE = TFManagerDemo/Info.plist; 345 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 346 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 347 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 348 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 349 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 350 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 351 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 352 | LD_RUNPATH_SEARCH_PATHS = ( 353 | "$(inherited)", 354 | "@executable_path/Frameworks", 355 | ); 356 | MARKETING_VERSION = 1.0; 357 | PRODUCT_BUNDLE_IDENTIFIER = com.abspr.TFManagerDemo; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | SWIFT_EMIT_LOC_STRINGS = YES; 360 | SWIFT_VERSION = 5.0; 361 | TARGETED_DEVICE_FAMILY = 1; 362 | }; 363 | name = Debug; 364 | }; 365 | FEED8878280AC68C003CC779 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | baseConfigurationReference = 933BA34010181A29A6C41E45 /* Pods-TFManagerDemo.release.xcconfig */; 368 | buildSettings = { 369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 370 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 371 | CODE_SIGN_STYLE = Automatic; 372 | CURRENT_PROJECT_VERSION = 1; 373 | GENERATE_INFOPLIST_FILE = YES; 374 | INFOPLIST_FILE = TFManagerDemo/Info.plist; 375 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 376 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 377 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 378 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 379 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 380 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 381 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@executable_path/Frameworks", 385 | ); 386 | MARKETING_VERSION = 1.0; 387 | PRODUCT_BUNDLE_IDENTIFIER = com.abspr.TFManagerDemo; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SWIFT_EMIT_LOC_STRINGS = YES; 390 | SWIFT_VERSION = 5.0; 391 | TARGETED_DEVICE_FAMILY = 1; 392 | }; 393 | name = Release; 394 | }; 395 | /* End XCBuildConfiguration section */ 396 | 397 | /* Begin XCConfigurationList section */ 398 | FEED885D280AC68B003CC779 /* Build configuration list for PBXProject "TFManagerDemo" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | FEED8874280AC68C003CC779 /* Debug */, 402 | FEED8875280AC68C003CC779 /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | defaultConfigurationName = Release; 406 | }; 407 | FEED8876280AC68C003CC779 /* Build configuration list for PBXNativeTarget "TFManagerDemo" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | FEED8877280AC68C003CC779 /* Debug */, 411 | FEED8878280AC68C003CC779 /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | defaultConfigurationName = Release; 415 | }; 416 | /* End XCConfigurationList section */ 417 | }; 418 | rootObject = FEED885A280AC68B003CC779 /* Project object */; 419 | } 420 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TFManagerDemo 4 | // 5 | // Created by Hosein Abbaspour on 4/16/22. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | return true 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/CustomValidatableField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomValidatableField.swift 3 | // TFManagerDemo 4 | // 5 | // Created by Hosein Abbaspour on 4/24/22. 6 | // 7 | 8 | import TFManager 9 | 10 | class CustomValidatableField: ValidatableField { 11 | 12 | private lazy var errorImageView: UIImageView = { 13 | let view = UIImageView() 14 | view.tintColor = .systemRed 15 | return view 16 | }() 17 | 18 | required init?(coder: NSCoder) { 19 | super.init(coder: coder) 20 | commonInit() 21 | } 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | commonInit() 26 | } 27 | 28 | private func commonInit() { 29 | rightViewMode = .always 30 | rightView = errorImageView 31 | } 32 | 33 | // from Validatable protocol: 34 | 35 | override func validationDidPass() { 36 | errorImageView.image = nil 37 | } 38 | 39 | override func validationDidFail(_ rule: TextRule) { 40 | errorImageView.image = UIImage(systemName: "exclamationmark.circle") 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TFManagerDemo/TFManagerDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TFManagerDemo 4 | // 5 | // Created by Hosein Abbaspour on 4/16/22. 6 | // 7 | 8 | import UIKit 9 | import TFManager 10 | 11 | class ViewController: UITableViewController { 12 | 13 | var fieldsManager = TFManager() 14 | 15 | @IBOutlet weak var nameField: UITextField! 16 | 17 | @IBOutlet weak var mailField: ValidatableField! { 18 | didSet { 19 | mailField.rulesRepo.add(TextRulesSet.mail()) 20 | } 21 | } 22 | 23 | @IBOutlet weak var ageField: CustomValidatableField! { 24 | didSet { 25 | ageField.rulesRepo.ignoreNil = true 26 | ageField.rulesRepo.add(TextRulesSet.numbersOnly()) 27 | ageField.rulesRepo.add(TextRulesSet.minLenght(1)) 28 | ageField.rulesRepo.add(TextRulesSet.maxLenght(2)) 29 | } 30 | } 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | fieldsManager.add([nameField, mailField, ageField]) 35 | fieldsManager.delegate = self 36 | } 37 | 38 | } 39 | 40 | extension ViewController: TFManagerDelegate { 41 | 42 | func textDidChange(_ textField: UITextField, validationResult: ValidationResult?) { 43 | guard let validationResult = validationResult, textField == mailField else { return } 44 | textField.textColor = validationResult.isValid ? .label : .systemRed 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Tests/TFManagerTests/TFManagerTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TFManager 3 | 4 | final class TFManagerTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(TFManager().text, "Hello, World!") 10 | } 11 | } 12 | --------------------------------------------------------------------------------