├── .gitignore ├── Classes ├── CharacterSetSpecification.swift ├── CountSpecification.swift ├── EmailSpecification.swift ├── Operator.swift ├── PredicateSpecification.swift ├── RegularExpressionSpecification.swift └── Specification.swift ├── Examples └── Basic_iOS │ ├── Basic_iOS.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── Basic_iOS.xccheckout │ └── xcuserdata │ │ └── neoneye.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── Basic_iOS.xcscheme │ │ └── xcschememanagement.plist │ └── Basic_iOS │ ├── AppDelegate.swift │ ├── Base.lproj │ └── Main.storyboard │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── LICENSE ├── README.md ├── SpecificationPattern.podspec ├── Tests ├── AdvancedTests.swift ├── CharacterSetSpecificationTests.swift ├── CountSpecificationTests.swift ├── EmailSpecificationTests.swift ├── Info.plist ├── OperatorTests.swift ├── PredicateSpecificationTests.swift ├── RegularExpressionSpecificationTests.swift ├── SpecificationTesting.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── SpecificationTesting.xccheckout │ └── xcuserdata │ │ └── neoneye.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── Test-iOS.xcscheme │ │ ├── Tests.xcscheme │ │ └── xcschememanagement.plist ├── SpecificationTests.swift ├── Test-iOS │ ├── Info.plist │ └── alien.csv └── TrueFalseTests.swift └── example0.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | # Pods - for those of you who use CocoaPods 20 | #Pods -------------------------------------------------------------------------------- /Classes/CharacterSetSpecification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class CharacterSetSpecification: CompositeSpecification { 4 | public let characterSet: NSCharacterSet 5 | 6 | init(characterSet: NSCharacterSet) { 7 | self.characterSet = characterSet 8 | super.init() 9 | } 10 | 11 | public class func charactersInString(charactersInString: String) -> CharacterSetSpecification { 12 | let cs = NSCharacterSet(charactersInString: charactersInString) 13 | return CharacterSetSpecification(characterSet: cs) 14 | } 15 | 16 | public class func controlCharacterSet() -> CharacterSetSpecification { 17 | let cs = NSCharacterSet.controlCharacterSet() 18 | return CharacterSetSpecification(characterSet: cs) 19 | } 20 | 21 | public class func whitespaceCharacterSet() -> CharacterSetSpecification { 22 | let cs = NSCharacterSet.whitespaceCharacterSet() 23 | return CharacterSetSpecification(characterSet: cs) 24 | } 25 | 26 | public class func whitespaceAndNewlineCharacterSet() -> CharacterSetSpecification { 27 | let cs = NSCharacterSet.whitespaceAndNewlineCharacterSet() 28 | return CharacterSetSpecification(characterSet: cs) 29 | } 30 | 31 | public class func decimalDigitCharacterSet() -> CharacterSetSpecification { 32 | let cs = NSCharacterSet.decimalDigitCharacterSet() 33 | return CharacterSetSpecification(characterSet: cs) 34 | } 35 | 36 | public class func lowercaseLetterCharacterSet() -> CharacterSetSpecification { 37 | let cs = NSCharacterSet.lowercaseLetterCharacterSet() 38 | return CharacterSetSpecification(characterSet: cs) 39 | } 40 | 41 | public class func uppercaseLetterCharacterSet() -> CharacterSetSpecification { 42 | let cs = NSCharacterSet.uppercaseLetterCharacterSet() 43 | return CharacterSetSpecification(characterSet: cs) 44 | } 45 | 46 | public class func nonBaseCharacterSet() -> CharacterSetSpecification { 47 | let cs = NSCharacterSet.nonBaseCharacterSet() 48 | return CharacterSetSpecification(characterSet: cs) 49 | } 50 | 51 | public class func alphanumericCharacterSet() -> CharacterSetSpecification { 52 | let cs = NSCharacterSet.alphanumericCharacterSet() 53 | return CharacterSetSpecification(characterSet: cs) 54 | } 55 | 56 | public class func decomposableCharacterSet() -> CharacterSetSpecification { 57 | let cs = NSCharacterSet.decomposableCharacterSet() 58 | return CharacterSetSpecification(characterSet: cs) 59 | } 60 | 61 | public class func illegalCharacterSet() -> CharacterSetSpecification { 62 | let cs = NSCharacterSet.illegalCharacterSet() 63 | return CharacterSetSpecification(characterSet: cs) 64 | } 65 | 66 | public class func punctuationCharacterSet() -> CharacterSetSpecification { 67 | let cs = NSCharacterSet.punctuationCharacterSet() 68 | return CharacterSetSpecification(characterSet: cs) 69 | } 70 | 71 | public class func capitalizedLetterCharacterSet() -> CharacterSetSpecification { 72 | let cs = NSCharacterSet.capitalizedLetterCharacterSet() 73 | return CharacterSetSpecification(characterSet: cs) 74 | } 75 | 76 | public class func symbolCharacterSet() -> CharacterSetSpecification { 77 | let cs = NSCharacterSet.symbolCharacterSet() 78 | return CharacterSetSpecification(characterSet: cs) 79 | } 80 | 81 | public class func newlineCharacterSet() -> CharacterSetSpecification { 82 | let cs = NSCharacterSet.newlineCharacterSet() 83 | return CharacterSetSpecification(characterSet: cs) 84 | } 85 | 86 | 87 | public override func isSatisfiedBy(candidate: Any?) -> Bool { 88 | guard let fullString = candidate as? String else { return false } 89 | for character: Character in fullString.characters { 90 | let range: Range? = 91 | String(character).rangeOfCharacterFromSet(characterSet) 92 | if range == nil { 93 | return false // one or more characters does not satify the characterSet 94 | } 95 | if range!.isEmpty { 96 | return false // one or more characters does not satify the characterSet 97 | } 98 | } 99 | return true // the whole string satisfies our characterSet 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Classes/CountSpecification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class CountSpecification: CompositeSpecification { 4 | 5 | public class func min(count: Int) -> CountSpecification { 6 | return CountSpecification().min(count) 7 | } 8 | 9 | public class func max(count: Int) -> CountSpecification { 10 | return CountSpecification().max(count) 11 | } 12 | 13 | public class func between(minCount: Int, _ maxCount: Int) -> CountSpecification { 14 | return CountSpecification().min(minCount).max(maxCount) 15 | } 16 | 17 | public class func exactly(count: Int) -> CountSpecification { 18 | return CountSpecification().min(count).max(count) 19 | } 20 | 21 | public var minCount: Int? 22 | public var maxCount: Int? 23 | 24 | public func min(count: Int) -> CountSpecification { 25 | minCount = count 26 | return self 27 | } 28 | 29 | public func max(count: Int) -> CountSpecification { 30 | maxCount = count 31 | return self 32 | } 33 | 34 | public override func isSatisfiedBy(candidate: Any?) -> Bool { 35 | if candidate == nil { 36 | return false 37 | } 38 | 39 | var n: Int = 0 40 | repeat { 41 | if let x = candidate as? String { 42 | n = x.characters.count 43 | break 44 | } 45 | 46 | // Obtain length of Array, see http://stackoverflow.com/a/25901509/78336 47 | if let y = candidate as? NSArray { 48 | n = (y as Array).count 49 | break 50 | } 51 | 52 | // This candidate is not a collection 53 | return false 54 | } while(false) 55 | 56 | 57 | switch (minCount, maxCount) { 58 | case (.Some(let min), .Some(let max)): 59 | return (n >= min) && (n <= max) 60 | case (.Some(let min), _): 61 | return (n >= min) 62 | case (_, .Some(let max)): 63 | return (n <= max) 64 | default: 65 | break 66 | } 67 | 68 | return false 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Classes/EmailSpecification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /** 4 | Partial validation of email 5 | 6 | Email addresses are often requested as input to website as user identification for the purpose of data validation. 7 | An email address is generally recognized as having two parts joined with an at-sign (@). However, the technical 8 | specification detailed in RFC 822 and subsequent RFCs are more extensive, offering complex and strict restrictions. 9 | It is impossible to match these restrictions with a single technique. Using regular expressions results in 10 | long patterns giving incomplete results. 11 | 12 | http://en.wikipedia.org/wiki/Email_address 13 | */ 14 | public class EmailSpecification: CompositeSpecification { 15 | private let specification: RegularExpressionSpecification 16 | 17 | public override init() { 18 | self.specification = RegularExpressionSpecification(pattern: emailRegularExpression) 19 | super.init() 20 | } 21 | 22 | public override func isSatisfiedBy(candidate: Any?) -> Bool { 23 | return specification.isSatisfiedBy(candidate) 24 | } 25 | 26 | // RFC5322 address specification 27 | // http://tools.ietf.org/html/rfc5322#section-3.4 28 | // Taken from http://stackoverflow.com/a/1149894/78336 29 | private let emailRegularExpression = 30 | "(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}" + 31 | "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" + 32 | "x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-" + 33 | "z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5" + 34 | "]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-" + 35 | "9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" + 36 | "-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" 37 | } 38 | -------------------------------------------------------------------------------- /Classes/Operator.swift: -------------------------------------------------------------------------------- 1 | /* 2 | And operator - shorthand for .and() 3 | 4 | USAGE: 5 | let spec = onlyDigits & between2And4Letters & modulus13Checksum 6 | */ 7 | public func & (left: Specification, right: Specification) -> Specification { 8 | return left.and(right) 9 | } 10 | 11 | /* 12 | Or operator - shorthand for .or() 13 | 14 | USAGE: 15 | let spec = connectionTypeWifi | connectionType4G | hasOfflineData 16 | */ 17 | public func | (left: Specification, right: Specification) -> Specification { 18 | return left.or(right) 19 | } 20 | 21 | /* 22 | Negate operator - shorthand for .not() 23 | 24 | USAGE: 25 | let spec = ! filesystemIsFull 26 | */ 27 | public prefix func ! (specification: Specification) -> Specification { 28 | return specification.not() 29 | } 30 | 31 | 32 | /* 33 | Equivalence operators - shorthand for .isSatisfiedBy() 34 | 35 | USAGE: 36 | spec == "123" 37 | spec != "123" 38 | */ 39 | public func == (left: Specification, right: Any?) -> Bool { 40 | return left.isSatisfiedBy(right) 41 | } 42 | public func != (left: Specification, right: Any?) -> Bool { 43 | return !left.isSatisfiedBy(right) 44 | } 45 | -------------------------------------------------------------------------------- /Classes/PredicateSpecification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class PredicateSpecification: CompositeSpecification { 4 | public let predicate: T -> Bool 5 | 6 | init(predicate: T -> Bool) { 7 | self.predicate = predicate 8 | super.init() 9 | } 10 | 11 | public override func isSatisfiedBy(candidate: Any?) -> Bool { 12 | guard let obj = candidate as? T else { return false } 13 | return predicate(obj) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Classes/RegularExpressionSpecification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class RegularExpressionSpecification: CompositeSpecification { 4 | public let regularExpression: NSRegularExpression 5 | 6 | init(regularExpression: NSRegularExpression) { 7 | self.regularExpression = regularExpression 8 | super.init() 9 | } 10 | 11 | convenience init(pattern: String) { 12 | let regularExpression = try! NSRegularExpression(pattern: pattern, options: []) 13 | self.init(regularExpression: regularExpression) 14 | } 15 | 16 | public override func isSatisfiedBy(candidate: Any?) -> Bool { 17 | guard let s = candidate as? String else { return false } 18 | return regularExpression.numberOfMatchesInString(s, options: [], range: NSMakeRange(0, s.characters.count)) > 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Classes/Specification.swift: -------------------------------------------------------------------------------- 1 | public protocol Specification { 2 | func isSatisfiedBy(candidate: Any?) -> Bool 3 | func and(other: Specification) -> Specification 4 | func or(other: Specification) -> Specification 5 | func not() -> Specification 6 | } 7 | 8 | public class CompositeSpecification: Specification { 9 | public func isSatisfiedBy(candidate: Any?) -> Bool { 10 | // subclass must implement this method 11 | return false 12 | } 13 | 14 | public func and(other: Specification) -> Specification { 15 | return AndSpecification(self, other) 16 | } 17 | 18 | public func or(other: Specification) -> Specification { 19 | return OrSpecification(self, other) 20 | } 21 | 22 | public func not() -> Specification { 23 | return NotSpecification(self) 24 | } 25 | } 26 | 27 | public class AndSpecification: CompositeSpecification { 28 | private let one: Specification 29 | private let other: Specification 30 | 31 | public init(_ x: Specification, _ y: Specification) { 32 | self.one = x 33 | self.other = y 34 | super.init() 35 | } 36 | 37 | override public func isSatisfiedBy(candidate: Any?) -> Bool { 38 | return one.isSatisfiedBy(candidate) && other.isSatisfiedBy(candidate) 39 | } 40 | } 41 | 42 | public class OrSpecification: CompositeSpecification { 43 | private let one: Specification 44 | private let other: Specification 45 | 46 | public init(_ x: Specification, _ y: Specification) { 47 | self.one = x 48 | self.other = y 49 | super.init() 50 | } 51 | 52 | override public func isSatisfiedBy(candidate: Any?) -> Bool { 53 | return one.isSatisfiedBy(candidate) || other.isSatisfiedBy(candidate) 54 | } 55 | } 56 | 57 | public class NotSpecification: CompositeSpecification { 58 | private let wrapped: Specification 59 | 60 | public init(_ x: Specification) { 61 | self.wrapped = x 62 | super.init() 63 | } 64 | 65 | override public func isSatisfiedBy(candidate: Any?) -> Bool { 66 | return !wrapped.isSatisfiedBy(candidate) 67 | } 68 | } 69 | 70 | public class FalseSpecification: CompositeSpecification { 71 | override public init() { 72 | super.init() 73 | } 74 | 75 | override public func isSatisfiedBy(candidate: Any?) -> Bool { 76 | return false 77 | } 78 | } 79 | 80 | public class TrueSpecification: CompositeSpecification { 81 | override public init() { 82 | super.init() 83 | } 84 | 85 | override public func isSatisfiedBy(candidate: Any?) -> Bool { 86 | return true 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 424CE09D195B5538001C5321 /* PredicateSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424CE09C195B5538001C5321 /* PredicateSpecification.swift */; }; 11 | 4250D83D1947BA2600537779 /* CharacterSetSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D83C1947BA2600537779 /* CharacterSetSpecification.swift */; }; 12 | 4288532919CA37D500362DE5 /* CountSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4288532819CA37D500362DE5 /* CountSpecification.swift */; }; 13 | 428F9FEF197E808500D327EC /* Operator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 428F9FEE197E808500D327EC /* Operator.swift */; }; 14 | 42B7F34519479AE500CBB434 /* Specification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B7F34319479AE500CBB434 /* Specification.swift */; }; 15 | 42C69FD2194A54200017DFAB /* RegularExpressionSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C69FD1194A54200017DFAB /* RegularExpressionSpecification.swift */; }; 16 | 42E1B7AF19422EFC0022E796 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E1B7AE19422EFC0022E796 /* AppDelegate.swift */; }; 17 | 42E1B7B119422EFC0022E796 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E1B7B019422EFC0022E796 /* ViewController.swift */; }; 18 | 42E1B7B419422EFC0022E796 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42E1B7B219422EFC0022E796 /* Main.storyboard */; }; 19 | 42E1B7B619422EFC0022E796 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42E1B7B519422EFC0022E796 /* Images.xcassets */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 424CE09C195B5538001C5321 /* PredicateSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateSpecification.swift; sourceTree = ""; }; 24 | 4250D83C1947BA2600537779 /* CharacterSetSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterSetSpecification.swift; sourceTree = ""; }; 25 | 4288532819CA37D500362DE5 /* CountSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountSpecification.swift; sourceTree = ""; }; 26 | 428F9FEE197E808500D327EC /* Operator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operator.swift; sourceTree = ""; }; 27 | 42B7F34319479AE500CBB434 /* Specification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Specification.swift; sourceTree = ""; }; 28 | 42C69FD1194A54200017DFAB /* RegularExpressionSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegularExpressionSpecification.swift; sourceTree = ""; }; 29 | 42E1B7A919422EFC0022E796 /* Basic_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Basic_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 42E1B7AD19422EFC0022E796 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 42E1B7AE19422EFC0022E796 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 32 | 42E1B7B019422EFC0022E796 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 33 | 42E1B7B319422EFC0022E796 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 34 | 42E1B7B519422EFC0022E796 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 42E1B7A619422EFC0022E796 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 42B7F34119479AE500CBB434 /* Classes */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 4250D83C1947BA2600537779 /* CharacterSetSpecification.swift */, 52 | 4288532819CA37D500362DE5 /* CountSpecification.swift */, 53 | 428F9FEE197E808500D327EC /* Operator.swift */, 54 | 424CE09C195B5538001C5321 /* PredicateSpecification.swift */, 55 | 42C69FD1194A54200017DFAB /* RegularExpressionSpecification.swift */, 56 | 42B7F34319479AE500CBB434 /* Specification.swift */, 57 | ); 58 | name = Classes; 59 | path = ../../Classes; 60 | sourceTree = ""; 61 | }; 62 | 42E1B7A019422EFC0022E796 = { 63 | isa = PBXGroup; 64 | children = ( 65 | 42B7F34119479AE500CBB434 /* Classes */, 66 | 42E1B7AB19422EFC0022E796 /* Basic_iOS */, 67 | 42E1B7AA19422EFC0022E796 /* Products */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 42E1B7AA19422EFC0022E796 /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 42E1B7A919422EFC0022E796 /* Basic_iOS.app */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 42E1B7AB19422EFC0022E796 /* Basic_iOS */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 42E1B7AE19422EFC0022E796 /* AppDelegate.swift */, 83 | 42E1B7B019422EFC0022E796 /* ViewController.swift */, 84 | 42E1B7B219422EFC0022E796 /* Main.storyboard */, 85 | 42E1B7B519422EFC0022E796 /* Images.xcassets */, 86 | 42E1B7AC19422EFC0022E796 /* Supporting Files */, 87 | ); 88 | path = Basic_iOS; 89 | sourceTree = ""; 90 | }; 91 | 42E1B7AC19422EFC0022E796 /* Supporting Files */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 42E1B7AD19422EFC0022E796 /* Info.plist */, 95 | ); 96 | name = "Supporting Files"; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 42E1B7A819422EFC0022E796 /* Basic_iOS */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 42E1B7C519422EFC0022E796 /* Build configuration list for PBXNativeTarget "Basic_iOS" */; 105 | buildPhases = ( 106 | 42E1B7A519422EFC0022E796 /* Sources */, 107 | 42E1B7A619422EFC0022E796 /* Frameworks */, 108 | 42E1B7A719422EFC0022E796 /* Resources */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = Basic_iOS; 115 | productName = Basic_iOS; 116 | productReference = 42E1B7A919422EFC0022E796 /* Basic_iOS.app */; 117 | productType = "com.apple.product-type.application"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 42E1B7A119422EFC0022E796 /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastSwiftUpdateCheck = 0700; 126 | LastUpgradeCheck = 0700; 127 | ORGANIZATIONNAME = None; 128 | TargetAttributes = { 129 | 42E1B7A819422EFC0022E796 = { 130 | CreatedOnToolsVersion = 6.0; 131 | }; 132 | }; 133 | }; 134 | buildConfigurationList = 42E1B7A419422EFC0022E796 /* Build configuration list for PBXProject "Basic_iOS" */; 135 | compatibilityVersion = "Xcode 3.2"; 136 | developmentRegion = English; 137 | hasScannedForEncodings = 0; 138 | knownRegions = ( 139 | en, 140 | Base, 141 | ); 142 | mainGroup = 42E1B7A019422EFC0022E796; 143 | productRefGroup = 42E1B7AA19422EFC0022E796 /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | 42E1B7A819422EFC0022E796 /* Basic_iOS */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | 42E1B7A719422EFC0022E796 /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 42E1B7B419422EFC0022E796 /* Main.storyboard in Resources */, 158 | 42E1B7B619422EFC0022E796 /* Images.xcassets in Resources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXResourcesBuildPhase section */ 163 | 164 | /* Begin PBXSourcesBuildPhase section */ 165 | 42E1B7A519422EFC0022E796 /* Sources */ = { 166 | isa = PBXSourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 42C69FD2194A54200017DFAB /* RegularExpressionSpecification.swift in Sources */, 170 | 424CE09D195B5538001C5321 /* PredicateSpecification.swift in Sources */, 171 | 4250D83D1947BA2600537779 /* CharacterSetSpecification.swift in Sources */, 172 | 42E1B7B119422EFC0022E796 /* ViewController.swift in Sources */, 173 | 428F9FEF197E808500D327EC /* Operator.swift in Sources */, 174 | 4288532919CA37D500362DE5 /* CountSpecification.swift in Sources */, 175 | 42E1B7AF19422EFC0022E796 /* AppDelegate.swift in Sources */, 176 | 42B7F34519479AE500CBB434 /* Specification.swift in Sources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXSourcesBuildPhase section */ 181 | 182 | /* Begin PBXVariantGroup section */ 183 | 42E1B7B219422EFC0022E796 /* Main.storyboard */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | 42E1B7B319422EFC0022E796 /* Base */, 187 | ); 188 | name = Main.storyboard; 189 | sourceTree = ""; 190 | }; 191 | /* End PBXVariantGroup section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | 42E1B7C319422EFC0022E796 /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 199 | CLANG_CXX_LIBRARY = "libc++"; 200 | CLANG_ENABLE_MODULES = YES; 201 | CLANG_ENABLE_OBJC_ARC = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 205 | CLANG_WARN_EMPTY_BODY = YES; 206 | CLANG_WARN_ENUM_CONVERSION = YES; 207 | CLANG_WARN_INT_CONVERSION = YES; 208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 209 | CLANG_WARN_UNREACHABLE_CODE = YES; 210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 211 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 212 | COPY_PHASE_STRIP = NO; 213 | ENABLE_STRICT_OBJC_MSGSEND = YES; 214 | ENABLE_TESTABILITY = YES; 215 | GCC_C_LANGUAGE_STANDARD = gnu99; 216 | GCC_DYNAMIC_NO_PIC = NO; 217 | GCC_OPTIMIZATION_LEVEL = 0; 218 | GCC_PREPROCESSOR_DEFINITIONS = ( 219 | "DEBUG=1", 220 | "$(inherited)", 221 | ); 222 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 230 | METAL_ENABLE_DEBUG_INFO = YES; 231 | ONLY_ACTIVE_ARCH = YES; 232 | SDKROOT = iphoneos; 233 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 234 | TARGETED_DEVICE_FAMILY = "1,2"; 235 | }; 236 | name = Debug; 237 | }; 238 | 42E1B7C419422EFC0022E796 /* Release */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | ALWAYS_SEARCH_USER_PATHS = NO; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_WARN_BOOL_CONVERSION = YES; 247 | CLANG_WARN_CONSTANT_CONVERSION = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 256 | COPY_PHASE_STRIP = YES; 257 | ENABLE_NS_ASSERTIONS = NO; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 267 | METAL_ENABLE_DEBUG_INFO = NO; 268 | SDKROOT = iphoneos; 269 | TARGETED_DEVICE_FAMILY = "1,2"; 270 | VALIDATE_PRODUCT = YES; 271 | }; 272 | name = Release; 273 | }; 274 | 42E1B7C619422EFC0022E796 /* Debug */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 278 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 279 | INFOPLIST_FILE = Basic_iOS/Info.plist; 280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 281 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.${PRODUCT_NAME:rfc1034identifier}"; 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | }; 284 | name = Debug; 285 | }; 286 | 42E1B7C719422EFC0022E796 /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 291 | INFOPLIST_FILE = Basic_iOS/Info.plist; 292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 293 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.${PRODUCT_NAME:rfc1034identifier}"; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | 42E1B7A419422EFC0022E796 /* Build configuration list for PBXProject "Basic_iOS" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | 42E1B7C319422EFC0022E796 /* Debug */, 305 | 42E1B7C419422EFC0022E796 /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | 42E1B7C519422EFC0022E796 /* Build configuration list for PBXNativeTarget "Basic_iOS" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | 42E1B7C619422EFC0022E796 /* Debug */, 314 | 42E1B7C719422EFC0022E796 /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | /* End XCConfigurationList section */ 320 | }; 321 | rootObject = 42E1B7A119422EFC0022E796 /* Project object */; 322 | } 323 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/project.xcworkspace/xcshareddata/Basic_iOS.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | A1624A65-2C26-4595-8115-88819F2A0CAC 9 | IDESourceControlProjectName 10 | Basic_iOS 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 14 | https://github.com/neoneye/SpecificationPattern.git 15 | 16 | IDESourceControlProjectPath 17 | Examples/Basic_iOS/Basic_iOS.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 21 | ../../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/neoneye/SpecificationPattern.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 36 | IDESourceControlWCCName 37 | swift-specification-pattern 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcschemes/Basic_iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Basic_iOS.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 42E1B7A819422EFC0022E796 16 | 17 | primary 18 | 19 | 20 | 42E1B7BA19422EFC0022E796 21 | 22 | primary 23 | 24 | 25 | 42E1B7D419422F510022E796 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Basic_iOS 4 | // 5 | // Created by Simon Strandgaard on 06/06/14. 6 | // Copyright (c) 2014 None. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 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 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "29x29", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "40x40", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "76x76", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/Basic_iOS/Basic_iOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Basic_iOS 4 | // 5 | // Created by Simon Strandgaard on 06/06/14. 6 | // Copyright (c) 2014 None. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, UITextFieldDelegate { 12 | 13 | @IBOutlet weak var textField : UITextField! 14 | 15 | let validSpecification : Specification 16 | let partialSpecification : Specification 17 | 18 | required init(coder aDecoder: NSCoder) { 19 | validSpecification = ViewController.createValidSpecification() 20 | partialSpecification = ViewController.createPartialSpecification() 21 | super.init(coder: aDecoder) 22 | } 23 | 24 | override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) { 25 | validSpecification = ViewController.createValidSpecification() 26 | partialSpecification = ViewController.createPartialSpecification() 27 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 28 | } 29 | 30 | class func createValidSpecification() -> Specification { 31 | let spec0 = RegularExpressionSpecification(pattern: "^taylor$") 32 | let spec1 = RegularExpressionSpecification(pattern: "^swift$") 33 | return spec0 | spec1 34 | } 35 | 36 | class func createPartialSpecification() -> Specification { 37 | let spec0 = RegularExpressionSpecification(pattern: "^t?a?y?l?o?r?$") 38 | let spec1 = RegularExpressionSpecification(pattern: "^s?w?i?f?t?$") 39 | return spec0 | spec1 40 | } 41 | 42 | override func viewDidLoad() { 43 | super.viewDidLoad() 44 | textField.delegate = self 45 | } 46 | 47 | override func viewWillAppear(animated: Bool) { 48 | super.viewWillAppear(animated) 49 | updateColorForText("") 50 | } 51 | 52 | override func viewDidAppear(animated: Bool) { 53 | super.viewDidAppear(animated) 54 | textField.becomeFirstResponder() 55 | } 56 | 57 | func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { 58 | if let originalText : NSString = textField.text { 59 | let newText = originalText.stringByReplacingCharactersInRange(range, withString: string) 60 | updateColorForText(newText) 61 | } 62 | return true 63 | } 64 | 65 | func updateColorForText(text: String?) { 66 | if text == nil { 67 | return 68 | } 69 | if validSpecification == text { 70 | textField.backgroundColor = UIColor.greenColor() 71 | return 72 | } 73 | if partialSpecification == text { 74 | textField.backgroundColor = UIColor.whiteColor() 75 | return 76 | } 77 | textField.backgroundColor = UIColor.redColor() 78 | } 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Simon Strandgaard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpecificationPattern 2 | 3 | The **Specification** design pattern implemented in swift for iOS/OSX. 4 | 5 | > In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. 6 | > 7 | > **Source:** [wikipedia.org](http://en.wikipedia.org/wiki/Specification_pattern) 8 | 9 | 10 | ## Requirements 11 | 12 | - Xcode 7.0 beta 4 (7A165t) 13 | - Swift 2 14 | - iOS 9.0+ / Mac OS X 10.10+ 15 | 16 | 17 | ## Usage 18 | 19 | Chain two specifications into a single specification. 20 | 21 | ```swift 22 | let s0 = RegularExpressionSpecification(pattern: "hello.*world") 23 | let s1 = CountSpecification.between(20, 30) 24 | let spec = s0 & s1 25 | 26 | spec == "42" 27 | # false, doesn't satify s0 and s1 28 | spec == "hello world" 29 | # false, s0 is satisfied, but not s1 30 | spec == "say hello to the world today" 31 | # true 32 | ``` 33 | 34 | ## Useful classes 35 | 36 | Beyond the specification pattern itself, this project provides the following iOS/OSX specifications 37 | 38 | * CharacterSetSpecification - for ensuring all characters in a string are of a certain kind, eg. all digits 39 | * CountSpecification - for ensuring that a string/array isn't too long or short 40 | * EmailSpecification - decide if an email is valid or not 41 | * PredicateSpecification - if you don't want to subclass you can use this and instead provide a closure 42 | * RegularExpressionSpecification - useful for string matching 43 | 44 | 45 | ## Operators 46 | 47 | #### And operator 48 | 49 | Use the `&` operator when two specifications both must be satisfied. 50 | 51 | 52 | #### Or operator 53 | 54 | Use the `|` operator when one of two specifications must be satisfied. 55 | 56 | 57 | #### Not operator 58 | 59 | Use the `!` operator when a specifications must not be satisfied. 60 | 61 | 62 | #### Equal operator 63 | 64 | Use the `==` operator to check if a candidate object satisfies the specification. 65 | 66 | 67 | #### Not equal operator 68 | 69 | Use the `!=` operator to check if a candidate object doesn't satisfy the specification. 70 | 71 | 72 | 73 | ## Example - Invoice handling 74 | 75 | In the following example, we are retrieving invoices and sending them to a collection agency if 76 | 77 | 1. they are overdue, 78 | 2. notices have been sent, and 79 | 3. they are not already with the collection agency. 80 | 81 | This example is meant to show the end result of how the logic is 'chained' together. 82 | 83 | ```swift 84 | let overDue = OverDueSpecification() 85 | let noticeSent = NoticeSentSpecification() 86 | let inCollection = InCollectionSpecification() 87 | 88 | // example of specification pattern logic chaining 89 | let spec = overDue & noticeSent & !inCollection 90 | 91 | for invoice in Service.invoices() where spec == invoice { 92 | invoice.sendToCollection() 93 | } 94 | ``` 95 | 96 | ## Sample project - Live text validation 97 | 98 | ![](example0.gif) 99 | 100 | The coloring are like this: 101 | 102 | 1. Red, when the text neither can be "taylor" nor "swift" 103 | 2. White, when the text is partially matching either "taylor" or "swift" 104 | 3. Green, when the text is exactly "taylor" or exactly "swift". 105 | 106 | You find this in the Basic_iOS project in the Examples folder. 107 | 108 | 109 | ## Simon Strandgaard's App Projects 110 | 111 | 1. [SwiftyFORM](https://github.com/neoneye/SwiftyFORM) - A framework that makes it easier creating forms for iOS. 112 | 2. [Triangle Draw](http://www.triangledraw.com/) - A simple yet powerful drawing app for iOS. 113 | 3. [Colorblind Vision](https://itunes.apple.com/dk/app/colorblind-vision/id401516863?mt=8) - An app that processes live video and presents it as a colorblind person would see it, for iOS. 114 | 4. [Newton Commander](https://github.com/neoneye/newton-commander) - An advanced file manager for Mac. 115 | 5. [GraphToy](https://github.com/neoneye/GraphToy) - A werkkzeug inspired tool for iPad. 116 | 6. [GraphicDesignerToolbox](http://graphicdesignertoolbox.com/) - A werkkzeug inspired tool for Mac. 117 | 7. [AEditor](https://github.com/neoneye/aeditor) - A programming editor for Mac/Windows/Linux. 118 | -------------------------------------------------------------------------------- /SpecificationPattern.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SpecificationPattern" 3 | s.version = "0.9.0" 4 | s.summary = "The Specification pattern implemented in swift" 5 | s.description = <<-DESC 6 | Business logic is the heartbeat of our applications. 7 | It is what makes your application worth something to the business. 8 | This simple pattern takes the complex business logic and turn it 9 | into a more manageable and readable piece of art. 10 | DESC 11 | s.homepage = "https://github.com/neoneye/SpecificationPattern" 12 | s.screenshots = "https://raw.githubusercontent.com/neoneye/SpecificationPattern/master/example0.gif" 13 | s.license = { :type => "MIT", :file => "LICENSE" } 14 | s.author = { "Simon Strandgaard" => "neoneye@gmail.com" } 15 | s.social_media_url = "http://twitter.com/neoneye" 16 | s.source = { :git => "https://github.com/neoneye/SpecificationPattern.git", :tag => s.version } 17 | s.source_files = "Classes/*.swift" 18 | s.requires_arc = true 19 | end 20 | -------------------------------------------------------------------------------- /Tests/AdvancedTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class AdvancedTests: XCTestCase { 4 | 5 | func testAdvanced0() { 6 | /* 7 | This specification checks these contraints 8 | 1. the string must be all digits 9 | 2. the string.length must be between 2..4 10 | 3. the string must not contain two zeroes 11 | */ 12 | let onlyDigits = CharacterSetSpecification.decimalDigitCharacterSet() 13 | let between2And4Letters = CountSpecification.between(2, 4) 14 | let twoZeroes = RegularExpressionSpecification(pattern: "0.*0") 15 | 16 | let spec = onlyDigits.and(between2And4Letters).and(twoZeroes.not()) 17 | 18 | XCTAssertTrue(spec.isSatisfiedBy("42")) 19 | XCTAssertTrue(spec.isSatisfiedBy("0123")) 20 | XCTAssertTrue(spec.isSatisfiedBy("666")) 21 | XCTAssertFalse(spec.isSatisfiedBy("ice")) 22 | XCTAssertFalse(spec.isSatisfiedBy("too long")) 23 | XCTAssertFalse(spec.isSatisfiedBy("00")) 24 | XCTAssertFalse(spec.isSatisfiedBy("1010")) 25 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 26 | } 27 | 28 | func testFilter0() { 29 | /* 30 | This specification is used for filtering an array of records (movies in the alien franchise). 31 | Here we finds all the movies directed by Ridley Scott. 32 | */ 33 | let spec = RegularExpressionSpecification(pattern: "Ridley Scott") 34 | let filterSpec = PredicateSpecification { (candidate: MovieRecord) -> Bool in 35 | return spec.isSatisfiedBy(candidate.directorsWriters) 36 | } 37 | let result = movieRecords().filter { filterSpec.isSatisfiedBy($0) } 38 | // Ridley Scott has directed 2 movies: Alien and Prometheus 39 | XCTAssertEqual(2, result.count) 40 | } 41 | 42 | func testFilter1() { 43 | /* 44 | This specification is used for filtering an array of records (movies in the alien franchise). 45 | Here we finds the cheapest movies and also the most expensive movies. 46 | */ 47 | let nonZeroBudget = PredicateSpecification { (candidate: MovieRecord) -> Bool in 48 | return candidate.budget > 0 49 | } 50 | let smallBudget = PredicateSpecification { (candidate: MovieRecord) -> Bool in 51 | // cheaper than 20 millions USD 52 | return candidate.budget < 20 53 | } 54 | let bigBudget = PredicateSpecification { (candidate: MovieRecord) -> Bool in 55 | // more expensive than 70 millions USD 56 | return candidate.budget > 70 57 | } 58 | let filterSpec = nonZeroBudget.and(smallBudget.or(bigBudget)) 59 | let result = movieRecords().filter { filterSpec.isSatisfiedBy($0) } 60 | // There are 2 cheap movies and 2 expensive movies 61 | XCTAssertEqual(4, result.count) 62 | } 63 | 64 | class MovieRecord { 65 | var recordId: Int = 0 66 | var name: String = "" 67 | var released: Int = 0 68 | var directorsWriters: String = "" 69 | var budget: Double = 0.0 70 | var runningTime: Int = 0 71 | } 72 | 73 | func movieRecords() -> [MovieRecord] { 74 | // Read a CSV file 75 | let path = NSBundle(forClass: self.dynamicType).pathForResource("alien", ofType: "csv") 76 | assert(path != nil) 77 | let dataString = try! NSString(contentsOfFile: path!, encoding: NSUTF8StringEncoding) 78 | 79 | // Split CSV data into rows and fields 80 | let rows = dataString.componentsSeparatedByString("\n") 81 | var records: [MovieRecord] = [] 82 | for row in rows { 83 | let cells = row.componentsSeparatedByString(";") 84 | assert(cells.count == 6) 85 | 86 | // Create record populated with CSV data 87 | let record: MovieRecord = MovieRecord() 88 | record.recordId = Int((cells[0] as String)) ?? 0 89 | record.name = cells[1] as String 90 | record.released = Int((cells[2] as String)) ?? 0 91 | record.directorsWriters = cells[3] as String 92 | record.budget = NSString(string: (cells[4] as String)).doubleValue 93 | record.runningTime = Int((cells[5] as String)) ?? 0 94 | records.append(record) 95 | } 96 | 97 | return records 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /Tests/CharacterSetSpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class CharacterSetSpecificationTests: XCTestCase { 4 | 5 | func testCharactersInString() { 6 | let spec = CharacterSetSpecification.charactersInString("abc") 7 | XCTAssertTrue(spec.isSatisfiedBy("")) 8 | XCTAssertTrue(spec.isSatisfiedBy("abc")) 9 | XCTAssertFalse(spec.isSatisfiedBy("0123hello")) 10 | XCTAssertFalse(spec.isSatisfiedBy("hello")) 11 | } 12 | 13 | func testDigit() { 14 | let spec = CharacterSetSpecification.decimalDigitCharacterSet() 15 | XCTAssertTrue(spec.isSatisfiedBy("")) 16 | XCTAssertTrue(spec.isSatisfiedBy("0123456789")) 17 | XCTAssertFalse(spec.isSatisfiedBy("0123hello")) 18 | XCTAssertFalse(spec.isSatisfiedBy("hello")) 19 | } 20 | 21 | func testWhitespaceAndNewline() { 22 | let spec = CharacterSetSpecification.whitespaceAndNewlineCharacterSet() 23 | XCTAssertTrue(spec.isSatisfiedBy("")) 24 | XCTAssertTrue(spec.isSatisfiedBy(" \t \n ")) 25 | XCTAssertFalse(spec.isSatisfiedBy("---")) 26 | XCTAssertFalse(spec.isSatisfiedBy("###")) 27 | } 28 | } -------------------------------------------------------------------------------- /Tests/CountSpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class CountSpecificationTests: XCTestCase { 4 | 5 | func testStringMin() { 6 | let spec = CountSpecification.min(3) 7 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 8 | XCTAssertFalse(spec.isSatisfiedBy("")) 9 | XCTAssertFalse(spec.isSatisfiedBy("0")) 10 | XCTAssertFalse(spec.isSatisfiedBy("01")) 11 | XCTAssertTrue(spec.isSatisfiedBy("012")) 12 | XCTAssertTrue(spec.isSatisfiedBy("0123")) 13 | } 14 | 15 | func testStringMax() { 16 | let spec = CountSpecification.max(2) 17 | XCTAssertTrue(spec.isSatisfiedBy("")) 18 | XCTAssertTrue(spec.isSatisfiedBy("0")) 19 | XCTAssertTrue(spec.isSatisfiedBy("01")) 20 | XCTAssertFalse(spec.isSatisfiedBy("012")) 21 | XCTAssertFalse(spec.isSatisfiedBy("0123")) 22 | } 23 | 24 | func testStringBetween() { 25 | let spec = CountSpecification.between(2, 4) 26 | XCTAssertFalse(spec.isSatisfiedBy("")) 27 | XCTAssertFalse(spec.isSatisfiedBy("0")) 28 | XCTAssertTrue(spec.isSatisfiedBy("01")) 29 | XCTAssertTrue(spec.isSatisfiedBy("012")) 30 | XCTAssertTrue(spec.isSatisfiedBy("0123")) 31 | XCTAssertFalse(spec.isSatisfiedBy("01234")) 32 | XCTAssertFalse(spec.isSatisfiedBy("012345")) 33 | } 34 | 35 | func testStringExcactly() { 36 | let spec = CountSpecification.exactly(2) 37 | XCTAssertFalse(spec.isSatisfiedBy("")) 38 | XCTAssertFalse(spec.isSatisfiedBy("0")) 39 | XCTAssertTrue(spec.isSatisfiedBy("01")) 40 | XCTAssertFalse(spec.isSatisfiedBy("012")) 41 | XCTAssertFalse(spec.isSatisfiedBy("0123")) 42 | } 43 | 44 | func testArrayMin() { 45 | let array0: [Bool] = [] 46 | let spec = CountSpecification.min(3) 47 | XCTAssertFalse(spec.isSatisfiedBy( array0 )) 48 | XCTAssertFalse(spec.isSatisfiedBy( [0] )) 49 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1] )) 50 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1, 2] )) 51 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1, 2, 3] )) 52 | } 53 | 54 | func testArrayMax() { 55 | let array0: [Bool] = [] 56 | let spec = CountSpecification.max(2) 57 | XCTAssertTrue(spec.isSatisfiedBy( array0 )) 58 | XCTAssertTrue(spec.isSatisfiedBy( [0] )) 59 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1] )) 60 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2] )) 61 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2, 3] )) 62 | } 63 | 64 | func testArrayBetween() { 65 | let array0: [Bool] = [] 66 | let spec = CountSpecification.between(2, 4) 67 | XCTAssertFalse(spec.isSatisfiedBy(array0)) 68 | XCTAssertFalse(spec.isSatisfiedBy( [0] )) 69 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1] )) 70 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1, 2] )) 71 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1, 2, 3] )) 72 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2, 3, 4] )) 73 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2, 3, 4, 5] )) 74 | } 75 | 76 | func testArrayExactly() { 77 | let array0: [Bool] = [] 78 | let spec = CountSpecification.exactly(2) 79 | XCTAssertFalse(spec.isSatisfiedBy(array0)) 80 | XCTAssertFalse(spec.isSatisfiedBy( [0] )) 81 | XCTAssertTrue(spec.isSatisfiedBy( [0, 1] )) 82 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2] )) 83 | XCTAssertFalse(spec.isSatisfiedBy( [0, 1, 2, 3] )) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Tests/EmailSpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class EmailSpecificationTests: XCTestCase { 4 | 5 | func testBasic() { 6 | let spec = EmailSpecification() 7 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 8 | XCTAssertFalse(spec.isSatisfiedBy("")) 9 | XCTAssertFalse(spec.isSatisfiedBy("not a valid email")) 10 | XCTAssertFalse(spec.isSatisfiedBy("0123456789")) 11 | XCTAssertFalse(spec.isSatisfiedBy("Abc.example.com")) // an @ character must separate the local and domain parts 12 | XCTAssertFalse(spec.isSatisfiedBy("john.doe@example..com")) // double dot after @ 13 | XCTAssertFalse(spec.isSatisfiedBy("@example.com")) 14 | XCTAssertTrue(spec.isSatisfiedBy("john-doe@example.com")) 15 | XCTAssertTrue(spec.isSatisfiedBy("John.Smith@example.com")) 16 | XCTAssertTrue(spec.isSatisfiedBy("jsmith@[192.168.2.1]")) 17 | XCTAssertTrue(spec.isSatisfiedBy("jsmith@192.168.2.1")) 18 | XCTAssertTrue(spec.isSatisfiedBy("disposable.style.email.with+symbol@example.com")) 19 | XCTAssertTrue(spec.isSatisfiedBy("hello@subdomain.example.co.jp")) 20 | XCTAssertTrue(spec.isSatisfiedBy("spaces\\ are\\ allowed@example.com")) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Tests/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 | -------------------------------------------------------------------------------- /Tests/OperatorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class OperatorTests: XCTestCase { 4 | 5 | func testOperator0() { 6 | /* 7 | This specification checks these contraints 8 | 1. the string must be all digits 9 | 2. the string.length must be between 2..4 10 | 3. the string must not contain two zeroes 11 | */ 12 | let onlyDigits = CharacterSetSpecification.decimalDigitCharacterSet() 13 | let between2And4Letters = RegularExpressionSpecification(pattern: "^.{2,4}$") 14 | let twoZeroes = RegularExpressionSpecification(pattern: "0.*0") 15 | 16 | let spec = onlyDigits & between2And4Letters & !twoZeroes 17 | 18 | XCTAssertTrue(spec == "42") 19 | XCTAssertTrue(spec == "0123") 20 | XCTAssertTrue(spec == "666") 21 | XCTAssertFalse(spec == "ice") 22 | XCTAssertFalse(spec == "too long") 23 | XCTAssertFalse(spec == "00") 24 | XCTAssertFalse(spec == "1010") 25 | 26 | XCTAssertFalse(spec != "42") 27 | XCTAssertFalse(spec != "0123") 28 | XCTAssertFalse(spec != "666") 29 | XCTAssertTrue(spec != "ice") 30 | XCTAssertTrue(spec != "too long") 31 | XCTAssertTrue(spec != "00") 32 | XCTAssertTrue(spec != "1010") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Tests/PredicateSpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class PredicateSpecificationTests: XCTestCase { 4 | 5 | func testString0() { 6 | let spec = PredicateSpecification { (candidate: String) -> Bool in 7 | return candidate.hasPrefix("hello") 8 | } 9 | XCTAssertTrue(spec.isSatisfiedBy("hello world")) 10 | XCTAssertTrue(spec.isSatisfiedBy("hello lambda world")) 11 | XCTAssertFalse(spec.isSatisfiedBy("hell")) 12 | XCTAssertFalse(spec.isSatisfiedBy("")) 13 | XCTAssertFalse(spec.isSatisfiedBy("nope")) 14 | XCTAssertFalse(spec.isSatisfiedBy(1234)) 15 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 16 | } 17 | 18 | func testInteger0() { 19 | let spec = PredicateSpecification { (candidate: Int) -> Bool in 20 | return candidate > 5 21 | } 22 | XCTAssertTrue(spec.isSatisfiedBy(10)) 23 | XCTAssertFalse(spec.isSatisfiedBy(0)) 24 | XCTAssertFalse(spec.isSatisfiedBy("not integer")) 25 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 26 | XCTAssertFalse(spec.isSatisfiedBy(10.0)) // float is not integer, thus it's false 27 | } 28 | 29 | func testCustomer0() { 30 | class Customer { 31 | let name: String 32 | init(name: String) { 33 | self.name = name 34 | } 35 | } 36 | 37 | let spec = PredicateSpecification { (candidate: Customer) -> Bool in 38 | return candidate.name.hasPrefix("john") 39 | } 40 | XCTAssertTrue(spec.isSatisfiedBy(Customer(name: "john doe"))) 41 | XCTAssertFalse(spec.isSatisfiedBy(Customer(name: "jane doe"))) 42 | XCTAssertFalse(spec.isSatisfiedBy(0)) 43 | XCTAssertFalse(spec.isSatisfiedBy("not a customer")) 44 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 45 | XCTAssertFalse(spec.isSatisfiedBy(10.0)) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Tests/RegularExpressionSpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class RegularExpressionSpecificationTests: XCTestCase { 4 | 5 | func testSimple() { 6 | let spec = RegularExpressionSpecification(pattern: "^\\d+$") 7 | XCTAssertTrue(spec.isSatisfiedBy("123")) 8 | XCTAssertFalse(spec.isSatisfiedBy("abc")) 9 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 420E46451A00329E00AD5779 /* TrueFalseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420E46441A00329E00AD5779 /* TrueFalseTests.swift */; }; 11 | 420E46461A00333500AD5779 /* TrueFalseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420E46441A00329E00AD5779 /* TrueFalseTests.swift */; }; 12 | 420E46471A00338900AD5779 /* alien.csv in Resources */ = {isa = PBXBuildFile; fileRef = 42B9E0F519E5B3BC00EBCCDE /* alien.csv */; }; 13 | 4250D8351947B5D000537779 /* CharacterSetSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8341947B5D000537779 /* CharacterSetSpecification.swift */; }; 14 | 4250D8371947B63000537779 /* CharacterSetSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8361947B63000537779 /* CharacterSetSpecificationTests.swift */; }; 15 | 4250D8391947B93400537779 /* RegularExpressionSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8381947B93400537779 /* RegularExpressionSpecificationTests.swift */; }; 16 | 4250D83B1947B98A00537779 /* AdvancedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D83A1947B98A00537779 /* AdvancedTests.swift */; }; 17 | 4288530B19C9FF3F00362DE5 /* CountSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4288530A19C9FF3F00362DE5 /* CountSpecification.swift */; }; 18 | 4288530D19CA007C00362DE5 /* CountSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4288530C19CA007C00362DE5 /* CountSpecificationTests.swift */; }; 19 | 4288531B19CA02CB00362DE5 /* CharacterSetSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8341947B5D000537779 /* CharacterSetSpecification.swift */; }; 20 | 4288531C19CA02CB00362DE5 /* Operator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 428F9FEC197E7FDC00D327EC /* Operator.swift */; }; 21 | 4288531D19CA02CB00362DE5 /* PredicateSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C69FC7194A01F30017DFAB /* PredicateSpecification.swift */; }; 22 | 4288531E19CA02CB00362DE5 /* RegularExpressionSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B7F33D19479AC200CBB434 /* RegularExpressionSpecification.swift */; }; 23 | 4288531F19CA02CB00362DE5 /* Specification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B7F33E19479AC200CBB434 /* Specification.swift */; }; 24 | 4288532019CA02CB00362DE5 /* CountSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4288530A19C9FF3F00362DE5 /* CountSpecification.swift */; }; 25 | 4288532119CA02CB00362DE5 /* AdvancedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D83A1947B98A00537779 /* AdvancedTests.swift */; }; 26 | 4288532219CA02CB00362DE5 /* CharacterSetSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8361947B63000537779 /* CharacterSetSpecificationTests.swift */; }; 27 | 4288532319CA02CB00362DE5 /* CountSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4288530C19CA007C00362DE5 /* CountSpecificationTests.swift */; }; 28 | 4288532419CA02CB00362DE5 /* OperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 428F9FEA197E7FC700D327EC /* OperatorTests.swift */; }; 29 | 4288532519CA02CB00362DE5 /* PredicateSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C69FC9194A023C0017DFAB /* PredicateSpecificationTests.swift */; }; 30 | 4288532619CA02CB00362DE5 /* RegularExpressionSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4250D8381947B93400537779 /* RegularExpressionSpecificationTests.swift */; }; 31 | 4288532719CA02CB00362DE5 /* SpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E1B80D194241AD0022E796 /* SpecificationTests.swift */; }; 32 | 428F9FEB197E7FC700D327EC /* OperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 428F9FEA197E7FC700D327EC /* OperatorTests.swift */; }; 33 | 428F9FED197E7FDC00D327EC /* Operator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 428F9FEC197E7FDC00D327EC /* Operator.swift */; }; 34 | 42935FC71A1A55F300C06025 /* EmailSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42935FC61A1A55F300C06025 /* EmailSpecificationTests.swift */; }; 35 | 42935FC81A1A565600C06025 /* EmailSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42935FC61A1A55F300C06025 /* EmailSpecificationTests.swift */; }; 36 | 42935FC91A1A566700C06025 /* EmailSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42935FC41A1A54BE00C06025 /* EmailSpecification.swift */; }; 37 | 42935FCA1A1A566800C06025 /* EmailSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42935FC41A1A54BE00C06025 /* EmailSpecification.swift */; }; 38 | 42B7F33F19479AC200CBB434 /* RegularExpressionSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B7F33D19479AC200CBB434 /* RegularExpressionSpecification.swift */; }; 39 | 42B7F34019479AC200CBB434 /* Specification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B7F33E19479AC200CBB434 /* Specification.swift */; }; 40 | 42B9E0F619E5B3BC00EBCCDE /* alien.csv in Resources */ = {isa = PBXBuildFile; fileRef = 42B9E0F519E5B3BC00EBCCDE /* alien.csv */; }; 41 | 42C69FC8194A01F30017DFAB /* PredicateSpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C69FC7194A01F30017DFAB /* PredicateSpecification.swift */; }; 42 | 42C69FCA194A023C0017DFAB /* PredicateSpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C69FC9194A023C0017DFAB /* PredicateSpecificationTests.swift */; }; 43 | 42E1B80E194241AD0022E796 /* SpecificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E1B80D194241AD0022E796 /* SpecificationTests.swift */; }; 44 | /* End PBXBuildFile section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 420E46441A00329E00AD5779 /* TrueFalseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrueFalseTests.swift; sourceTree = ""; }; 48 | 4250D8341947B5D000537779 /* CharacterSetSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterSetSpecification.swift; sourceTree = ""; }; 49 | 4250D8361947B63000537779 /* CharacterSetSpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterSetSpecificationTests.swift; sourceTree = ""; }; 50 | 4250D8381947B93400537779 /* RegularExpressionSpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegularExpressionSpecificationTests.swift; sourceTree = ""; }; 51 | 4250D83A1947B98A00537779 /* AdvancedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedTests.swift; sourceTree = ""; }; 52 | 4288530A19C9FF3F00362DE5 /* CountSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountSpecification.swift; sourceTree = ""; }; 53 | 4288530C19CA007C00362DE5 /* CountSpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountSpecificationTests.swift; sourceTree = ""; }; 54 | 4288531219CA027B00362DE5 /* Test-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 4288531519CA027B00362DE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 428F9FEA197E7FC700D327EC /* OperatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorTests.swift; sourceTree = ""; }; 57 | 428F9FEC197E7FDC00D327EC /* Operator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operator.swift; sourceTree = ""; }; 58 | 42935FC41A1A54BE00C06025 /* EmailSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailSpecification.swift; sourceTree = ""; }; 59 | 42935FC61A1A55F300C06025 /* EmailSpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailSpecificationTests.swift; sourceTree = ""; }; 60 | 42B7F33D19479AC200CBB434 /* RegularExpressionSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegularExpressionSpecification.swift; sourceTree = ""; }; 61 | 42B7F33E19479AC200CBB434 /* Specification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Specification.swift; sourceTree = ""; }; 62 | 42B9E0F519E5B3BC00EBCCDE /* alien.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = alien.csv; sourceTree = ""; }; 63 | 42C69FC7194A01F30017DFAB /* PredicateSpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateSpecification.swift; sourceTree = ""; }; 64 | 42C69FC9194A023C0017DFAB /* PredicateSpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateSpecificationTests.swift; sourceTree = ""; }; 65 | 42E1B7FD194240A00022E796 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 42E1B80D194241AD0022E796 /* SpecificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpecificationTests.swift; sourceTree = ""; }; 67 | 42E1B80F194241B90022E796 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 4288530F19CA027B00362DE5 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 42E1B7FA194240A00022E796 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 4288531319CA027B00362DE5 /* Test-iOS */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 42B9E0F519E5B3BC00EBCCDE /* alien.csv */, 92 | 4288531419CA027B00362DE5 /* Supporting Files */, 93 | ); 94 | path = "Test-iOS"; 95 | sourceTree = ""; 96 | }; 97 | 4288531419CA027B00362DE5 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4288531519CA027B00362DE5 /* Info.plist */, 101 | ); 102 | name = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | 42B7F33C19479AC200CBB434 /* Classes */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 4250D8341947B5D000537779 /* CharacterSetSpecification.swift */, 109 | 4288530A19C9FF3F00362DE5 /* CountSpecification.swift */, 110 | 42935FC41A1A54BE00C06025 /* EmailSpecification.swift */, 111 | 428F9FEC197E7FDC00D327EC /* Operator.swift */, 112 | 42C69FC7194A01F30017DFAB /* PredicateSpecification.swift */, 113 | 42B7F33D19479AC200CBB434 /* RegularExpressionSpecification.swift */, 114 | 42B7F33E19479AC200CBB434 /* Specification.swift */, 115 | ); 116 | name = Classes; 117 | path = ../Classes; 118 | sourceTree = ""; 119 | }; 120 | 42E1B7E6194240640022E796 = { 121 | isa = PBXGroup; 122 | children = ( 123 | 42B7F33C19479AC200CBB434 /* Classes */, 124 | 4250D83A1947B98A00537779 /* AdvancedTests.swift */, 125 | 4250D8361947B63000537779 /* CharacterSetSpecificationTests.swift */, 126 | 4288530C19CA007C00362DE5 /* CountSpecificationTests.swift */, 127 | 42935FC61A1A55F300C06025 /* EmailSpecificationTests.swift */, 128 | 42E1B80F194241B90022E796 /* Info.plist */, 129 | 428F9FEA197E7FC700D327EC /* OperatorTests.swift */, 130 | 42C69FC9194A023C0017DFAB /* PredicateSpecificationTests.swift */, 131 | 42E1B7F0194240640022E796 /* Products */, 132 | 4250D8381947B93400537779 /* RegularExpressionSpecificationTests.swift */, 133 | 42E1B80D194241AD0022E796 /* SpecificationTests.swift */, 134 | 4288531319CA027B00362DE5 /* Test-iOS */, 135 | 420E46441A00329E00AD5779 /* TrueFalseTests.swift */, 136 | ); 137 | sourceTree = ""; 138 | }; 139 | 42E1B7F0194240640022E796 /* Products */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 42E1B7FD194240A00022E796 /* Tests.xctest */, 143 | 4288531219CA027B00362DE5 /* Test-iOS.xctest */, 144 | ); 145 | name = Products; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | 4288531119CA027B00362DE5 /* Test-iOS */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = 4288531819CA027B00362DE5 /* Build configuration list for PBXNativeTarget "Test-iOS" */; 154 | buildPhases = ( 155 | 4288530E19CA027B00362DE5 /* Sources */, 156 | 4288530F19CA027B00362DE5 /* Frameworks */, 157 | 4288531019CA027B00362DE5 /* Resources */, 158 | ); 159 | buildRules = ( 160 | ); 161 | dependencies = ( 162 | ); 163 | name = "Test-iOS"; 164 | productName = "Test-iOS"; 165 | productReference = 4288531219CA027B00362DE5 /* Test-iOS.xctest */; 166 | productType = "com.apple.product-type.bundle.unit-test"; 167 | }; 168 | 42E1B7FC194240A00022E796 /* Tests */ = { 169 | isa = PBXNativeTarget; 170 | buildConfigurationList = 42E1B803194240A00022E796 /* Build configuration list for PBXNativeTarget "Tests" */; 171 | buildPhases = ( 172 | 42E1B7F9194240A00022E796 /* Sources */, 173 | 42E1B7FA194240A00022E796 /* Frameworks */, 174 | 42E1B7FB194240A00022E796 /* Resources */, 175 | ); 176 | buildRules = ( 177 | ); 178 | dependencies = ( 179 | ); 180 | name = Tests; 181 | productName = Tests; 182 | productReference = 42E1B7FD194240A00022E796 /* Tests.xctest */; 183 | productType = "com.apple.product-type.bundle.unit-test"; 184 | }; 185 | /* End PBXNativeTarget section */ 186 | 187 | /* Begin PBXProject section */ 188 | 42E1B7E7194240640022E796 /* Project object */ = { 189 | isa = PBXProject; 190 | attributes = { 191 | LastSwiftUpdateCheck = 0700; 192 | LastUpgradeCheck = 0700; 193 | ORGANIZATIONNAME = None; 194 | TargetAttributes = { 195 | 4288531119CA027B00362DE5 = { 196 | CreatedOnToolsVersion = 6.0; 197 | }; 198 | 42E1B7FC194240A00022E796 = { 199 | CreatedOnToolsVersion = 6.0; 200 | }; 201 | }; 202 | }; 203 | buildConfigurationList = 42E1B7EA194240640022E796 /* Build configuration list for PBXProject "SpecificationTesting" */; 204 | compatibilityVersion = "Xcode 3.2"; 205 | developmentRegion = English; 206 | hasScannedForEncodings = 0; 207 | knownRegions = ( 208 | en, 209 | ); 210 | mainGroup = 42E1B7E6194240640022E796; 211 | productRefGroup = 42E1B7F0194240640022E796 /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | 42E1B7FC194240A00022E796 /* Tests */, 216 | 4288531119CA027B00362DE5 /* Test-iOS */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | 4288531019CA027B00362DE5 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 42B9E0F619E5B3BC00EBCCDE /* alien.csv in Resources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | 42E1B7FB194240A00022E796 /* Resources */ = { 231 | isa = PBXResourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 420E46471A00338900AD5779 /* alien.csv in Resources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXResourcesBuildPhase section */ 239 | 240 | /* Begin PBXSourcesBuildPhase section */ 241 | 4288530E19CA027B00362DE5 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 4288532619CA02CB00362DE5 /* RegularExpressionSpecificationTests.swift in Sources */, 246 | 4288531B19CA02CB00362DE5 /* CharacterSetSpecification.swift in Sources */, 247 | 4288531D19CA02CB00362DE5 /* PredicateSpecification.swift in Sources */, 248 | 4288531C19CA02CB00362DE5 /* Operator.swift in Sources */, 249 | 4288532019CA02CB00362DE5 /* CountSpecification.swift in Sources */, 250 | 420E46461A00333500AD5779 /* TrueFalseTests.swift in Sources */, 251 | 4288532719CA02CB00362DE5 /* SpecificationTests.swift in Sources */, 252 | 4288532419CA02CB00362DE5 /* OperatorTests.swift in Sources */, 253 | 4288531F19CA02CB00362DE5 /* Specification.swift in Sources */, 254 | 4288531E19CA02CB00362DE5 /* RegularExpressionSpecification.swift in Sources */, 255 | 4288532319CA02CB00362DE5 /* CountSpecificationTests.swift in Sources */, 256 | 4288532119CA02CB00362DE5 /* AdvancedTests.swift in Sources */, 257 | 42935FC91A1A566700C06025 /* EmailSpecification.swift in Sources */, 258 | 4288532219CA02CB00362DE5 /* CharacterSetSpecificationTests.swift in Sources */, 259 | 42935FC81A1A565600C06025 /* EmailSpecificationTests.swift in Sources */, 260 | 4288532519CA02CB00362DE5 /* PredicateSpecificationTests.swift in Sources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | 42E1B7F9194240A00022E796 /* Sources */ = { 265 | isa = PBXSourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | 4288530D19CA007C00362DE5 /* CountSpecificationTests.swift in Sources */, 269 | 4250D8351947B5D000537779 /* CharacterSetSpecification.swift in Sources */, 270 | 42B7F34019479AC200CBB434 /* Specification.swift in Sources */, 271 | 42C69FCA194A023C0017DFAB /* PredicateSpecificationTests.swift in Sources */, 272 | 428F9FEB197E7FC700D327EC /* OperatorTests.swift in Sources */, 273 | 420E46451A00329E00AD5779 /* TrueFalseTests.swift in Sources */, 274 | 42935FC71A1A55F300C06025 /* EmailSpecificationTests.swift in Sources */, 275 | 42E1B80E194241AD0022E796 /* SpecificationTests.swift in Sources */, 276 | 428F9FED197E7FDC00D327EC /* Operator.swift in Sources */, 277 | 4250D83B1947B98A00537779 /* AdvancedTests.swift in Sources */, 278 | 4250D8371947B63000537779 /* CharacterSetSpecificationTests.swift in Sources */, 279 | 42B7F33F19479AC200CBB434 /* RegularExpressionSpecification.swift in Sources */, 280 | 42935FCA1A1A566800C06025 /* EmailSpecification.swift in Sources */, 281 | 4288530B19C9FF3F00362DE5 /* CountSpecification.swift in Sources */, 282 | 42C69FC8194A01F30017DFAB /* PredicateSpecification.swift in Sources */, 283 | 4250D8391947B93400537779 /* RegularExpressionSpecificationTests.swift in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin XCBuildConfiguration section */ 290 | 4288531919CA027B00362DE5 /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | FRAMEWORK_SEARCH_PATHS = ( 294 | "$(SDKROOT)/Developer/Library/Frameworks", 295 | "$(inherited)", 296 | ); 297 | GCC_PREPROCESSOR_DEFINITIONS = ( 298 | "DEBUG=1", 299 | "$(inherited)", 300 | ); 301 | INFOPLIST_FILE = "Test-iOS/Info.plist"; 302 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 303 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 304 | MTL_ENABLE_DEBUG_INFO = YES; 305 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.$(PRODUCT_NAME:rfc1034identifier)"; 306 | PRODUCT_NAME = "$(TARGET_NAME)"; 307 | SDKROOT = iphoneos; 308 | }; 309 | name = Debug; 310 | }; 311 | 4288531A19CA027B00362DE5 /* Release */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | FRAMEWORK_SEARCH_PATHS = ( 315 | "$(SDKROOT)/Developer/Library/Frameworks", 316 | "$(inherited)", 317 | ); 318 | INFOPLIST_FILE = "Test-iOS/Info.plist"; 319 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 320 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 321 | MTL_ENABLE_DEBUG_INFO = NO; 322 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.$(PRODUCT_NAME:rfc1034identifier)"; 323 | PRODUCT_NAME = "$(TARGET_NAME)"; 324 | SDKROOT = iphoneos; 325 | VALIDATE_PRODUCT = YES; 326 | }; 327 | name = Release; 328 | }; 329 | 42E1B7F4194240640022E796 /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ALWAYS_SEARCH_USER_PATHS = NO; 333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 334 | CLANG_CXX_LIBRARY = "libc++"; 335 | CLANG_ENABLE_MODULES = YES; 336 | CLANG_ENABLE_OBJC_ARC = YES; 337 | CLANG_WARN_BOOL_CONVERSION = YES; 338 | CLANG_WARN_CONSTANT_CONVERSION = YES; 339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 340 | CLANG_WARN_EMPTY_BODY = YES; 341 | CLANG_WARN_ENUM_CONVERSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_UNREACHABLE_CODE = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | COPY_PHASE_STRIP = NO; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | ENABLE_TESTABILITY = YES; 349 | GCC_C_LANGUAGE_STANDARD = gnu99; 350 | GCC_DYNAMIC_NO_PIC = NO; 351 | GCC_OPTIMIZATION_LEVEL = 0; 352 | GCC_PREPROCESSOR_DEFINITIONS = ( 353 | "DEBUG=1", 354 | "$(inherited)", 355 | ); 356 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | MACOSX_DEPLOYMENT_TARGET = 10.9; 364 | METAL_ENABLE_DEBUG_INFO = YES; 365 | ONLY_ACTIVE_ARCH = YES; 366 | SDKROOT = macosx; 367 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 368 | }; 369 | name = Debug; 370 | }; 371 | 42E1B7F5194240640022E796 /* Release */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 376 | CLANG_CXX_LIBRARY = "libc++"; 377 | CLANG_ENABLE_MODULES = YES; 378 | CLANG_ENABLE_OBJC_ARC = YES; 379 | CLANG_WARN_BOOL_CONVERSION = YES; 380 | CLANG_WARN_CONSTANT_CONVERSION = YES; 381 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 382 | CLANG_WARN_EMPTY_BODY = YES; 383 | CLANG_WARN_ENUM_CONVERSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 386 | CLANG_WARN_UNREACHABLE_CODE = YES; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | COPY_PHASE_STRIP = YES; 389 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 390 | ENABLE_NS_ASSERTIONS = NO; 391 | ENABLE_STRICT_OBJC_MSGSEND = YES; 392 | GCC_C_LANGUAGE_STANDARD = gnu99; 393 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 394 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 395 | GCC_WARN_UNDECLARED_SELECTOR = YES; 396 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 397 | GCC_WARN_UNUSED_FUNCTION = YES; 398 | GCC_WARN_UNUSED_VARIABLE = YES; 399 | MACOSX_DEPLOYMENT_TARGET = 10.9; 400 | METAL_ENABLE_DEBUG_INFO = NO; 401 | SDKROOT = macosx; 402 | }; 403 | name = Release; 404 | }; 405 | 42E1B804194240A00022E796 /* Debug */ = { 406 | isa = XCBuildConfiguration; 407 | buildSettings = { 408 | COMBINE_HIDPI_IMAGES = YES; 409 | FRAMEWORK_SEARCH_PATHS = ( 410 | "$(DEVELOPER_FRAMEWORKS_DIR)", 411 | "$(inherited)", 412 | ); 413 | GCC_PREPROCESSOR_DEFINITIONS = ( 414 | "DEBUG=1", 415 | "$(inherited)", 416 | ); 417 | INFOPLIST_FILE = Info.plist; 418 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 419 | METAL_ENABLE_DEBUG_INFO = YES; 420 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.${PRODUCT_NAME:rfc1034identifier}"; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | }; 423 | name = Debug; 424 | }; 425 | 42E1B805194240A00022E796 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | COMBINE_HIDPI_IMAGES = YES; 429 | FRAMEWORK_SEARCH_PATHS = ( 430 | "$(DEVELOPER_FRAMEWORKS_DIR)", 431 | "$(inherited)", 432 | ); 433 | INFOPLIST_FILE = Info.plist; 434 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 435 | METAL_ENABLE_DEBUG_INFO = NO; 436 | PRODUCT_BUNDLE_IDENTIFIER = "com.opcoders.mytestapps.${PRODUCT_NAME:rfc1034identifier}"; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | }; 439 | name = Release; 440 | }; 441 | /* End XCBuildConfiguration section */ 442 | 443 | /* Begin XCConfigurationList section */ 444 | 4288531819CA027B00362DE5 /* Build configuration list for PBXNativeTarget "Test-iOS" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | 4288531919CA027B00362DE5 /* Debug */, 448 | 4288531A19CA027B00362DE5 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | 42E1B7EA194240640022E796 /* Build configuration list for PBXProject "SpecificationTesting" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | 42E1B7F4194240640022E796 /* Debug */, 457 | 42E1B7F5194240640022E796 /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | 42E1B803194240A00022E796 /* Build configuration list for PBXNativeTarget "Tests" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 42E1B804194240A00022E796 /* Debug */, 466 | 42E1B805194240A00022E796 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | /* End XCConfigurationList section */ 472 | }; 473 | rootObject = 42E1B7E7194240640022E796 /* Project object */; 474 | } 475 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/project.xcworkspace/xcshareddata/SpecificationTesting.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 9A4ABF2E-AD94-414C-8D88-F3FC696949E3 9 | IDESourceControlProjectName 10 | SpecificationTesting 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 14 | https://github.com/neoneye/SpecificationPattern.git 15 | 16 | IDESourceControlProjectPath 17 | Tests/SpecificationTesting.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/neoneye/SpecificationPattern.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 49F6C4F898F10D3F11FD8CCADDCBF6579035BE0F 36 | IDESourceControlWCCName 37 | swift-specification-pattern 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcschemes/Test-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcschemes/Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Tests/SpecificationTesting.xcodeproj/xcuserdata/neoneye.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Test-iOS.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | Tests.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 4288531119CA027B00362DE5 21 | 22 | primary 23 | 24 | 25 | 42E1B7EE194240640022E796 26 | 27 | primary 28 | 29 | 30 | 42E1B7FC194240A00022E796 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Tests/SpecificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class SpecificationTests: XCTestCase { 4 | 5 | func testAnd() { 6 | let spec0 = RegularExpressionSpecification(pattern: "^hello") 7 | let spec1 = RegularExpressionSpecification(pattern: "world$") 8 | let spec = spec0.and(spec1) 9 | XCTAssertTrue(spec.isSatisfiedBy("hello world")) 10 | XCTAssertTrue(spec.isSatisfiedBy("hello swift world")) 11 | XCTAssertFalse(spec.isSatisfiedBy("world hello")) 12 | XCTAssertFalse(spec.isSatisfiedBy("xyz hello world xzy")) 13 | } 14 | 15 | func testOr() { 16 | let spec0 = RegularExpressionSpecification(pattern: "hello") 17 | let spec1 = RegularExpressionSpecification(pattern: "world") 18 | let spec = spec0.or(spec1) 19 | XCTAssertTrue(spec.isSatisfiedBy("hello world")) 20 | XCTAssertTrue(spec.isSatisfiedBy("hello")) 21 | XCTAssertTrue(spec.isSatisfiedBy("world")) 22 | XCTAssertFalse(spec.isSatisfiedBy("johndoe")) 23 | } 24 | 25 | func testNot() { 26 | let spec0 = RegularExpressionSpecification(pattern: "hello") 27 | let spec = spec0.not() 28 | XCTAssertFalse(spec.isSatisfiedBy("hello")) 29 | XCTAssertTrue(spec.isSatisfiedBy("world")) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Tests/Test-iOS/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 | -------------------------------------------------------------------------------- /Tests/Test-iOS/alien.csv: -------------------------------------------------------------------------------- 1 | id;Name;Released;Directors/Writers;Budget in millions USD;Running time in minutes 2 | 1;Alien;1979;Ridley Scott;11;117 3 | 2;Aliens;1986;James Cameron;18.5;137 4 | 3;Alien 3;1992;David Fincher;50;114 5 | 4;Alien Resurrection;1997;Jean-Pierre Jeunet,Joss Whedon;75;109 6 | 5;Alien vs. Predator;2004;Paul W. S. Anderson;60;101 7 | 6;Aliens vs. Predator: Requiem;2007;Greg Strause,Colin Strause,Shane Salerno;40;94 8 | 7;Prometheus;2012;Ridley Scott,Jon Spaihts,Damon Lindelof;120.5;124 -------------------------------------------------------------------------------- /Tests/TrueFalseTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class TrueFalseTests: XCTestCase { 4 | 5 | func testTrue() { 6 | let spec = TrueSpecification() 7 | XCTAssertTrue(spec.isSatisfiedBy("hello world")) 8 | XCTAssertTrue(spec.isSatisfiedBy(nil)) 9 | } 10 | 11 | func testFalse() { 12 | let spec = FalseSpecification() 13 | XCTAssertFalse(spec.isSatisfiedBy("world hello")) 14 | XCTAssertFalse(spec.isSatisfiedBy(nil)) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /example0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neoneye/SpecificationPattern/38c6e32e182d162334aacb3312ac5b1d951b3d2e/example0.gif --------------------------------------------------------------------------------