├── .codecov.yml ├── .gitignore ├── .swift-version ├── .travis.yml ├── LICENSE ├── README.md ├── SwiftCop.podspec ├── SwiftCop.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── SwiftCop.xcscheme ├── SwiftCop ├── Suspect.swift ├── SwiftCop.swift └── Trial.swift ├── SwiftCopExample ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon@2x.png │ │ └── icon@3x.png │ ├── Contents.json │ └── policeman.imageset │ │ ├── Contents.json │ │ └── policeman.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift ├── SwiftCopTests ├── Info.plist ├── SuspectTest.swift ├── SwiftCopTests.swift └── TrialTests.swift ├── SwiftCopUITests ├── Info.plist └── SwiftCopUITests.swift ├── swiftCop.png └── swiftCopExample.gif /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - SwiftCopExample/* 4 | - SwiftCopTests/* 5 | - SwiftCopUITests/* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode9 2 | language: objective-c 3 | 4 | script: 5 | - "xcodebuild -sdk iphonesimulator clean" 6 | - "xcodebuild -scheme SwiftCop -project SwiftCop.xcodeproj -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.0' build-for-testing" 7 | - "xcodebuild -scheme SwiftCop -project SwiftCop.xcodeproj -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.0' test-without-building" 8 | 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andres Canal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SwiftCop](swiftCop.png) 2 | 3 | SwiftCop is a validation library fully written in Swift and inspired by the clarity of [Ruby On Rails Active Record validations](http://guides.rubyonrails.org/active_record_validations.html). 4 | 5 | [![Build Status](https://travis-ci.org/andresinaka/SwiftCop.svg)](https://travis-ci.org/andresinaka/SwiftCop) [![codecov.io](https://codecov.io/github/andresinaka/SwiftCop/badge.svg?branch=master)](https://codecov.io/github/andresinaka/SwiftCop?branch=master) [![codecov.io](https://img.shields.io/badge/pod-v1.0.0-brightgreen.svg)]() 6 | 7 | ### Objective 8 | 9 | Build a standard drop-in library for validations in Swift while making it easily extensible for users to create custom validations. And avoid developers from writing over and over again the same code and validations for different projects. 10 | 11 | ## Features 12 | 13 | - Quick validations. 14 | - Super simple and declarative syntax. 15 | - Easily extensible. 16 | - Fully Swift 4.0 17 | 18 | ## Modules 19 | 20 | SwiftCop was built around three different concepts: 21 | 22 | ### Trial 23 | 24 | ```Trial``` is an ```Enum``` that implements the ```TrialProtocol``` 25 | 26 | ```swift 27 | public protocol TrialProtocol { 28 | func trial() -> ((_ evidence: String) -> Bool) 29 | } 30 | ``` 31 | 32 | We can use ```Trial``` to quickly validate ```strings```. ```SwiftCop``` ships with a very fully featured ```Trial``` implementation. The following trials are provided: 33 | 34 | #### ```Exclusion([String])``` 35 | This validates that the attributes are not included in the evidence string. 36 | 37 | ```swift 38 | let exclusionTrial = Trial.exclusion([".com",".ar", ".uy"]) 39 | let trial = exclusionTrial.trial() 40 | 41 | XCTAssertFalse(trial("http://www.nytimes.com")) 42 | XCTAssertFalse(trial("http://www.lanacion.com.ar")) 43 | XCTAssertTrue(trial("http://www.elpais.es")) 44 | ``` 45 | 46 | #### ```Format(String)``` 47 | This validates whether the evidence matches a given regular expression. 48 | 49 | ```swift 50 | let formatTrial = Trial.format("^#([a-f0-9]{6}|[a-f0-9]{3})$") // hexa number with # 51 | let trial = formatTrial.trial() 52 | 53 | XCTAssertTrue(trial("#57b5b5")) 54 | XCTAssertFalse(trial("57b5b5")) 55 | XCTAssertFalse(trial("#h7b5b5")) 56 | ``` 57 | 58 | #### ```Inclusion([String])``` 59 | This validates that the attributes are included in the evidence string. 60 | 61 | ```swift 62 | let inclusionTrial = Trial.inclusion([".com",".ar", ".uy"]) 63 | let trial = inclusionTrial.trial() 64 | 65 | XCTAssertTrue(trial("http://www.nytimes.com")) 66 | XCTAssertTrue(trial("http://www.lanacion.com.ar")) 67 | XCTAssertFalse(trial("http://www.elpais.es")) 68 | ``` 69 | 70 | #### ```Email``` 71 | This validates whether the evidence is an email or not. 72 | 73 | ```swift 74 | let emailTrial = Trial.email 75 | let trial = emailTrial.trial() 76 | XCTAssertTrue(trial("test@test.com")) 77 | ``` 78 | 79 | #### ```Length(Length,Any)``` 80 | This validates the length of given evidence: 81 | 82 | ```swift 83 | let lengthTrial = Trial.Length(.Is, 10) 84 | let trial = lengthTrial.trial() 85 | XCTAssertTrue(trial("0123456789")) 86 | ``` 87 | ```swift 88 | let lengthTrial = Trial.Length(.Minimum, 10) 89 | let trial = lengthTrial.trial() 90 | XCTAssertTrue(trial("0123456789")) 91 | ``` 92 | ```swift 93 | let lengthTrial = Trial.Length(.Maximum, 10) 94 | let trial = lengthTrial.trial() 95 | XCTAssertTrue(trial("0123456789")) 96 | ``` 97 | ```swift 98 | let interval = Trial.Length(.In, 2..<5 as HalfOpenInterval) 99 | let trial = interval.trial() 100 | XCTAssertTrue(trial("1234")) 101 | ``` 102 | ```swift 103 | let interval = Trial.Length(.In, 2...5 as ClosedInterval) 104 | let trial = interval.trial() 105 | XCTAssertFalse(trial("123456")) 106 | ``` 107 | 108 | ### Suspect 109 | 110 | The ```Suspect``` is a ```Struct``` that is the glue between some other concepts always used while validating fields. It puts together a ```UITextField``` that is going to be the source of the ```evidence```, a ```sentence``` that is going to be the text shown if the ```suspect``` is guilty (when the ```Trial``` returns false) and the ```Trial``` itself, that can be a custom made trial for the suspect or you can use one of the trials provided by the library: 111 | 112 | ```swift 113 | Suspect(view: UITextField, sentence: String, trial: TrialProtocol) 114 | Suspect(view: UITextField, sentence: String, trial: (String) -> Bool) 115 | ``` 116 | 117 | We can check if the ```Suspect``` is guilty or not with: 118 | 119 | ``` 120 | func isGuilty() -> Bool 121 | ``` 122 | 123 | This method is going to return ```true``` if the ```Trial``` returns ```false```. 124 | 125 | Also we can directly ask for the ```verdict``` on the ```Suspect```, this is going to check if it's guilty or not and then return and empty string (```""```) or the ```sentence```. 126 | 127 | For example: 128 | 129 | ```swift 130 | let suspect = Suspect(view: self.dummyTextField, sentence: "Invalid email", trial: .Email) 131 | let verdict = suspect.verdict() // this can be "" or "Invalid Email" 132 | ``` 133 | 134 | ### SwiftCop 135 | 136 | Finally we have the guy that is going to enforce the validations! The cop is going to get all the suspects together and give us the tools to check who are the guilty suspects or if there is any guilty suspect at all. 137 | 138 | As you can imagine this is going to add a suspect under the vigilance of a cop, we can add as many suspects as we want: 139 | 140 | ```swift 141 | open func addSuspect(_ suspect: Suspect) 142 | ``` 143 | 144 | This will let us check if there is any guilty suspect between all the suspects under the surveillance of our cop: 145 | 146 | ```swift 147 | public func anyGuilty() -> Bool 148 | ``` 149 | 150 | This will let us know all the guilty suspects our cop found: 151 | 152 | ```swift 153 | public func allGuilties() -> Array 154 | ``` 155 | 156 | This will let us check if a UITextField that is suspect is guilty or not: 157 | 158 | ```swift 159 | public func isGuilty(textField: UITextField) -> Suspect? 160 | ``` 161 | 162 | ## Example 163 | 164 | The example is shipped in the repository: 165 | 166 | ```Swift 167 | class ViewController: UIViewController { 168 | @IBOutlet weak var validationLabel: UILabel! 169 | 170 | @IBOutlet weak var fullNameMessage: UILabel! 171 | @IBOutlet weak var emailMessage: UILabel! 172 | @IBOutlet weak var passwordMessage: UILabel! 173 | 174 | @IBOutlet weak var fullName: UITextField! 175 | @IBOutlet weak var emailTextField: UITextField! 176 | @IBOutlet weak var password: UITextField! 177 | 178 | let swiftCop = SwiftCop() 179 | 180 | override func viewDidLoad() { 181 | super.viewDidLoad() 182 | 183 | swiftCop.addSuspect(Suspect(view: self.fullName, sentence: "More Than Two Words Needed"){ 184 | return $0.components(separatedBy: " ").filter{$0 != ""}.count >= 2 185 | }) 186 | swiftCop.addSuspect(Suspect(view:self.emailTextField, sentence: "Invalid email", trial: Trial.email)) 187 | swiftCop.addSuspect(Suspect(view:self.password, sentence: "Minimum 4 Characters", trial: Trial.length(.minimum, 4))) 188 | } 189 | 190 | @IBAction func validateFullName(_ sender: UITextField) { 191 | self.fullNameMessage.text = swiftCop.isGuilty(sender)?.verdict() 192 | } 193 | 194 | @IBAction func validateEmail(_ sender: UITextField) { 195 | self.emailMessage.text = swiftCop.isGuilty(sender)?.verdict() 196 | } 197 | 198 | @IBAction func validatePassword(_ sender: UITextField) { 199 | self.passwordMessage.text = swiftCop.isGuilty(sender)?.verdict() 200 | } 201 | 202 | @IBAction func allValid(_ sender: UITextField) { 203 | let nonGultiesMessage = "Everything fine!" 204 | let allGuiltiesMessage = swiftCop.allGuilties().map{ return $0.sentence}.joined(separator: "\n") 205 | 206 | self.validationLabel.text = allGuiltiesMessage.characters.count > 0 ? allGuiltiesMessage : nonGultiesMessage 207 | } 208 | 209 | @IBAction func hideKeyboard(_ sender: AnyObject) { 210 | self.view.endEditing(true) 211 | } 212 | } 213 | ``` 214 | 215 | ![SwiftCopExampel](swiftCopExample.gif) 216 | 217 | # Installation 218 | 219 | You can just clone the repo and copy the ```SwiftCop``` folder to your project or you can use one of the following options: 220 | 221 | ### Setting up with [CocoaPods](http://cocoapods.org/) 222 | 223 | ``` 224 | pod 'SwiftCop' 225 | ``` 226 | 227 | Then: 228 | 229 | ``` 230 | import SwiftCop 231 | ``` 232 | 233 | And you are all set! 234 | 235 | ### Setting up with [Carthage](https://github.com/Carthage/Carthage) 236 | 237 | - TODO 238 | -------------------------------------------------------------------------------- /SwiftCop.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwiftCop" 3 | s.version = "1.1.0" 4 | s.summary = "SwiftCop is a validation library fully written in Swift and inspired by the verbosity and clarity of Ruby On Rails Active Record validations" 5 | s.description = <<-DESC 6 | Build a standard drop-in library for validations in Swift while making it easily extensible for users to create custom validations. And avoid developers from writting over and over again the same code and validations for different projects. 7 | DESC 8 | 9 | s.homepage = "https://github.com/andresinaka/SwiftCop/tree/master" 10 | s.screenshots = "https://github.com/andresinaka/SwiftCop/raw/master/swiftCop.png", "https://github.com/andresinaka/SwiftCop/raw/master/swiftCopExample.gif" 11 | 12 | s.license = 'MIT' 13 | s.author = "Andres Canal" 14 | s.social_media_url = "http://twitter.com/andangc" 15 | s.platform = :ios, "8.0" 16 | s.source = { :git => "https://github.com/andresinaka/SwiftCop.git", :tag => s.version } 17 | s.source_files = "SwiftCop/**/*.swift" 18 | end 19 | -------------------------------------------------------------------------------- /SwiftCop.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E0030DFF1BE99FDF00482649 /* Suspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0030DFC1BE99FDF00482649 /* Suspect.swift */; }; 11 | E0030E001BE99FDF00482649 /* SwiftCop.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0030DFD1BE99FDF00482649 /* SwiftCop.swift */; }; 12 | E0030E011BE99FDF00482649 /* Trial.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0030DFE1BE99FDF00482649 /* Trial.swift */; }; 13 | E04EBB491BD1C67000517851 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E04EBB481BD1C67000517851 /* AppDelegate.swift */; }; 14 | E04EBB4B1BD1C67000517851 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E04EBB4A1BD1C67000517851 /* ViewController.swift */; }; 15 | E04EBB4E1BD1C67000517851 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E04EBB4C1BD1C67000517851 /* Main.storyboard */; }; 16 | E04EBB501BD1C67000517851 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E04EBB4F1BD1C67000517851 /* Assets.xcassets */; }; 17 | E04EBB531BD1C67000517851 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E04EBB511BD1C67000517851 /* LaunchScreen.storyboard */; }; 18 | E04EBB5E1BD1C67000517851 /* SwiftCopTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E04EBB5D1BD1C67000517851 /* SwiftCopTests.swift */; }; 19 | E04EBB691BD1C67100517851 /* SwiftCopUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E04EBB681BD1C67100517851 /* SwiftCopUITests.swift */; }; 20 | E05551551BD8526C0012CD93 /* TrialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E05551541BD8526C0012CD93 /* TrialTests.swift */; }; 21 | E07E95C81BE03E6A000CA328 /* SuspectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07E95C71BE03E6A000CA328 /* SuspectTest.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | E04EBB5A1BD1C67000517851 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = E04EBB3D1BD1C67000517851 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = E04EBB441BD1C67000517851; 30 | remoteInfo = SwiftCop; 31 | }; 32 | E04EBB651BD1C67100517851 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = E04EBB3D1BD1C67000517851 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = E04EBB441BD1C67000517851; 37 | remoteInfo = SwiftCop; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | E0030DFC1BE99FDF00482649 /* Suspect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Suspect.swift; path = SwiftCop/Suspect.swift; sourceTree = ""; }; 43 | E0030DFD1BE99FDF00482649 /* SwiftCop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftCop.swift; path = SwiftCop/SwiftCop.swift; sourceTree = ""; }; 44 | E0030DFE1BE99FDF00482649 /* Trial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Trial.swift; path = SwiftCop/Trial.swift; sourceTree = ""; }; 45 | E04EBB451BD1C67000517851 /* SwiftCop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftCop.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | E04EBB481BD1C67000517851 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | E04EBB4A1BD1C67000517851 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 48 | E04EBB4D1BD1C67000517851 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | E04EBB4F1BD1C67000517851 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | E04EBB521BD1C67000517851 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | E04EBB541BD1C67000517851 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | E04EBB591BD1C67000517851 /* SwiftCopTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCopTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | E04EBB5D1BD1C67000517851 /* SwiftCopTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCopTests.swift; sourceTree = ""; }; 54 | E04EBB5F1BD1C67100517851 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | E04EBB641BD1C67100517851 /* SwiftCopUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCopUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | E04EBB681BD1C67100517851 /* SwiftCopUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCopUITests.swift; sourceTree = ""; }; 57 | E04EBB6A1BD1C67100517851 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | E05551541BD8526C0012CD93 /* TrialTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrialTests.swift; sourceTree = ""; }; 59 | E07E95C71BE03E6A000CA328 /* SuspectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuspectTest.swift; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | E04EBB421BD1C67000517851 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | E04EBB561BD1C67000517851 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | E04EBB611BD1C67100517851 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | E0030DFB1BE99FCB00482649 /* SwiftCop */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | E0030DFC1BE99FDF00482649 /* Suspect.swift */, 91 | E0030DFD1BE99FDF00482649 /* SwiftCop.swift */, 92 | E0030DFE1BE99FDF00482649 /* Trial.swift */, 93 | ); 94 | name = SwiftCop; 95 | sourceTree = ""; 96 | }; 97 | E04EBB3C1BD1C67000517851 = { 98 | isa = PBXGroup; 99 | children = ( 100 | E0030DFB1BE99FCB00482649 /* SwiftCop */, 101 | E04EBB471BD1C67000517851 /* SwiftCopExample */, 102 | E04EBB5C1BD1C67000517851 /* SwiftCopTests */, 103 | E04EBB671BD1C67100517851 /* SwiftCopUITests */, 104 | E04EBB461BD1C67000517851 /* Products */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | E04EBB461BD1C67000517851 /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | E04EBB451BD1C67000517851 /* SwiftCop.app */, 112 | E04EBB591BD1C67000517851 /* SwiftCopTests.xctest */, 113 | E04EBB641BD1C67100517851 /* SwiftCopUITests.xctest */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | E04EBB471BD1C67000517851 /* SwiftCopExample */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | E04EBB481BD1C67000517851 /* AppDelegate.swift */, 122 | E04EBB4A1BD1C67000517851 /* ViewController.swift */, 123 | E04EBB4C1BD1C67000517851 /* Main.storyboard */, 124 | E04EBB4F1BD1C67000517851 /* Assets.xcassets */, 125 | E04EBB511BD1C67000517851 /* LaunchScreen.storyboard */, 126 | E04EBB541BD1C67000517851 /* Info.plist */, 127 | ); 128 | path = SwiftCopExample; 129 | sourceTree = ""; 130 | }; 131 | E04EBB5C1BD1C67000517851 /* SwiftCopTests */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | E05551541BD8526C0012CD93 /* TrialTests.swift */, 135 | E04EBB5D1BD1C67000517851 /* SwiftCopTests.swift */, 136 | E07E95C71BE03E6A000CA328 /* SuspectTest.swift */, 137 | E04EBB5F1BD1C67100517851 /* Info.plist */, 138 | ); 139 | path = SwiftCopTests; 140 | sourceTree = ""; 141 | }; 142 | E04EBB671BD1C67100517851 /* SwiftCopUITests */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | E04EBB681BD1C67100517851 /* SwiftCopUITests.swift */, 146 | E04EBB6A1BD1C67100517851 /* Info.plist */, 147 | ); 148 | path = SwiftCopUITests; 149 | sourceTree = ""; 150 | }; 151 | /* End PBXGroup section */ 152 | 153 | /* Begin PBXNativeTarget section */ 154 | E04EBB441BD1C67000517851 /* SwiftCop */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = E04EBB6D1BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCop" */; 157 | buildPhases = ( 158 | E04EBB411BD1C67000517851 /* Sources */, 159 | E04EBB421BD1C67000517851 /* Frameworks */, 160 | E04EBB431BD1C67000517851 /* Resources */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | ); 166 | name = SwiftCop; 167 | productName = SwiftCop; 168 | productReference = E04EBB451BD1C67000517851 /* SwiftCop.app */; 169 | productType = "com.apple.product-type.application"; 170 | }; 171 | E04EBB581BD1C67000517851 /* SwiftCopTests */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = E04EBB701BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCopTests" */; 174 | buildPhases = ( 175 | E04EBB551BD1C67000517851 /* Sources */, 176 | E04EBB561BD1C67000517851 /* Frameworks */, 177 | E04EBB571BD1C67000517851 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | E04EBB5B1BD1C67000517851 /* PBXTargetDependency */, 183 | ); 184 | name = SwiftCopTests; 185 | productName = SwiftCopTests; 186 | productReference = E04EBB591BD1C67000517851 /* SwiftCopTests.xctest */; 187 | productType = "com.apple.product-type.bundle.unit-test"; 188 | }; 189 | E04EBB631BD1C67100517851 /* SwiftCopUITests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = E04EBB731BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCopUITests" */; 192 | buildPhases = ( 193 | E04EBB601BD1C67100517851 /* Sources */, 194 | E04EBB611BD1C67100517851 /* Frameworks */, 195 | E04EBB621BD1C67100517851 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | E04EBB661BD1C67100517851 /* PBXTargetDependency */, 201 | ); 202 | name = SwiftCopUITests; 203 | productName = SwiftCopUITests; 204 | productReference = E04EBB641BD1C67100517851 /* SwiftCopUITests.xctest */; 205 | productType = "com.apple.product-type.bundle.ui-testing"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | E04EBB3D1BD1C67000517851 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastUpgradeCheck = 0940; 214 | ORGANIZATIONNAME = "Andres Canal"; 215 | TargetAttributes = { 216 | E04EBB441BD1C67000517851 = { 217 | CreatedOnToolsVersion = 7.0.1; 218 | LastSwiftMigration = 0900; 219 | ProvisioningStyle = Manual; 220 | }; 221 | E04EBB581BD1C67000517851 = { 222 | CreatedOnToolsVersion = 7.0.1; 223 | LastSwiftMigration = 0900; 224 | TestTargetID = E04EBB441BD1C67000517851; 225 | }; 226 | E04EBB631BD1C67100517851 = { 227 | CreatedOnToolsVersion = 7.0.1; 228 | DevelopmentTeam = W85VJR9GUS; 229 | LastSwiftMigration = 0900; 230 | TestTargetID = E04EBB441BD1C67000517851; 231 | }; 232 | }; 233 | }; 234 | buildConfigurationList = E04EBB401BD1C67000517851 /* Build configuration list for PBXProject "SwiftCop" */; 235 | compatibilityVersion = "Xcode 3.2"; 236 | developmentRegion = English; 237 | hasScannedForEncodings = 0; 238 | knownRegions = ( 239 | en, 240 | Base, 241 | ); 242 | mainGroup = E04EBB3C1BD1C67000517851; 243 | productRefGroup = E04EBB461BD1C67000517851 /* Products */; 244 | projectDirPath = ""; 245 | projectRoot = ""; 246 | targets = ( 247 | E04EBB441BD1C67000517851 /* SwiftCop */, 248 | E04EBB581BD1C67000517851 /* SwiftCopTests */, 249 | E04EBB631BD1C67100517851 /* SwiftCopUITests */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXResourcesBuildPhase section */ 255 | E04EBB431BD1C67000517851 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | E04EBB531BD1C67000517851 /* LaunchScreen.storyboard in Resources */, 260 | E04EBB501BD1C67000517851 /* Assets.xcassets in Resources */, 261 | E04EBB4E1BD1C67000517851 /* Main.storyboard in Resources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | E04EBB571BD1C67000517851 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | E04EBB621BD1C67100517851 /* Resources */ = { 273 | isa = PBXResourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXResourcesBuildPhase section */ 280 | 281 | /* Begin PBXSourcesBuildPhase section */ 282 | E04EBB411BD1C67000517851 /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | E0030E011BE99FDF00482649 /* Trial.swift in Sources */, 287 | E0030E001BE99FDF00482649 /* SwiftCop.swift in Sources */, 288 | E04EBB4B1BD1C67000517851 /* ViewController.swift in Sources */, 289 | E0030DFF1BE99FDF00482649 /* Suspect.swift in Sources */, 290 | E04EBB491BD1C67000517851 /* AppDelegate.swift in Sources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | E04EBB551BD1C67000517851 /* Sources */ = { 295 | isa = PBXSourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | E04EBB5E1BD1C67000517851 /* SwiftCopTests.swift in Sources */, 299 | E05551551BD8526C0012CD93 /* TrialTests.swift in Sources */, 300 | E07E95C81BE03E6A000CA328 /* SuspectTest.swift in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | E04EBB601BD1C67100517851 /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | E04EBB691BD1C67100517851 /* SwiftCopUITests.swift in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXSourcesBuildPhase section */ 313 | 314 | /* Begin PBXTargetDependency section */ 315 | E04EBB5B1BD1C67000517851 /* PBXTargetDependency */ = { 316 | isa = PBXTargetDependency; 317 | target = E04EBB441BD1C67000517851 /* SwiftCop */; 318 | targetProxy = E04EBB5A1BD1C67000517851 /* PBXContainerItemProxy */; 319 | }; 320 | E04EBB661BD1C67100517851 /* PBXTargetDependency */ = { 321 | isa = PBXTargetDependency; 322 | target = E04EBB441BD1C67000517851 /* SwiftCop */; 323 | targetProxy = E04EBB651BD1C67100517851 /* PBXContainerItemProxy */; 324 | }; 325 | /* End PBXTargetDependency section */ 326 | 327 | /* Begin PBXVariantGroup section */ 328 | E04EBB4C1BD1C67000517851 /* Main.storyboard */ = { 329 | isa = PBXVariantGroup; 330 | children = ( 331 | E04EBB4D1BD1C67000517851 /* Base */, 332 | ); 333 | name = Main.storyboard; 334 | sourceTree = ""; 335 | }; 336 | E04EBB511BD1C67000517851 /* LaunchScreen.storyboard */ = { 337 | isa = PBXVariantGroup; 338 | children = ( 339 | E04EBB521BD1C67000517851 /* Base */, 340 | ); 341 | name = LaunchScreen.storyboard; 342 | sourceTree = ""; 343 | }; 344 | /* End PBXVariantGroup section */ 345 | 346 | /* Begin XCBuildConfiguration section */ 347 | E04EBB6B1BD1C67100517851 /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ALWAYS_SEARCH_USER_PATHS = NO; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 352 | CLANG_CXX_LIBRARY = "libc++"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 356 | CLANG_WARN_BOOL_CONVERSION = YES; 357 | CLANG_WARN_COMMA = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 361 | CLANG_WARN_EMPTY_BODY = YES; 362 | CLANG_WARN_ENUM_CONVERSION = YES; 363 | CLANG_WARN_INFINITE_RECURSION = YES; 364 | CLANG_WARN_INT_CONVERSION = YES; 365 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 366 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 367 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 368 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 369 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 370 | CLANG_WARN_STRICT_PROTOTYPES = YES; 371 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 372 | CLANG_WARN_UNREACHABLE_CODE = YES; 373 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 374 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 375 | COPY_PHASE_STRIP = NO; 376 | DEBUG_INFORMATION_FORMAT = dwarf; 377 | ENABLE_STRICT_OBJC_MSGSEND = YES; 378 | ENABLE_TESTABILITY = YES; 379 | GCC_C_LANGUAGE_STANDARD = gnu99; 380 | GCC_DYNAMIC_NO_PIC = NO; 381 | GCC_NO_COMMON_BLOCKS = YES; 382 | GCC_OPTIMIZATION_LEVEL = 0; 383 | GCC_PREPROCESSOR_DEFINITIONS = ( 384 | "DEBUG=1", 385 | "$(inherited)", 386 | ); 387 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 388 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 389 | GCC_WARN_UNDECLARED_SELECTOR = YES; 390 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 391 | GCC_WARN_UNUSED_FUNCTION = YES; 392 | GCC_WARN_UNUSED_VARIABLE = YES; 393 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 394 | MTL_ENABLE_DEBUG_INFO = YES; 395 | ONLY_ACTIVE_ARCH = YES; 396 | SDKROOT = iphoneos; 397 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 398 | }; 399 | name = Debug; 400 | }; 401 | E04EBB6C1BD1C67100517851 /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ALWAYS_SEARCH_USER_PATHS = NO; 405 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 406 | CLANG_CXX_LIBRARY = "libc++"; 407 | CLANG_ENABLE_MODULES = YES; 408 | CLANG_ENABLE_OBJC_ARC = YES; 409 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 410 | CLANG_WARN_BOOL_CONVERSION = YES; 411 | CLANG_WARN_COMMA = YES; 412 | CLANG_WARN_CONSTANT_CONVERSION = YES; 413 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INFINITE_RECURSION = YES; 418 | CLANG_WARN_INT_CONVERSION = YES; 419 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 421 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 431 | ENABLE_NS_ASSERTIONS = NO; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 436 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 437 | GCC_WARN_UNDECLARED_SELECTOR = YES; 438 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 439 | GCC_WARN_UNUSED_FUNCTION = YES; 440 | GCC_WARN_UNUSED_VARIABLE = YES; 441 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 442 | MTL_ENABLE_DEBUG_INFO = NO; 443 | SDKROOT = iphoneos; 444 | SWIFT_COMPILATION_MODE = wholemodule; 445 | VALIDATE_PRODUCT = YES; 446 | }; 447 | name = Release; 448 | }; 449 | E04EBB6E1BD1C67100517851 /* Debug */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 453 | CODE_SIGN_STYLE = Manual; 454 | DEVELOPMENT_TEAM = ""; 455 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 456 | INFOPLIST_FILE = SwiftCopExample/Info.plist; 457 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 458 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCop; 459 | PRODUCT_NAME = "$(TARGET_NAME)"; 460 | PROVISIONING_PROFILE_SPECIFIER = ""; 461 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 462 | SWIFT_VERSION = 4.0; 463 | }; 464 | name = Debug; 465 | }; 466 | E04EBB6F1BD1C67100517851 /* Release */ = { 467 | isa = XCBuildConfiguration; 468 | buildSettings = { 469 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 470 | CODE_SIGN_STYLE = Manual; 471 | DEVELOPMENT_TEAM = ""; 472 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 473 | INFOPLIST_FILE = SwiftCopExample/Info.plist; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 475 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCop; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | PROVISIONING_PROFILE_SPECIFIER = ""; 478 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 479 | SWIFT_VERSION = 4.0; 480 | }; 481 | name = Release; 482 | }; 483 | E04EBB711BD1C67100517851 /* Debug */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | BUNDLE_LOADER = "$(TEST_HOST)"; 487 | INFOPLIST_FILE = SwiftCopTests/Info.plist; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 489 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCopTests; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 492 | SWIFT_VERSION = 4.0; 493 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCop.app/SwiftCop"; 494 | }; 495 | name = Debug; 496 | }; 497 | E04EBB721BD1C67100517851 /* Release */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | BUNDLE_LOADER = "$(TEST_HOST)"; 501 | INFOPLIST_FILE = SwiftCopTests/Info.plist; 502 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 503 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCopTests; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 506 | SWIFT_VERSION = 4.0; 507 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCop.app/SwiftCop"; 508 | }; 509 | name = Release; 510 | }; 511 | E04EBB741BD1C67100517851 /* Debug */ = { 512 | isa = XCBuildConfiguration; 513 | buildSettings = { 514 | DEVELOPMENT_TEAM = W85VJR9GUS; 515 | INFOPLIST_FILE = SwiftCopUITests/Info.plist; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 517 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCopUITests; 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 520 | SWIFT_VERSION = 4.0; 521 | TEST_TARGET_NAME = SwiftCop; 522 | USES_XCTRUNNER = YES; 523 | }; 524 | name = Debug; 525 | }; 526 | E04EBB751BD1C67100517851 /* Release */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | DEVELOPMENT_TEAM = W85VJR9GUS; 530 | INFOPLIST_FILE = SwiftCopUITests/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 532 | PRODUCT_BUNDLE_IDENTIFIER = ar.com.acanal.SwiftCopUITests; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 535 | SWIFT_VERSION = 4.0; 536 | TEST_TARGET_NAME = SwiftCop; 537 | USES_XCTRUNNER = YES; 538 | }; 539 | name = Release; 540 | }; 541 | /* End XCBuildConfiguration section */ 542 | 543 | /* Begin XCConfigurationList section */ 544 | E04EBB401BD1C67000517851 /* Build configuration list for PBXProject "SwiftCop" */ = { 545 | isa = XCConfigurationList; 546 | buildConfigurations = ( 547 | E04EBB6B1BD1C67100517851 /* Debug */, 548 | E04EBB6C1BD1C67100517851 /* Release */, 549 | ); 550 | defaultConfigurationIsVisible = 0; 551 | defaultConfigurationName = Release; 552 | }; 553 | E04EBB6D1BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCop" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | E04EBB6E1BD1C67100517851 /* Debug */, 557 | E04EBB6F1BD1C67100517851 /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | E04EBB701BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCopTests" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | E04EBB711BD1C67100517851 /* Debug */, 566 | E04EBB721BD1C67100517851 /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | E04EBB731BD1C67100517851 /* Build configuration list for PBXNativeTarget "SwiftCopUITests" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | E04EBB741BD1C67100517851 /* Debug */, 575 | E04EBB751BD1C67100517851 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | /* End XCConfigurationList section */ 581 | }; 582 | rootObject = E04EBB3D1BD1C67000517851 /* Project object */; 583 | } 584 | -------------------------------------------------------------------------------- /SwiftCop.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftCop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftCop.xcodeproj/xcshareddata/xcschemes/SwiftCop.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /SwiftCop/Suspect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Suspect.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/27/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct Suspect { 12 | fileprivate(set) public var view: UITextField 13 | fileprivate var trial: (_ evidence: String) -> Bool 14 | fileprivate(set) public var sentence: String 15 | 16 | public init(view: UITextField, sentence: String, trial: @escaping (_ evidence: String) -> Bool) { 17 | self.view = view 18 | self.trial = trial 19 | self.sentence = sentence 20 | } 21 | 22 | public init(view: UITextField, sentence: String, trial: TrialProtocol) { 23 | self.view = view 24 | self.trial = trial.trial() 25 | self.sentence = sentence 26 | } 27 | 28 | public func isGuilty() -> Bool { 29 | return !self.trial(self.view.text!) 30 | } 31 | 32 | public func verdict() -> String { 33 | return self.isGuilty() ? self.sentence : "" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SwiftCop/SwiftCop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCop.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/16/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | open class SwiftCop { 13 | var suspects = Array() 14 | 15 | public init(){} 16 | 17 | open func addSuspect(_ suspect: Suspect) { 18 | suspects.append(suspect) 19 | } 20 | 21 | open func anyGuilty() -> Bool { 22 | return suspects.filter{ 23 | return $0.isGuilty() 24 | }.count != 0 25 | } 26 | 27 | open func allGuilties() -> Array { 28 | return suspects.filter{ 29 | return $0.isGuilty() 30 | } 31 | } 32 | 33 | open func isGuilty(_ textField: UITextField) -> Suspect? { 34 | for suspect in suspects where suspect.view == textField { 35 | if suspect.isGuilty() { 36 | return suspect 37 | } 38 | } 39 | return nil 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SwiftCop/Trial.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Trial.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/27/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol TrialProtocol { 12 | func trial() -> ((_ evidence: String) -> Bool) 13 | } 14 | 15 | public enum Lenght { 16 | case `is` 17 | case maximum 18 | case minimum 19 | case `in` 20 | } 21 | 22 | public enum Trial: TrialProtocol { 23 | case exclusion([String]) 24 | case format(String) 25 | case inclusion([String]) 26 | case email 27 | case length(Lenght,Any) 28 | case beTrue 29 | case beFalse 30 | 31 | public func trial() -> ((_ evidence: String) -> Bool){ 32 | switch self { 33 | case let .exclusion(exclusionElements): 34 | return { (evidence: String) -> Bool in 35 | 36 | for exclusionElement in exclusionElements { 37 | if ((evidence.range(of: exclusionElement)) != nil) { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | 44 | case let .format(regex): 45 | return { (evidence: String) -> Bool in 46 | let regexTest = NSPredicate(format:"SELF MATCHES %@", regex) 47 | return regexTest.evaluate(with: evidence) 48 | } 49 | 50 | case let .inclusion(inclusionElements): 51 | return { (evidence: String) -> Bool in 52 | 53 | for inclusionElement in inclusionElements { 54 | if ((evidence.range(of: inclusionElement)) != nil) { 55 | return true 56 | } 57 | } 58 | return false 59 | } 60 | 61 | case .email: 62 | return { (evidence: String) -> Bool in 63 | let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" 64 | let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) 65 | return emailTest.evaluate(with: evidence) 66 | } 67 | 68 | case .length(Lenght.is, let exact as Int): 69 | return { (evidence: String) -> Bool in 70 | return evidence.count == exact 71 | } 72 | 73 | case .length(Lenght.minimum, let minimum as Int): 74 | return { (evidence: String) -> Bool in 75 | return evidence.count >= minimum 76 | } 77 | 78 | case .length(Lenght.maximum , let maximum as Int): 79 | return { (evidence: String) -> Bool in 80 | return evidence.count <= maximum 81 | } 82 | 83 | case .length(Lenght.in , let interval as Range): 84 | return { (evidence: String) -> Bool in 85 | return interval.contains(evidence.count) 86 | } 87 | 88 | case .length(Lenght.in , let interval as ClosedRange): 89 | return { (evidence: String) -> Bool in 90 | return interval.contains(evidence.count) 91 | } 92 | 93 | case .beTrue: 94 | return { (evidence: String) -> Bool in 95 | return true 96 | } 97 | 98 | case .beFalse: 99 | return { (evidence: String) -> Bool in 100 | return false 101 | } 102 | 103 | default: 104 | return { (evidence: String) -> Bool in 105 | return false 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /SwiftCopExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/16/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ios-marketing", 47 | "size" : "1024x1024", 48 | "scale" : "1x" 49 | } 50 | ], 51 | "info" : { 52 | "version" : 1, 53 | "author" : "xcode" 54 | } 55 | } -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/AppIcon.appiconset/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresinaka/SwiftCop/9f15702e4eb77b2fa07705f81d44d29a10e2e014/SwiftCopExample/Assets.xcassets/AppIcon.appiconset/icon@2x.png -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/AppIcon.appiconset/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresinaka/SwiftCop/9f15702e4eb77b2fa07705f81d44d29a10e2e014/SwiftCopExample/Assets.xcassets/AppIcon.appiconset/icon@3x.png -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/policeman.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "policeman.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SwiftCopExample/Assets.xcassets/policeman.imageset/policeman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresinaka/SwiftCop/9f15702e4eb77b2fa07705f81d44d29a10e2e014/SwiftCopExample/Assets.xcassets/policeman.imageset/policeman.png -------------------------------------------------------------------------------- /SwiftCopExample/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 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /SwiftCopExample/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 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 70 | 78 | 86 | 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 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /SwiftCopExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SwiftCopExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/16/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | @IBOutlet weak var validationLabel: UILabel! 13 | 14 | @IBOutlet weak var fullNameMessage: UILabel! 15 | @IBOutlet weak var emailMessage: UILabel! 16 | @IBOutlet weak var passwordMessage: UILabel! 17 | 18 | @IBOutlet weak var fullName: UITextField! 19 | @IBOutlet weak var emailTextField: UITextField! 20 | @IBOutlet weak var password: UITextField! 21 | 22 | let swiftCop = SwiftCop() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | swiftCop.addSuspect(Suspect(view: self.fullName, sentence: "More Than Two Words Needed"){ 28 | return $0.components(separatedBy: " ").filter{$0 != ""}.count >= 2 29 | }) 30 | swiftCop.addSuspect(Suspect(view:self.emailTextField, sentence: "Invalid email", trial: Trial.email)) 31 | swiftCop.addSuspect(Suspect(view:self.password, sentence: "Minimum 4 Characters", trial: Trial.length(.minimum, 4))) 32 | } 33 | 34 | @IBAction func validateFullName(_ sender: UITextField) { 35 | self.fullNameMessage.text = swiftCop.isGuilty(sender)?.verdict() 36 | } 37 | 38 | @IBAction func validateEmail(_ sender: UITextField) { 39 | self.emailMessage.text = swiftCop.isGuilty(sender)?.verdict() 40 | } 41 | 42 | @IBAction func validatePassword(_ sender: UITextField) { 43 | self.passwordMessage.text = swiftCop.isGuilty(sender)?.verdict() 44 | } 45 | 46 | @IBAction func allValid(_ sender: UITextField) { 47 | let nonGultiesMessage = "Everything fine!" 48 | let allGuiltiesMessage = swiftCop.allGuilties().map{ return $0.sentence}.joined(separator: "\n") 49 | 50 | self.validationLabel.text = allGuiltiesMessage.count > 0 ? allGuiltiesMessage : nonGultiesMessage 51 | } 52 | 53 | @IBAction func hideKeyboard(_ sender: AnyObject) { 54 | self.view.endEditing(true) 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /SwiftCopTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftCopTests/SuspectTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SuspectTest.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/27/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftCop 11 | 12 | class SuspectTest: XCTestCase { 13 | 14 | var dummyTextField: UITextField! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | self.dummyTextField = UITextField() 20 | self.dummyTextField.text = "Sample Text" 21 | } 22 | 23 | func testCreateSuspectWithBlockTrue() { 24 | 25 | let trial: (_ evidence: String) -> Bool = { 26 | (evidence: String) -> Bool in 27 | return true 28 | } 29 | 30 | let suspect = Suspect(view: self.dummyTextField, sentence: "True Trial", trial: trial) 31 | 32 | XCTAssertEqual(suspect.verdict(), "") 33 | XCTAssertEqual(suspect.sentence, "True Trial") 34 | XCTAssertEqual(suspect.view, self.dummyTextField) 35 | XCTAssertFalse(suspect.isGuilty()) 36 | } 37 | 38 | func testCreateSuspectWithBlockFalse() { 39 | 40 | let trial: (_ evidence: String) -> Bool = { 41 | (evidence: String) -> Bool in 42 | return false 43 | } 44 | 45 | let suspect = Suspect(view: self.dummyTextField, sentence: "False Trial", trial: trial) 46 | 47 | XCTAssertEqual(suspect.verdict(), "False Trial") 48 | XCTAssertEqual(suspect.view, self.dummyTextField) 49 | XCTAssertTrue(suspect.isGuilty()) 50 | } 51 | 52 | func testVeredictReturn() { 53 | let notGuiltySuspect = Suspect(view: self.dummyTextField, sentence: "True Trial", trial: Trial.beTrue) 54 | let guiltySuspect = Suspect(view: self.dummyTextField, sentence: "False Trial", trial: Trial.beFalse) 55 | 56 | XCTAssertEqual(notGuiltySuspect.verdict(), "") 57 | XCTAssertEqual(guiltySuspect.verdict(), "False Trial") 58 | } 59 | 60 | func testCreateSuspectWithTrialTrue() { 61 | 62 | let suspect = Suspect(view: self.dummyTextField, sentence: "True Trial", trial: Trial.beTrue) 63 | 64 | 65 | XCTAssertEqual(suspect.verdict(), "") 66 | XCTAssertEqual(suspect.sentence, "True Trial") 67 | XCTAssertEqual(suspect.view, self.dummyTextField) 68 | XCTAssertFalse(suspect.isGuilty()) 69 | } 70 | 71 | func testCreateSuspectWithTrialFalse() { 72 | 73 | let suspect = Suspect(view: self.dummyTextField, sentence: "False Trial", trial: Trial.beFalse) 74 | 75 | XCTAssertEqual(suspect.verdict(), "False Trial") 76 | XCTAssertEqual(suspect.sentence, "False Trial") 77 | XCTAssertEqual(suspect.view, self.dummyTextField) 78 | XCTAssertTrue(suspect.isGuilty()) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /SwiftCopTests/SwiftCopTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCopTests.swift 3 | // SwiftCopTests 4 | // 5 | // Created by Andres on 10/16/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftCop 11 | 12 | class SwiftCopTests: XCTestCase { 13 | var nameTextField: UITextField! 14 | var emailTextField: UITextField! 15 | 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | self.nameTextField = UITextField() 21 | self.nameTextField.text = "Not Used" 22 | 23 | self.emailTextField = UITextField() 24 | self.emailTextField.text = "Not Used" 25 | } 26 | 27 | override func tearDown() { 28 | super.tearDown() 29 | } 30 | 31 | func testAddSuspect() { 32 | let swiftCop = SwiftCop() 33 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "True Trial", trial: Trial.beTrue)) 34 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "False Trial", trial: Trial.beFalse)) 35 | XCTAssertTrue(swiftCop.suspects.count == 2) 36 | } 37 | 38 | func testAnyGuiltyFalse() { 39 | let swiftCop = SwiftCop() 40 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "True Trial", trial: Trial.beTrue)) 41 | XCTAssertFalse(swiftCop.anyGuilty()) 42 | } 43 | 44 | func testAnyGuiltyTrue() { 45 | let swiftCop = SwiftCop() 46 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "False Trial", trial: Trial.beFalse)) 47 | XCTAssertTrue(swiftCop.anyGuilty()) 48 | } 49 | 50 | func testIsGuiltyTrue() { 51 | let swiftCop = SwiftCop() 52 | 53 | let textFieldNotGuilty = UITextField() 54 | textFieldNotGuilty.text = "Not guilty" 55 | 56 | let textFieldGuilty = UITextField() 57 | textFieldGuilty.text = "Guilty" 58 | 59 | swiftCop.addSuspect(Suspect(view: textFieldNotGuilty, sentence: "True Trial" , trial: Trial.beTrue)) 60 | swiftCop.addSuspect(Suspect(view: textFieldGuilty, sentence: "True Trial" , trial: Trial.beTrue)) 61 | swiftCop.addSuspect(Suspect(view: textFieldGuilty, sentence: "False Trial" , trial: Trial.beFalse)) 62 | 63 | let guilties = swiftCop.allGuilties() 64 | XCTAssertTrue(guilties.count == 1) 65 | 66 | XCTAssertNil(swiftCop.isGuilty(textFieldNotGuilty)) 67 | XCTAssertNotNil(swiftCop.isGuilty(textFieldGuilty)) 68 | 69 | let expectation = self.expectation(description: "isGuilty returns true") 70 | 71 | if let suspect = swiftCop.isGuilty(textFieldGuilty) { 72 | XCTAssertEqual(suspect.view, textFieldGuilty) 73 | XCTAssertEqual(suspect.verdict(), "False Trial") 74 | expectation.fulfill() 75 | } 76 | 77 | waitForExpectations(timeout: 1) { error in 78 | 79 | } 80 | } 81 | 82 | func testCustomTrialNoTGuilty() { 83 | let swiftCop = SwiftCop() 84 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty") { 85 | (evidence: String) -> Bool in 86 | return true 87 | }) 88 | 89 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty") { 90 | (evidence: String) -> Bool in 91 | return true 92 | }) 93 | 94 | XCTAssertFalse(swiftCop.anyGuilty()) 95 | } 96 | 97 | func testCustomTrialGuilty() { 98 | let swiftCop = SwiftCop() 99 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Guilty") { 100 | (evidence: String) -> Bool in 101 | return false 102 | }) 103 | 104 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty") { 105 | (evidence: String) -> Bool in 106 | return true 107 | }) 108 | 109 | XCTAssertTrue(swiftCop.anyGuilty()) 110 | } 111 | 112 | func testCustomTrialAllGuiltiesFalse() { 113 | let swiftCop = SwiftCop() 114 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty", trial: Trial.beTrue)) 115 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty", trial: Trial.beTrue)) 116 | 117 | let guilties = swiftCop.allGuilties() 118 | XCTAssertTrue(guilties.count == 0) 119 | } 120 | 121 | func testCustomTrialAllGuiltiesTrue() { 122 | let swiftCop = SwiftCop() 123 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Guilty", trial: Trial.beFalse)) 124 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Not Guilty", trial: Trial.beTrue)) 125 | 126 | let guilties = swiftCop.allGuilties() 127 | XCTAssertTrue(guilties.count == 1) 128 | XCTAssertEqual(guilties.first!.view, self.nameTextField) 129 | XCTAssertEqual(guilties.first!.verdict(), "Guilty") 130 | } 131 | 132 | func testNoTextTextField() { 133 | let swiftCop = SwiftCop() 134 | self.nameTextField.text = nil 135 | swiftCop.addSuspect(Suspect(view: self.nameTextField, sentence: "Guilty" , trial: Trial.beFalse)) 136 | 137 | let guilties = swiftCop.allGuilties() 138 | XCTAssertTrue(guilties.count == 1) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /SwiftCopTests/TrialTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrialSwiftCopTests.swift 3 | // SwiftCop 4 | // 5 | // Created by Andres on 10/21/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftCop 11 | 12 | class TrialTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | 18 | func testExclusion() { 19 | let exclusionTrial = Trial.exclusion([".com",".ar", ".uy"]) 20 | let trial = exclusionTrial.trial() 21 | 22 | XCTAssertFalse(trial("http://www.nytimes.com")) 23 | XCTAssertFalse(trial("http://www.lanacion.com.ar")) 24 | XCTAssertTrue(trial("http://www.elpais.es")) 25 | } 26 | 27 | func testFormat() { 28 | let formatTrial = Trial.format("^#([a-f0-9]{6}|[a-f0-9]{3})$") // hexa number with # 29 | let trial = formatTrial.trial() 30 | 31 | XCTAssertTrue(trial("#57b5b5")) 32 | XCTAssertFalse(trial("57b5b5")) 33 | XCTAssertFalse(trial("#h7b5b5")) 34 | } 35 | 36 | 37 | func testInclusion() { 38 | let inclusionTrial = Trial.inclusion([".com",".ar", ".uy"]) 39 | let trial = inclusionTrial.trial() 40 | 41 | XCTAssertTrue(trial("http://www.nytimes.com")) 42 | XCTAssertTrue(trial("http://www.lanacion.com.ar")) 43 | XCTAssertFalse(trial("http://www.elpais.es")) 44 | } 45 | 46 | func testEmail() { 47 | let emailTrial = Trial.email 48 | let trial = emailTrial.trial() 49 | 50 | XCTAssertTrue(trial("test@test.com")) 51 | XCTAssertFalse(trial("test@test")) 52 | XCTAssertFalse(trial("test@")) 53 | XCTAssertFalse(trial("test.com")) 54 | XCTAssertFalse(trial(".com")) 55 | } 56 | 57 | func testLengthIs() { 58 | let lengthTrial = Trial.length(.is, 10) 59 | let trial = lengthTrial.trial() 60 | 61 | XCTAssertTrue(trial("0123456789")) 62 | XCTAssertFalse(trial("56789")) 63 | } 64 | 65 | func testLengthMinimum() { 66 | let lengthTrial = Trial.length(.minimum, 10) 67 | let trial = lengthTrial.trial() 68 | 69 | XCTAssertTrue(trial("0123456789")) 70 | XCTAssertFalse(trial("56789")) 71 | } 72 | 73 | func testLengthMaximum() { 74 | let lengthTrial = Trial.length(.maximum, 10) 75 | let trial = lengthTrial.trial() 76 | 77 | XCTAssertTrue(trial("0123456789")) 78 | XCTAssertFalse(trial("01234567890")) 79 | } 80 | 81 | func testLengthIntervalsHalfOpen() { 82 | let interval = Trial.length(.in, 2..<5 as Range) 83 | let trial = interval.trial() 84 | 85 | XCTAssertTrue(trial("1234")) 86 | XCTAssertFalse(trial("12345")) 87 | XCTAssertFalse(trial("1")) 88 | } 89 | 90 | func testLengthIntervalsClosed() { 91 | let interval = Trial.length(.in, 2...5 as ClosedRange) 92 | let trial = interval.trial() 93 | 94 | XCTAssertFalse(trial("123456")) 95 | XCTAssertTrue(trial("12345")) 96 | XCTAssertTrue(trial("1234")) 97 | XCTAssertTrue(trial("12")) 98 | XCTAssertFalse(trial("1")) 99 | } 100 | 101 | func testInvalid() { 102 | let interval = Trial.length(.in, 5) 103 | let trial = interval.trial() 104 | 105 | XCTAssertFalse(trial("123456")) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /SwiftCopUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftCopUITests/SwiftCopUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCopUITests.swift 3 | // SwiftCopUITests 4 | // 5 | // Created by Andres on 10/16/15. 6 | // Copyright © 2015 Andres Canal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftCopUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | continueAfterFailure = false 16 | XCUIApplication().launch() 17 | } 18 | 19 | override func tearDown() { 20 | super.tearDown() 21 | } 22 | 23 | func testFullName() { 24 | let fullNameTextField = XCUIApplication().textFields["Full Name"] 25 | fullNameTextField.tap() 26 | fullNameTextField.typeText("first") 27 | XCTAssert(XCUIApplication().staticTexts["More Than Two Words Needed"].exists) 28 | fullNameTextField.typeText(" last") 29 | XCTAssertFalse(XCUIApplication().staticTexts["More Than Two Words Needed"].exists) 30 | } 31 | 32 | func testEmail() { 33 | let emailTextField = XCUIApplication().textFields["Email"] 34 | emailTextField.tap() 35 | emailTextField.typeText("email") 36 | XCTAssert(XCUIApplication().staticTexts["Invalid email"].exists) 37 | emailTextField.typeText("@email.com") 38 | XCTAssertFalse(XCUIApplication().staticTexts["Invalid Email"].exists) 39 | } 40 | 41 | func testPassword() { 42 | let passwordTextField = XCUIApplication().textFields["Password"] 43 | passwordTextField.tap() 44 | passwordTextField.typeText("1") 45 | XCTAssert(XCUIApplication().staticTexts["Minimum 4 Characters"].exists) 46 | passwordTextField.typeText("234") 47 | XCTAssertFalse(XCUIApplication().staticTexts["Minimum 4 Characters"].exists) 48 | } 49 | 50 | func testAllValidationsPass(){ 51 | 52 | let fullNameTextField = XCUIApplication().textFields["Full Name"] 53 | fullNameTextField.tap() 54 | fullNameTextField.typeText("fist name") 55 | 56 | let emailTextField = XCUIApplication().textFields["Email"] 57 | emailTextField.tap() 58 | emailTextField.typeText("email@email.com") 59 | 60 | let passwordTextField = XCUIApplication().textFields["Password"] 61 | passwordTextField.tap() 62 | passwordTextField.typeText("password") 63 | 64 | let policemanElement = XCUIApplication().otherElements.containing(.image, identifier:"policeman").element 65 | policemanElement.tap() 66 | 67 | let checkValidationsButton = XCUIApplication().buttons["Check Validations"] 68 | checkValidationsButton.tap() 69 | 70 | XCTAssert(XCUIApplication().staticTexts["Everything fine!"].exists) 71 | } 72 | 73 | func testAllValidationsFail(){ 74 | let checkValidationsButton = XCUIApplication().buttons["Check Validations"] 75 | checkValidationsButton.tap() 76 | 77 | XCTAssertFalse(XCUIApplication().staticTexts["Everything fine!"].exists) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /swiftCop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresinaka/SwiftCop/9f15702e4eb77b2fa07705f81d44d29a10e2e014/swiftCop.png -------------------------------------------------------------------------------- /swiftCopExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresinaka/SwiftCop/9f15702e4eb77b2fa07705f81d44d29a10e2e014/swiftCopExample.gif --------------------------------------------------------------------------------