├── .gitignore ├── .jazzy.yaml ├── .swift-version ├── .swiftlint.yml ├── Combinations.podspec ├── Combinations.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Combinations.xcscheme │ └── CombinationsDemo.xcscheme ├── Combinations.xcworkspace └── contents.xcworkspacedata ├── Combinations ├── Combinations.h ├── CombinationsGenerator.swift ├── CombinationsSpec.h ├── CombinationsSpec.m └── Info.plist ├── CombinationsDemo ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── icon-arrow-left.imageset │ │ ├── Contents.json │ │ ├── icon-arrow-left.png │ │ ├── icon-arrow-left@2x.png │ │ └── icon-arrow-left@3x.png │ └── icon-arrow-right.imageset │ │ ├── Contents.json │ │ ├── icon-arrow-right.png │ │ ├── icon-arrow-right@2x.png │ │ └── icon-arrow-right@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FormRow.swift ├── FormRowAccessoryView.swift ├── FormRowCell.swift ├── FormViewController.swift ├── Info.plist └── MainViewController.swift ├── CombinationsDemoUITests ├── CombinationsDemoUITests.swift ├── Info.plist └── Utils.swift ├── CombinationsTests ├── CombinationsTests.swift ├── CombinationsTestsWithKiwi.m └── Info.plist ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── assets ├── form.png ├── tests.png └── ui-tests-example.gif └── docs ├── Classes.html ├── Classes └── CombinationsGenerator.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── Combinations.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ └── CombinationsGenerator.html │ │ ├── badge.svg │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ ├── search.json │ │ └── undocumented.json │ │ └── docSet.dsidx └── Combinations.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js ├── search.json └── undocumented.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | 52 | Carthage/Checkouts 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | author: Alex Maimescu 2 | framework_root: . 3 | sdk: iphonesimulator 4 | copyright: Alex Maimescu 5 | github_url: https://github.com/alexmx/Combinations -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - trailing_newline 4 | - line_length 5 | - type_name 6 | 7 | excluded: 8 | - Carthage 9 | - Pods 10 | - Libs 11 | -------------------------------------------------------------------------------- /Combinations.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "Combinations" 4 | s.version = "1.0.1" 5 | s.summary = "Blazingly fast test generator suited for boundary and brute force testing." 6 | 7 | s.description = <<-DESC 8 | Combinations is an iOS testing utility framework suited for fast boundary testing. 9 | It gets a set of input test data values and transforms it in run-time tests for each generated combination of values. 10 | DESC 11 | 12 | 13 | s.homepage = "https://github.com/alexmx/Combinations" 14 | s.license = "MIT" 15 | s.authors = "Alex Maimescu" 16 | 17 | s.platform = :ios 18 | s.ios.deployment_target = '8.0' 19 | 20 | s.source = { :git => "https://github.com/alexmx/Combinations.git", :tag => "v#{s.version}" } 21 | s.source_files = "Combinations/**/*.{h,m,swift}" 22 | 23 | s.libraries = 'xml2', 'z' 24 | 25 | s.framework = "XCTest" 26 | s.requires_arc = true 27 | s.user_target_xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '$(PLATFORM_DIR)/Developer/Library/Frameworks' } 28 | s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' } 29 | 30 | end 31 | -------------------------------------------------------------------------------- /Combinations.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | CD96687E1EAA6BF6007A3827 /* Run SwiftLint */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = CD96687F1EAA6BF6007A3827 /* Build configuration list for PBXAggregateTarget "Run SwiftLint" */; 13 | buildPhases = ( 14 | CD9668821EAA6BFA007A3827 /* Run SwiftLint */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = "Run SwiftLint"; 19 | productName = "Run SwiftLint"; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 01641E5CA867271352901AAE /* libPods-CombinationsTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67717491FEF1282D449C7B02 /* libPods-CombinationsTests.a */; }; 25 | CD5DE6AE1D1C4434004FE7AF /* Combinations.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD665A6B1D101CCF00F33632 /* Combinations.framework */; }; 26 | CD665A6F1D101CCF00F33632 /* Combinations.h in Headers */ = {isa = PBXBuildFile; fileRef = CD665A6E1D101CCF00F33632 /* Combinations.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27 | CD665A761D101CCF00F33632 /* Combinations.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD665A6B1D101CCF00F33632 /* Combinations.framework */; }; 28 | CD665A7B1D101CCF00F33632 /* CombinationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD665A7A1D101CCF00F33632 /* CombinationsTests.swift */; }; 29 | CD665A861D101D8600F33632 /* CombinationsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD665A851D101D8600F33632 /* CombinationsGenerator.swift */; }; 30 | CD665A891D101E8500F33632 /* CombinationsSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = CD665A871D101E8500F33632 /* CombinationsSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | CD665A8A1D101E8500F33632 /* CombinationsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CD665A881D101E8500F33632 /* CombinationsSpec.m */; }; 32 | CDDA53901D18119F00C559B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA538F1D18119F00C559B8 /* AppDelegate.swift */; }; 33 | CDDA53921D18119F00C559B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53911D18119F00C559B8 /* MainViewController.swift */; }; 34 | CDDA53951D18119F00C559B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDDA53931D18119F00C559B8 /* Main.storyboard */; }; 35 | CDDA53971D18119F00C559B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CDDA53961D18119F00C559B8 /* Assets.xcassets */; }; 36 | CDDA539A1D18119F00C559B8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDDA53981D18119F00C559B8 /* LaunchScreen.storyboard */; }; 37 | CDDA53A51D18119F00C559B8 /* CombinationsDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53A41D18119F00C559B8 /* CombinationsDemoUITests.swift */; }; 38 | CDDA53B01D18146D00C559B8 /* FormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53AF1D18146D00C559B8 /* FormViewController.swift */; }; 39 | CDDA53B41D18175100C559B8 /* FormRowCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53B31D18175100C559B8 /* FormRowCell.swift */; }; 40 | CDDA53B61D18175800C559B8 /* FormRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53B51D18175800C559B8 /* FormRow.swift */; }; 41 | CDDA53B91D18192100C559B8 /* FormRowAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDA53B81D18192100C559B8 /* FormRowAccessoryView.swift */; }; 42 | CDE7DE1C1D1D83B20027AA43 /* CombinationsTestsWithKiwi.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE7DE1B1D1D83B20027AA43 /* CombinationsTestsWithKiwi.m */; }; 43 | CDEAEF781D1C4EA6001F91AA /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEAEF771D1C4EA6001F91AA /* Utils.swift */; }; 44 | /* End PBXBuildFile section */ 45 | 46 | /* Begin PBXContainerItemProxy section */ 47 | CD665A771D101CCF00F33632 /* PBXContainerItemProxy */ = { 48 | isa = PBXContainerItemProxy; 49 | containerPortal = CD665A621D101CCF00F33632 /* Project object */; 50 | proxyType = 1; 51 | remoteGlobalIDString = CD665A6A1D101CCF00F33632; 52 | remoteInfo = Combinations; 53 | }; 54 | CDDA53A11D18119F00C559B8 /* PBXContainerItemProxy */ = { 55 | isa = PBXContainerItemProxy; 56 | containerPortal = CD665A621D101CCF00F33632 /* Project object */; 57 | proxyType = 1; 58 | remoteGlobalIDString = CDDA538C1D18119F00C559B8; 59 | remoteInfo = CombinationsDemo; 60 | }; 61 | /* End PBXContainerItemProxy section */ 62 | 63 | /* Begin PBXCopyFilesBuildPhase section */ 64 | CD9C6FD41D21370600D207EF /* Copy Dependencies */ = { 65 | isa = PBXCopyFilesBuildPhase; 66 | buildActionMask = 2147483647; 67 | dstPath = ""; 68 | dstSubfolderSpec = 16; 69 | files = ( 70 | ); 71 | name = "Copy Dependencies"; 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXCopyFilesBuildPhase section */ 75 | 76 | /* Begin PBXFileReference section */ 77 | 5D9BA590FC3731F5695756F6 /* Pods-CombinationsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CombinationsTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CombinationsTests/Pods-CombinationsTests.release.xcconfig"; sourceTree = ""; }; 78 | 67717491FEF1282D449C7B02 /* libPods-CombinationsTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CombinationsTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | BB988082D5737D56D132F4CB /* libPods-Combinations.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Combinations.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | C05027C4FD21CD586CE9EA9A /* Pods-CombinationsTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CombinationsTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CombinationsTests/Pods-CombinationsTests.debug.xcconfig"; sourceTree = ""; }; 81 | CD665A6B1D101CCF00F33632 /* Combinations.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Combinations.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | CD665A6E1D101CCF00F33632 /* Combinations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Combinations.h; sourceTree = ""; }; 83 | CD665A701D101CCF00F33632 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84 | CD665A751D101CCF00F33632 /* CombinationsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CombinationsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | CD665A7A1D101CCF00F33632 /* CombinationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinationsTests.swift; sourceTree = ""; }; 86 | CD665A7C1D101CCF00F33632 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 87 | CD665A851D101D8600F33632 /* CombinationsGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinationsGenerator.swift; sourceTree = ""; }; 88 | CD665A871D101E8500F33632 /* CombinationsSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CombinationsSpec.h; sourceTree = ""; }; 89 | CD665A881D101E8500F33632 /* CombinationsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CombinationsSpec.m; sourceTree = ""; }; 90 | CDDA538D1D18119F00C559B8 /* CombinationsDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CombinationsDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | CDDA538F1D18119F00C559B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 92 | CDDA53911D18119F00C559B8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 93 | CDDA53941D18119F00C559B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 94 | CDDA53961D18119F00C559B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 95 | CDDA53991D18119F00C559B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 96 | CDDA539B1D18119F00C559B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 97 | CDDA53A01D18119F00C559B8 /* CombinationsDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CombinationsDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | CDDA53A41D18119F00C559B8 /* CombinationsDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinationsDemoUITests.swift; sourceTree = ""; }; 99 | CDDA53A61D18119F00C559B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | CDDA53AF1D18146D00C559B8 /* FormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormViewController.swift; sourceTree = ""; }; 101 | CDDA53B31D18175100C559B8 /* FormRowCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormRowCell.swift; sourceTree = ""; }; 102 | CDDA53B51D18175800C559B8 /* FormRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormRow.swift; sourceTree = ""; }; 103 | CDDA53B81D18192100C559B8 /* FormRowAccessoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormRowAccessoryView.swift; sourceTree = ""; }; 104 | CDE7DE1B1D1D83B20027AA43 /* CombinationsTestsWithKiwi.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CombinationsTestsWithKiwi.m; sourceTree = ""; }; 105 | CDEAEF771D1C4EA6001F91AA /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 106 | /* End PBXFileReference section */ 107 | 108 | /* Begin PBXFrameworksBuildPhase section */ 109 | CD665A671D101CCF00F33632 /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | CD665A721D101CCF00F33632 /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | CD665A761D101CCF00F33632 /* Combinations.framework in Frameworks */, 121 | 01641E5CA867271352901AAE /* libPods-CombinationsTests.a in Frameworks */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | CDDA538A1D18119F00C559B8 /* Frameworks */ = { 126 | isa = PBXFrameworksBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | CDDA539D1D18119F00C559B8 /* Frameworks */ = { 133 | isa = PBXFrameworksBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | CD5DE6AE1D1C4434004FE7AF /* Combinations.framework in Frameworks */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXFrameworksBuildPhase section */ 141 | 142 | /* Begin PBXGroup section */ 143 | 382FACB8886E020427809A21 /* Pods */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | C05027C4FD21CD586CE9EA9A /* Pods-CombinationsTests.debug.xcconfig */, 147 | 5D9BA590FC3731F5695756F6 /* Pods-CombinationsTests.release.xcconfig */, 148 | ); 149 | name = Pods; 150 | sourceTree = ""; 151 | }; 152 | 88ABCDB4F77B5C882A8790DB /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | BB988082D5737D56D132F4CB /* libPods-Combinations.a */, 156 | 67717491FEF1282D449C7B02 /* libPods-CombinationsTests.a */, 157 | ); 158 | name = Frameworks; 159 | sourceTree = ""; 160 | }; 161 | CD665A611D101CCF00F33632 = { 162 | isa = PBXGroup; 163 | children = ( 164 | CD665A6D1D101CCF00F33632 /* Combinations */, 165 | CDDA538E1D18119F00C559B8 /* CombinationsDemo */, 166 | CD665A791D101CCF00F33632 /* CombinationsTests */, 167 | CDDA53A31D18119F00C559B8 /* CombinationsDemoUITests */, 168 | CDBF87761D1D770B00EF9757 /* Supporting Files */, 169 | CD665A6C1D101CCF00F33632 /* Products */, 170 | 382FACB8886E020427809A21 /* Pods */, 171 | 88ABCDB4F77B5C882A8790DB /* Frameworks */, 172 | ); 173 | sourceTree = ""; 174 | }; 175 | CD665A6C1D101CCF00F33632 /* Products */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | CD665A6B1D101CCF00F33632 /* Combinations.framework */, 179 | CD665A751D101CCF00F33632 /* CombinationsTests.xctest */, 180 | CDDA538D1D18119F00C559B8 /* CombinationsDemo.app */, 181 | CDDA53A01D18119F00C559B8 /* CombinationsDemoUITests.xctest */, 182 | ); 183 | name = Products; 184 | sourceTree = ""; 185 | }; 186 | CD665A6D1D101CCF00F33632 /* Combinations */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | CD665A6E1D101CCF00F33632 /* Combinations.h */, 190 | CD665A871D101E8500F33632 /* CombinationsSpec.h */, 191 | CD665A881D101E8500F33632 /* CombinationsSpec.m */, 192 | CD665A851D101D8600F33632 /* CombinationsGenerator.swift */, 193 | CD665A701D101CCF00F33632 /* Info.plist */, 194 | ); 195 | path = Combinations; 196 | sourceTree = ""; 197 | }; 198 | CD665A791D101CCF00F33632 /* CombinationsTests */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | CD665A7A1D101CCF00F33632 /* CombinationsTests.swift */, 202 | CDE7DE1B1D1D83B20027AA43 /* CombinationsTestsWithKiwi.m */, 203 | CD665A7C1D101CCF00F33632 /* Info.plist */, 204 | ); 205 | path = CombinationsTests; 206 | sourceTree = ""; 207 | }; 208 | CDBF87761D1D770B00EF9757 /* Supporting Files */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | ); 212 | name = "Supporting Files"; 213 | sourceTree = ""; 214 | }; 215 | CDDA538E1D18119F00C559B8 /* CombinationsDemo */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | CDDA53BC1D1839D400C559B8 /* Models */, 219 | CDDA53BB1D1839CD00C559B8 /* Views */, 220 | CDDA53B11D18150100C559B8 /* Controllers */, 221 | CDDA538F1D18119F00C559B8 /* AppDelegate.swift */, 222 | CDDA53B21D18151400C559B8 /* Supporting Files */, 223 | ); 224 | path = CombinationsDemo; 225 | sourceTree = ""; 226 | }; 227 | CDDA53A31D18119F00C559B8 /* CombinationsDemoUITests */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | CDEAEF771D1C4EA6001F91AA /* Utils.swift */, 231 | CDDA53A41D18119F00C559B8 /* CombinationsDemoUITests.swift */, 232 | CDDA53A61D18119F00C559B8 /* Info.plist */, 233 | ); 234 | path = CombinationsDemoUITests; 235 | sourceTree = ""; 236 | }; 237 | CDDA53B11D18150100C559B8 /* Controllers */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | CDDA53AF1D18146D00C559B8 /* FormViewController.swift */, 241 | CDDA53911D18119F00C559B8 /* MainViewController.swift */, 242 | ); 243 | name = Controllers; 244 | sourceTree = ""; 245 | }; 246 | CDDA53B21D18151400C559B8 /* Supporting Files */ = { 247 | isa = PBXGroup; 248 | children = ( 249 | CDDA53931D18119F00C559B8 /* Main.storyboard */, 250 | CDDA53961D18119F00C559B8 /* Assets.xcassets */, 251 | CDDA53981D18119F00C559B8 /* LaunchScreen.storyboard */, 252 | CDDA539B1D18119F00C559B8 /* Info.plist */, 253 | ); 254 | name = "Supporting Files"; 255 | sourceTree = ""; 256 | }; 257 | CDDA53BB1D1839CD00C559B8 /* Views */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | CDDA53B31D18175100C559B8 /* FormRowCell.swift */, 261 | CDDA53B81D18192100C559B8 /* FormRowAccessoryView.swift */, 262 | ); 263 | name = Views; 264 | sourceTree = ""; 265 | }; 266 | CDDA53BC1D1839D400C559B8 /* Models */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | CDDA53B51D18175800C559B8 /* FormRow.swift */, 270 | ); 271 | name = Models; 272 | sourceTree = ""; 273 | }; 274 | /* End PBXGroup section */ 275 | 276 | /* Begin PBXHeadersBuildPhase section */ 277 | CD665A681D101CCF00F33632 /* Headers */ = { 278 | isa = PBXHeadersBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | CD665A6F1D101CCF00F33632 /* Combinations.h in Headers */, 282 | CD665A891D101E8500F33632 /* CombinationsSpec.h in Headers */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXHeadersBuildPhase section */ 287 | 288 | /* Begin PBXNativeTarget section */ 289 | CD665A6A1D101CCF00F33632 /* Combinations */ = { 290 | isa = PBXNativeTarget; 291 | buildConfigurationList = CD665A7F1D101CCF00F33632 /* Build configuration list for PBXNativeTarget "Combinations" */; 292 | buildPhases = ( 293 | CD665A661D101CCF00F33632 /* Sources */, 294 | CD665A671D101CCF00F33632 /* Frameworks */, 295 | CD665A681D101CCF00F33632 /* Headers */, 296 | CD665A691D101CCF00F33632 /* Resources */, 297 | ); 298 | buildRules = ( 299 | ); 300 | dependencies = ( 301 | ); 302 | name = Combinations; 303 | productName = Combinations; 304 | productReference = CD665A6B1D101CCF00F33632 /* Combinations.framework */; 305 | productType = "com.apple.product-type.framework"; 306 | }; 307 | CD665A741D101CCF00F33632 /* CombinationsTests */ = { 308 | isa = PBXNativeTarget; 309 | buildConfigurationList = CD665A821D101CCF00F33632 /* Build configuration list for PBXNativeTarget "CombinationsTests" */; 310 | buildPhases = ( 311 | 7CF9101B9D77468F931F55FD /* [CP] Check Pods Manifest.lock */, 312 | CD665A711D101CCF00F33632 /* Sources */, 313 | CD665A721D101CCF00F33632 /* Frameworks */, 314 | CD665A731D101CCF00F33632 /* Resources */, 315 | CD9C6FD41D21370600D207EF /* Copy Dependencies */, 316 | 36B94571ED0EC00536D7894B /* [CP] Embed Pods Frameworks */, 317 | AEE4F3EA01269829AA0161FD /* [CP] Copy Pods Resources */, 318 | ); 319 | buildRules = ( 320 | ); 321 | dependencies = ( 322 | CD665A781D101CCF00F33632 /* PBXTargetDependency */, 323 | ); 324 | name = CombinationsTests; 325 | productName = CombinationsTests; 326 | productReference = CD665A751D101CCF00F33632 /* CombinationsTests.xctest */; 327 | productType = "com.apple.product-type.bundle.unit-test"; 328 | }; 329 | CDDA538C1D18119F00C559B8 /* CombinationsDemo */ = { 330 | isa = PBXNativeTarget; 331 | buildConfigurationList = CDDA53AB1D18119F00C559B8 /* Build configuration list for PBXNativeTarget "CombinationsDemo" */; 332 | buildPhases = ( 333 | CDDA53891D18119F00C559B8 /* Sources */, 334 | CDDA538A1D18119F00C559B8 /* Frameworks */, 335 | CDDA538B1D18119F00C559B8 /* Resources */, 336 | ); 337 | buildRules = ( 338 | ); 339 | dependencies = ( 340 | ); 341 | name = CombinationsDemo; 342 | productName = CombinationsDemo; 343 | productReference = CDDA538D1D18119F00C559B8 /* CombinationsDemo.app */; 344 | productType = "com.apple.product-type.application"; 345 | }; 346 | CDDA539F1D18119F00C559B8 /* CombinationsDemoUITests */ = { 347 | isa = PBXNativeTarget; 348 | buildConfigurationList = CDDA53AC1D18119F00C559B8 /* Build configuration list for PBXNativeTarget "CombinationsDemoUITests" */; 349 | buildPhases = ( 350 | CDDA539C1D18119F00C559B8 /* Sources */, 351 | CDDA539D1D18119F00C559B8 /* Frameworks */, 352 | CDDA539E1D18119F00C559B8 /* Resources */, 353 | ); 354 | buildRules = ( 355 | ); 356 | dependencies = ( 357 | CDDA53A21D18119F00C559B8 /* PBXTargetDependency */, 358 | ); 359 | name = CombinationsDemoUITests; 360 | productName = CombinationsDemoUITests; 361 | productReference = CDDA53A01D18119F00C559B8 /* CombinationsDemoUITests.xctest */; 362 | productType = "com.apple.product-type.bundle.ui-testing"; 363 | }; 364 | /* End PBXNativeTarget section */ 365 | 366 | /* Begin PBXProject section */ 367 | CD665A621D101CCF00F33632 /* Project object */ = { 368 | isa = PBXProject; 369 | attributes = { 370 | LastSwiftUpdateCheck = 0730; 371 | LastUpgradeCheck = 0900; 372 | ORGANIZATIONNAME = "Alexandru Maimescu"; 373 | TargetAttributes = { 374 | CD665A6A1D101CCF00F33632 = { 375 | CreatedOnToolsVersion = 7.3.1; 376 | LastSwiftMigration = 0900; 377 | }; 378 | CD665A741D101CCF00F33632 = { 379 | CreatedOnToolsVersion = 7.3.1; 380 | LastSwiftMigration = 0900; 381 | }; 382 | CD96687E1EAA6BF6007A3827 = { 383 | CreatedOnToolsVersion = 8.3.1; 384 | ProvisioningStyle = Automatic; 385 | }; 386 | CDDA538C1D18119F00C559B8 = { 387 | CreatedOnToolsVersion = 7.3.1; 388 | LastSwiftMigration = 0900; 389 | }; 390 | CDDA539F1D18119F00C559B8 = { 391 | CreatedOnToolsVersion = 7.3.1; 392 | LastSwiftMigration = 0900; 393 | TestTargetID = CDDA538C1D18119F00C559B8; 394 | }; 395 | }; 396 | }; 397 | buildConfigurationList = CD665A651D101CCF00F33632 /* Build configuration list for PBXProject "Combinations" */; 398 | compatibilityVersion = "Xcode 3.2"; 399 | developmentRegion = English; 400 | hasScannedForEncodings = 0; 401 | knownRegions = ( 402 | en, 403 | Base, 404 | ); 405 | mainGroup = CD665A611D101CCF00F33632; 406 | productRefGroup = CD665A6C1D101CCF00F33632 /* Products */; 407 | projectDirPath = ""; 408 | projectRoot = ""; 409 | targets = ( 410 | CD665A6A1D101CCF00F33632 /* Combinations */, 411 | CD665A741D101CCF00F33632 /* CombinationsTests */, 412 | CDDA538C1D18119F00C559B8 /* CombinationsDemo */, 413 | CDDA539F1D18119F00C559B8 /* CombinationsDemoUITests */, 414 | CD96687E1EAA6BF6007A3827 /* Run SwiftLint */, 415 | ); 416 | }; 417 | /* End PBXProject section */ 418 | 419 | /* Begin PBXResourcesBuildPhase section */ 420 | CD665A691D101CCF00F33632 /* Resources */ = { 421 | isa = PBXResourcesBuildPhase; 422 | buildActionMask = 2147483647; 423 | files = ( 424 | ); 425 | runOnlyForDeploymentPostprocessing = 0; 426 | }; 427 | CD665A731D101CCF00F33632 /* Resources */ = { 428 | isa = PBXResourcesBuildPhase; 429 | buildActionMask = 2147483647; 430 | files = ( 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | CDDA538B1D18119F00C559B8 /* Resources */ = { 435 | isa = PBXResourcesBuildPhase; 436 | buildActionMask = 2147483647; 437 | files = ( 438 | CDDA539A1D18119F00C559B8 /* LaunchScreen.storyboard in Resources */, 439 | CDDA53971D18119F00C559B8 /* Assets.xcassets in Resources */, 440 | CDDA53951D18119F00C559B8 /* Main.storyboard in Resources */, 441 | ); 442 | runOnlyForDeploymentPostprocessing = 0; 443 | }; 444 | CDDA539E1D18119F00C559B8 /* Resources */ = { 445 | isa = PBXResourcesBuildPhase; 446 | buildActionMask = 2147483647; 447 | files = ( 448 | ); 449 | runOnlyForDeploymentPostprocessing = 0; 450 | }; 451 | /* End PBXResourcesBuildPhase section */ 452 | 453 | /* Begin PBXShellScriptBuildPhase section */ 454 | 36B94571ED0EC00536D7894B /* [CP] Embed Pods Frameworks */ = { 455 | isa = PBXShellScriptBuildPhase; 456 | buildActionMask = 2147483647; 457 | files = ( 458 | ); 459 | inputPaths = ( 460 | "${SRCROOT}/Pods/Target Support Files/Pods-CombinationsTests/Pods-CombinationsTests-frameworks.sh", 461 | "${BUILT_PRODUCTS_DIR}/Kiwi/Kiwi.framework", 462 | ); 463 | name = "[CP] Embed Pods Frameworks"; 464 | outputPaths = ( 465 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kiwi.framework", 466 | ); 467 | runOnlyForDeploymentPostprocessing = 0; 468 | shellPath = /bin/sh; 469 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CombinationsTests/Pods-CombinationsTests-frameworks.sh\"\n"; 470 | showEnvVarsInLog = 0; 471 | }; 472 | 7CF9101B9D77468F931F55FD /* [CP] Check Pods Manifest.lock */ = { 473 | isa = PBXShellScriptBuildPhase; 474 | buildActionMask = 2147483647; 475 | files = ( 476 | ); 477 | inputPaths = ( 478 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 479 | "${PODS_ROOT}/Manifest.lock", 480 | ); 481 | name = "[CP] Check Pods Manifest.lock"; 482 | outputPaths = ( 483 | "$(DERIVED_FILE_DIR)/Pods-CombinationsTests-checkManifestLockResult.txt", 484 | ); 485 | runOnlyForDeploymentPostprocessing = 0; 486 | shellPath = /bin/sh; 487 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 488 | showEnvVarsInLog = 0; 489 | }; 490 | AEE4F3EA01269829AA0161FD /* [CP] Copy Pods Resources */ = { 491 | isa = PBXShellScriptBuildPhase; 492 | buildActionMask = 2147483647; 493 | files = ( 494 | ); 495 | inputPaths = ( 496 | ); 497 | name = "[CP] Copy Pods Resources"; 498 | outputPaths = ( 499 | ); 500 | runOnlyForDeploymentPostprocessing = 0; 501 | shellPath = /bin/sh; 502 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CombinationsTests/Pods-CombinationsTests-resources.sh\"\n"; 503 | showEnvVarsInLog = 0; 504 | }; 505 | CD9668821EAA6BFA007A3827 /* Run SwiftLint */ = { 506 | isa = PBXShellScriptBuildPhase; 507 | buildActionMask = 2147483647; 508 | files = ( 509 | ); 510 | inputPaths = ( 511 | ); 512 | name = "Run SwiftLint"; 513 | outputPaths = ( 514 | ); 515 | runOnlyForDeploymentPostprocessing = 0; 516 | shellPath = /bin/sh; 517 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; 518 | }; 519 | /* End PBXShellScriptBuildPhase section */ 520 | 521 | /* Begin PBXSourcesBuildPhase section */ 522 | CD665A661D101CCF00F33632 /* Sources */ = { 523 | isa = PBXSourcesBuildPhase; 524 | buildActionMask = 2147483647; 525 | files = ( 526 | CD665A8A1D101E8500F33632 /* CombinationsSpec.m in Sources */, 527 | CD665A861D101D8600F33632 /* CombinationsGenerator.swift in Sources */, 528 | ); 529 | runOnlyForDeploymentPostprocessing = 0; 530 | }; 531 | CD665A711D101CCF00F33632 /* Sources */ = { 532 | isa = PBXSourcesBuildPhase; 533 | buildActionMask = 2147483647; 534 | files = ( 535 | CD665A7B1D101CCF00F33632 /* CombinationsTests.swift in Sources */, 536 | CDE7DE1C1D1D83B20027AA43 /* CombinationsTestsWithKiwi.m in Sources */, 537 | ); 538 | runOnlyForDeploymentPostprocessing = 0; 539 | }; 540 | CDDA53891D18119F00C559B8 /* Sources */ = { 541 | isa = PBXSourcesBuildPhase; 542 | buildActionMask = 2147483647; 543 | files = ( 544 | CDDA53B91D18192100C559B8 /* FormRowAccessoryView.swift in Sources */, 545 | CDDA53B41D18175100C559B8 /* FormRowCell.swift in Sources */, 546 | CDDA53B01D18146D00C559B8 /* FormViewController.swift in Sources */, 547 | CDDA53921D18119F00C559B8 /* MainViewController.swift in Sources */, 548 | CDDA53901D18119F00C559B8 /* AppDelegate.swift in Sources */, 549 | CDDA53B61D18175800C559B8 /* FormRow.swift in Sources */, 550 | ); 551 | runOnlyForDeploymentPostprocessing = 0; 552 | }; 553 | CDDA539C1D18119F00C559B8 /* Sources */ = { 554 | isa = PBXSourcesBuildPhase; 555 | buildActionMask = 2147483647; 556 | files = ( 557 | CDDA53A51D18119F00C559B8 /* CombinationsDemoUITests.swift in Sources */, 558 | CDEAEF781D1C4EA6001F91AA /* Utils.swift in Sources */, 559 | ); 560 | runOnlyForDeploymentPostprocessing = 0; 561 | }; 562 | /* End PBXSourcesBuildPhase section */ 563 | 564 | /* Begin PBXTargetDependency section */ 565 | CD665A781D101CCF00F33632 /* PBXTargetDependency */ = { 566 | isa = PBXTargetDependency; 567 | target = CD665A6A1D101CCF00F33632 /* Combinations */; 568 | targetProxy = CD665A771D101CCF00F33632 /* PBXContainerItemProxy */; 569 | }; 570 | CDDA53A21D18119F00C559B8 /* PBXTargetDependency */ = { 571 | isa = PBXTargetDependency; 572 | target = CDDA538C1D18119F00C559B8 /* CombinationsDemo */; 573 | targetProxy = CDDA53A11D18119F00C559B8 /* PBXContainerItemProxy */; 574 | }; 575 | /* End PBXTargetDependency section */ 576 | 577 | /* Begin PBXVariantGroup section */ 578 | CDDA53931D18119F00C559B8 /* Main.storyboard */ = { 579 | isa = PBXVariantGroup; 580 | children = ( 581 | CDDA53941D18119F00C559B8 /* Base */, 582 | ); 583 | name = Main.storyboard; 584 | sourceTree = ""; 585 | }; 586 | CDDA53981D18119F00C559B8 /* LaunchScreen.storyboard */ = { 587 | isa = PBXVariantGroup; 588 | children = ( 589 | CDDA53991D18119F00C559B8 /* Base */, 590 | ); 591 | name = LaunchScreen.storyboard; 592 | sourceTree = ""; 593 | }; 594 | /* End PBXVariantGroup section */ 595 | 596 | /* Begin XCBuildConfiguration section */ 597 | CD665A7D1D101CCF00F33632 /* Debug */ = { 598 | isa = XCBuildConfiguration; 599 | buildSettings = { 600 | ALWAYS_SEARCH_USER_PATHS = NO; 601 | CLANG_ANALYZER_NONNULL = YES; 602 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 603 | CLANG_CXX_LIBRARY = "libc++"; 604 | CLANG_ENABLE_MODULES = YES; 605 | CLANG_ENABLE_OBJC_ARC = YES; 606 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 607 | CLANG_WARN_BOOL_CONVERSION = YES; 608 | CLANG_WARN_COMMA = YES; 609 | CLANG_WARN_CONSTANT_CONVERSION = YES; 610 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 611 | CLANG_WARN_EMPTY_BODY = YES; 612 | CLANG_WARN_ENUM_CONVERSION = YES; 613 | CLANG_WARN_INFINITE_RECURSION = YES; 614 | CLANG_WARN_INT_CONVERSION = YES; 615 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 616 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 617 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 618 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 619 | CLANG_WARN_STRICT_PROTOTYPES = YES; 620 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 621 | CLANG_WARN_UNREACHABLE_CODE = YES; 622 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 623 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 624 | COPY_PHASE_STRIP = NO; 625 | CURRENT_PROJECT_VERSION = 1; 626 | DEBUG_INFORMATION_FORMAT = dwarf; 627 | ENABLE_STRICT_OBJC_MSGSEND = YES; 628 | ENABLE_TESTABILITY = YES; 629 | GCC_C_LANGUAGE_STANDARD = gnu99; 630 | GCC_DYNAMIC_NO_PIC = NO; 631 | GCC_NO_COMMON_BLOCKS = YES; 632 | GCC_OPTIMIZATION_LEVEL = 0; 633 | GCC_PREPROCESSOR_DEFINITIONS = ( 634 | "DEBUG=1", 635 | "$(inherited)", 636 | ); 637 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 638 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 639 | GCC_WARN_UNDECLARED_SELECTOR = YES; 640 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 641 | GCC_WARN_UNUSED_FUNCTION = YES; 642 | GCC_WARN_UNUSED_VARIABLE = YES; 643 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 644 | MTL_ENABLE_DEBUG_INFO = YES; 645 | ONLY_ACTIVE_ARCH = YES; 646 | SDKROOT = iphoneos; 647 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 648 | TARGETED_DEVICE_FAMILY = "1,2"; 649 | VERSIONING_SYSTEM = "apple-generic"; 650 | VERSION_INFO_PREFIX = ""; 651 | }; 652 | name = Debug; 653 | }; 654 | CD665A7E1D101CCF00F33632 /* Release */ = { 655 | isa = XCBuildConfiguration; 656 | buildSettings = { 657 | ALWAYS_SEARCH_USER_PATHS = NO; 658 | CLANG_ANALYZER_NONNULL = YES; 659 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 660 | CLANG_CXX_LIBRARY = "libc++"; 661 | CLANG_ENABLE_MODULES = YES; 662 | CLANG_ENABLE_OBJC_ARC = YES; 663 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 664 | CLANG_WARN_BOOL_CONVERSION = YES; 665 | CLANG_WARN_COMMA = YES; 666 | CLANG_WARN_CONSTANT_CONVERSION = YES; 667 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 668 | CLANG_WARN_EMPTY_BODY = YES; 669 | CLANG_WARN_ENUM_CONVERSION = YES; 670 | CLANG_WARN_INFINITE_RECURSION = YES; 671 | CLANG_WARN_INT_CONVERSION = YES; 672 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 673 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 674 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 675 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 676 | CLANG_WARN_STRICT_PROTOTYPES = YES; 677 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 678 | CLANG_WARN_UNREACHABLE_CODE = YES; 679 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 680 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 681 | COPY_PHASE_STRIP = NO; 682 | CURRENT_PROJECT_VERSION = 1; 683 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 684 | ENABLE_NS_ASSERTIONS = NO; 685 | ENABLE_STRICT_OBJC_MSGSEND = YES; 686 | GCC_C_LANGUAGE_STANDARD = gnu99; 687 | GCC_NO_COMMON_BLOCKS = YES; 688 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 689 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 690 | GCC_WARN_UNDECLARED_SELECTOR = YES; 691 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 692 | GCC_WARN_UNUSED_FUNCTION = YES; 693 | GCC_WARN_UNUSED_VARIABLE = YES; 694 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 695 | MTL_ENABLE_DEBUG_INFO = NO; 696 | SDKROOT = iphoneos; 697 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 698 | TARGETED_DEVICE_FAMILY = "1,2"; 699 | VALIDATE_PRODUCT = YES; 700 | VERSIONING_SYSTEM = "apple-generic"; 701 | VERSION_INFO_PREFIX = ""; 702 | }; 703 | name = Release; 704 | }; 705 | CD665A801D101CCF00F33632 /* Debug */ = { 706 | isa = XCBuildConfiguration; 707 | buildSettings = { 708 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 709 | CLANG_ENABLE_MODULES = YES; 710 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 711 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 712 | DEFINES_MODULE = YES; 713 | DYLIB_COMPATIBILITY_VERSION = 1; 714 | DYLIB_CURRENT_VERSION = 1; 715 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 716 | ENABLE_BITCODE = NO; 717 | FRAMEWORK_SEARCH_PATHS = ( 718 | "$(inherited)", 719 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 720 | ); 721 | INFOPLIST_FILE = Combinations/Info.plist; 722 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 723 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 724 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.Combinations; 725 | PRODUCT_NAME = "$(TARGET_NAME)"; 726 | SKIP_INSTALL = YES; 727 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 728 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 729 | SWIFT_VERSION = 4.0; 730 | }; 731 | name = Debug; 732 | }; 733 | CD665A811D101CCF00F33632 /* Release */ = { 734 | isa = XCBuildConfiguration; 735 | buildSettings = { 736 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 737 | CLANG_ENABLE_MODULES = YES; 738 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 739 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 740 | DEFINES_MODULE = YES; 741 | DYLIB_COMPATIBILITY_VERSION = 1; 742 | DYLIB_CURRENT_VERSION = 1; 743 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 744 | ENABLE_BITCODE = NO; 745 | FRAMEWORK_SEARCH_PATHS = ( 746 | "$(inherited)", 747 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 748 | ); 749 | INFOPLIST_FILE = Combinations/Info.plist; 750 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 751 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 752 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.Combinations; 753 | PRODUCT_NAME = "$(TARGET_NAME)"; 754 | SKIP_INSTALL = YES; 755 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 756 | SWIFT_VERSION = 4.0; 757 | }; 758 | name = Release; 759 | }; 760 | CD665A831D101CCF00F33632 /* Debug */ = { 761 | isa = XCBuildConfiguration; 762 | baseConfigurationReference = C05027C4FD21CD586CE9EA9A /* Pods-CombinationsTests.debug.xcconfig */; 763 | buildSettings = { 764 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 765 | CLANG_ENABLE_MODULES = YES; 766 | INFOPLIST_FILE = CombinationsTests/Info.plist; 767 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 768 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsTests; 769 | PRODUCT_NAME = "$(TARGET_NAME)"; 770 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 771 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 772 | SWIFT_VERSION = 4.0; 773 | }; 774 | name = Debug; 775 | }; 776 | CD665A841D101CCF00F33632 /* Release */ = { 777 | isa = XCBuildConfiguration; 778 | baseConfigurationReference = 5D9BA590FC3731F5695756F6 /* Pods-CombinationsTests.release.xcconfig */; 779 | buildSettings = { 780 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 781 | CLANG_ENABLE_MODULES = YES; 782 | INFOPLIST_FILE = CombinationsTests/Info.plist; 783 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 784 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsTests; 785 | PRODUCT_NAME = "$(TARGET_NAME)"; 786 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 787 | SWIFT_VERSION = 4.0; 788 | }; 789 | name = Release; 790 | }; 791 | CD9668801EAA6BF6007A3827 /* Debug */ = { 792 | isa = XCBuildConfiguration; 793 | buildSettings = { 794 | PRODUCT_NAME = "$(TARGET_NAME)"; 795 | }; 796 | name = Debug; 797 | }; 798 | CD9668811EAA6BF6007A3827 /* Release */ = { 799 | isa = XCBuildConfiguration; 800 | buildSettings = { 801 | PRODUCT_NAME = "$(TARGET_NAME)"; 802 | }; 803 | name = Release; 804 | }; 805 | CDDA53A71D18119F00C559B8 /* Debug */ = { 806 | isa = XCBuildConfiguration; 807 | buildSettings = { 808 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 809 | INFOPLIST_FILE = CombinationsDemo/Info.plist; 810 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 811 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 812 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsDemo; 813 | PRODUCT_NAME = "$(TARGET_NAME)"; 814 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 815 | SWIFT_VERSION = 4.0; 816 | }; 817 | name = Debug; 818 | }; 819 | CDDA53A81D18119F00C559B8 /* Release */ = { 820 | isa = XCBuildConfiguration; 821 | buildSettings = { 822 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 823 | INFOPLIST_FILE = CombinationsDemo/Info.plist; 824 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 825 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 826 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsDemo; 827 | PRODUCT_NAME = "$(TARGET_NAME)"; 828 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 829 | SWIFT_VERSION = 4.0; 830 | }; 831 | name = Release; 832 | }; 833 | CDDA53A91D18119F00C559B8 /* Debug */ = { 834 | isa = XCBuildConfiguration; 835 | buildSettings = { 836 | INFOPLIST_FILE = CombinationsDemoUITests/Info.plist; 837 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 838 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 839 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsDemoUITests; 840 | PRODUCT_NAME = "$(TARGET_NAME)"; 841 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 842 | SWIFT_VERSION = 4.0; 843 | TEST_TARGET_NAME = CombinationsDemo; 844 | }; 845 | name = Debug; 846 | }; 847 | CDDA53AA1D18119F00C559B8 /* Release */ = { 848 | isa = XCBuildConfiguration; 849 | buildSettings = { 850 | INFOPLIST_FILE = CombinationsDemoUITests/Info.plist; 851 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 852 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 853 | PRODUCT_BUNDLE_IDENTIFIER = com.alexmx.CombinationsDemoUITests; 854 | PRODUCT_NAME = "$(TARGET_NAME)"; 855 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 856 | SWIFT_VERSION = 4.0; 857 | TEST_TARGET_NAME = CombinationsDemo; 858 | }; 859 | name = Release; 860 | }; 861 | /* End XCBuildConfiguration section */ 862 | 863 | /* Begin XCConfigurationList section */ 864 | CD665A651D101CCF00F33632 /* Build configuration list for PBXProject "Combinations" */ = { 865 | isa = XCConfigurationList; 866 | buildConfigurations = ( 867 | CD665A7D1D101CCF00F33632 /* Debug */, 868 | CD665A7E1D101CCF00F33632 /* Release */, 869 | ); 870 | defaultConfigurationIsVisible = 0; 871 | defaultConfigurationName = Release; 872 | }; 873 | CD665A7F1D101CCF00F33632 /* Build configuration list for PBXNativeTarget "Combinations" */ = { 874 | isa = XCConfigurationList; 875 | buildConfigurations = ( 876 | CD665A801D101CCF00F33632 /* Debug */, 877 | CD665A811D101CCF00F33632 /* Release */, 878 | ); 879 | defaultConfigurationIsVisible = 0; 880 | defaultConfigurationName = Release; 881 | }; 882 | CD665A821D101CCF00F33632 /* Build configuration list for PBXNativeTarget "CombinationsTests" */ = { 883 | isa = XCConfigurationList; 884 | buildConfigurations = ( 885 | CD665A831D101CCF00F33632 /* Debug */, 886 | CD665A841D101CCF00F33632 /* Release */, 887 | ); 888 | defaultConfigurationIsVisible = 0; 889 | defaultConfigurationName = Release; 890 | }; 891 | CD96687F1EAA6BF6007A3827 /* Build configuration list for PBXAggregateTarget "Run SwiftLint" */ = { 892 | isa = XCConfigurationList; 893 | buildConfigurations = ( 894 | CD9668801EAA6BF6007A3827 /* Debug */, 895 | CD9668811EAA6BF6007A3827 /* Release */, 896 | ); 897 | defaultConfigurationIsVisible = 0; 898 | defaultConfigurationName = Release; 899 | }; 900 | CDDA53AB1D18119F00C559B8 /* Build configuration list for PBXNativeTarget "CombinationsDemo" */ = { 901 | isa = XCConfigurationList; 902 | buildConfigurations = ( 903 | CDDA53A71D18119F00C559B8 /* Debug */, 904 | CDDA53A81D18119F00C559B8 /* Release */, 905 | ); 906 | defaultConfigurationIsVisible = 0; 907 | defaultConfigurationName = Release; 908 | }; 909 | CDDA53AC1D18119F00C559B8 /* Build configuration list for PBXNativeTarget "CombinationsDemoUITests" */ = { 910 | isa = XCConfigurationList; 911 | buildConfigurations = ( 912 | CDDA53A91D18119F00C559B8 /* Debug */, 913 | CDDA53AA1D18119F00C559B8 /* Release */, 914 | ); 915 | defaultConfigurationIsVisible = 0; 916 | defaultConfigurationName = Release; 917 | }; 918 | /* End XCConfigurationList section */ 919 | }; 920 | rootObject = CD665A621D101CCF00F33632 /* Project object */; 921 | } 922 | -------------------------------------------------------------------------------- /Combinations.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Combinations.xcodeproj/xcshareddata/xcschemes/Combinations.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Combinations.xcodeproj/xcshareddata/xcschemes/CombinationsDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 87 | 93 | 94 | 95 | 96 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Combinations.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Combinations/Combinations.h: -------------------------------------------------------------------------------- 1 | // 2 | // Combinations.h 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/14/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Combinations. 12 | FOUNDATION_EXPORT double CombinationsVersionNumber; 13 | 14 | //! Project version string for Combinations. 15 | FOUNDATION_EXPORT const unsigned char CombinationsVersionString[]; 16 | 17 | #import -------------------------------------------------------------------------------- /Combinations/CombinationsGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsGenerator.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/14/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias Matrix = [[AnyObject]] 12 | 13 | @objcMembers 14 | public final class CombinationsGenerator: NSObject { 15 | 16 | func cartesianProduct(forMatrix matrix: Matrix) -> Matrix { 17 | 18 | let productSize = matrix.reduce(1) { $0 * $1.count } 19 | 20 | var product = Matrix() 21 | for i in 0.. Matrix { 42 | return cartesianProduct(forMatrix: values) 43 | } 44 | 45 | /** 46 | Generate combinations for provided input values. 47 | 48 | - parameter values: The input values used to generate combinations. 49 | - parameter assertCombination: The closure which will be invoked for every generated combination. 50 | */ 51 | public func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void) { 52 | combinations(forValues: values).forEach { (combination) in 53 | assertCombination(combination) 54 | } 55 | } 56 | 57 | /** 58 | Generate combinations for provided input values. 59 | 60 | - parameter values: The input values used to generate combinations. 61 | - parameter assertCombination: The closure which will be invoked for every generated combination. 62 | */ 63 | public static func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void) { 64 | CombinationsGenerator().combinations(forValues: values, assertCombination: assertCombination) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Combinations/CombinationsSpec.h: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsSpec.h 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/14/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * The base class for generated combinations tests. 13 | */ 14 | @interface CombinationsSpec : XCTestCase 15 | 16 | /** 17 | * Get input values which will be used to generate combinations. 18 | * 19 | * @discussion This method must be overriden. 20 | * 21 | * @return A matrix of input values. 22 | */ 23 | + (nullable NSArray *)valuesForCombinations; 24 | 25 | /** 26 | * Filter combinations for which we should skip the test generation. 27 | * 28 | * @param combination The combination to be checked. 29 | * 30 | * @return YES if you need to skip the given combination. Default implementation returns NO. 31 | */ 32 | + (BOOL)skipTestForCombination:(nonnull NSArray *)combination; 33 | 34 | /** 35 | * Assert the generated combination of values. 36 | * 37 | * @discussion This method will be called for each generated combination and a new test will be added in run-time. 38 | * This method must be overriden. 39 | * 40 | * @param combination The combination to be asserted. 41 | */ 42 | - (void)assertCombination:(nonnull NSArray *)combination; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Combinations/CombinationsSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsSpec.m 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/14/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | #import 12 | #import 13 | 14 | #import "CombinationsSpec.h" 15 | 16 | @interface CombinationsSpec () 17 | 18 | @end 19 | 20 | @implementation CombinationsSpec 21 | 22 | + (NSArray *)valuesForCombinations 23 | { 24 | [self raiseOverrideRequired]; // Must be overridden 25 | 26 | return nil; 27 | } 28 | 29 | + (instancetype)testCaseWithSelector:(SEL)selector 30 | { 31 | NSArray *invocations = [self testInvocations]; 32 | for (NSInvocation *invocation in invocations) { 33 | if (invocation.selector == selector) { 34 | return [super testCaseWithSelector:selector]; 35 | } 36 | } 37 | 38 | return nil; 39 | } 40 | 41 | + (NSArray *)testInvocations 42 | { 43 | if (self == [CombinationsSpec self]) return nil; 44 | 45 | NSArray *inputValues = [self valuesForCombinations]; 46 | NSArray *combinations = [[CombinationsGenerator new] combinationsForValues:inputValues]; 47 | 48 | NSMutableArray *invocations = [NSMutableArray arrayWithCapacity:combinations.count]; 49 | 50 | for (int i = 0; i < combinations.count; i++) { 51 | if (![self skipTestForCombination:combinations[i]]) { 52 | NSString *combinationIdentifier = [NSString stringWithFormat:@"%d", i]; 53 | [invocations addObject:[self invocationWithIdentifier:combinationIdentifier combination:combinations[i]]]; 54 | } 55 | } 56 | 57 | return invocations; 58 | } 59 | 60 | + (NSInvocation *)invocationWithIdentifier:(NSString *)identifier combination:(NSArray *)combination 61 | { 62 | SEL selector = [self addInstanceMethodForIdentifier:identifier combination:combination]; 63 | 64 | NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector]; 65 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 66 | invocation.selector = selector; 67 | 68 | return invocation; 69 | } 70 | 71 | + (SEL)addInstanceMethodForIdentifier:(NSString *)identifier combination:(NSArray *)combination 72 | { 73 | NSString *selectorName = [NSString stringWithFormat:@"%@.testComb%@", NSStringFromClass(self), identifier]; 74 | SEL selector = NSSelectorFromString(selectorName); 75 | 76 | IMP implementation = imp_implementationWithBlock(^(CombinationsSpec *self) { 77 | [self assertCombination:combination]; 78 | }); 79 | 80 | NSString *typeString = [NSString stringWithFormat:@"%s%s%s", @encode(id), @encode(id), @encode(SEL)]; 81 | class_addMethod(self, selector, implementation, typeString.UTF8String); 82 | 83 | return selector; 84 | } 85 | 86 | + (void)raiseOverrideRequired 87 | { 88 | [NSException raise:@"Fatal Error" 89 | format:@"This method must be overridden in subclass!"]; 90 | } 91 | 92 | - (void)raiseOverrideRequired 93 | { 94 | [self.class raiseOverrideRequired]; 95 | } 96 | 97 | + (BOOL)skipTestForCombination:(NSArray *)combination 98 | { 99 | return NO; 100 | } 101 | 102 | - (void)assertCombination:(NSArray *)combination 103 | { 104 | [self raiseOverrideRequired]; // Must be overridden 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /Combinations/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CombinationsDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CombinationsDemo 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. 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: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | 18 | UINavigationBar.appearance().barStyle = .black 19 | UINavigationBar.appearance().tintColor = .white 20 | UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white] 21 | UINavigationBar.appearance().barTintColor = UIColor(red: 1, green: 45/255.0, blue: 85/255.0, alpha: 1) 22 | 23 | return true 24 | } 25 | 26 | func applicationWillResignActive(_ application: UIApplication) { 27 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 28 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 29 | } 30 | 31 | func applicationDidEnterBackground(_ application: UIApplication) { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | func applicationWillEnterForeground(_ application: UIApplication) { 37 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | func applicationDidBecomeActive(_ application: UIApplication) { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | func applicationWillTerminate(_ application: UIApplication) { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /CombinationsDemo/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon-arrow-left.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon-arrow-left@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon-arrow-left@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left.png -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left@2x.png -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-left.imageset/icon-arrow-left@3x.png -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon-arrow-right.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon-arrow-right@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon-arrow-right@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right.png -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right@2x.png -------------------------------------------------------------------------------- /CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/CombinationsDemo/Assets.xcassets/icon-arrow-right.imageset/icon-arrow-right@3x.png -------------------------------------------------------------------------------- /CombinationsDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CombinationsDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /CombinationsDemo/FormRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormRow.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Validatable { 12 | 13 | func validate() throws 14 | } 15 | 16 | enum FormRowValidationError: Error { 17 | 18 | case mandatoryField(fieldName: String) 19 | case incorrectFormat(fieldName: String) 20 | } 21 | 22 | enum FormRowValueType: UInt { 23 | 24 | case genericAlphanumeric 25 | case genericNumeric 26 | case email 27 | case passcode 28 | case multipleChoice 29 | } 30 | 31 | class FormRow { 32 | 33 | var rowType: FormRowValueType = .genericAlphanumeric 34 | 35 | var title: String? 36 | var placeholder: String? 37 | var value: String? 38 | 39 | var maximumLength: UInt = 0 40 | 41 | var values: [String]? { 42 | 43 | didSet { 44 | value = values?[selectedValueIndex] 45 | } 46 | } 47 | 48 | var selectedValueIndex: Int = 0 { 49 | 50 | didSet { 51 | value = values?[selectedValueIndex] 52 | } 53 | } 54 | 55 | var editable: Bool = true 56 | var optional: Bool = false 57 | 58 | var tag: UInt? 59 | 60 | convenience init(title: String?, value: String?) { 61 | 62 | self.init() 63 | 64 | self.title = title 65 | self.value = value 66 | } 67 | 68 | convenience init(title: String?, values: [String]) { 69 | 70 | self.init(title: title, value: nil) 71 | 72 | self.rowType = .multipleChoice 73 | self.values = values 74 | self.value = values[selectedValueIndex] 75 | } 76 | } 77 | 78 | extension FormRow: Validatable { 79 | 80 | func validate() throws { 81 | 82 | guard optional == false else { 83 | return 84 | } 85 | 86 | guard let value = value, !value.isEmpty else { 87 | throw FormRowValidationError.mandatoryField(fieldName: title ?? "unknown") 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CombinationsDemo/FormRowAccessoryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormRowAccessoryView.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol FormRowAccessoryViewDelegate: UIToolbarDelegate { 12 | 13 | func formRowAccessoryViewDidPressPreviousButton(_ accessoryView: FormRowAccessoryView) 14 | func formRowAccessoryViewDidPressNextButton(_ accessoryView: FormRowAccessoryView) 15 | func formRowAccessoryViewDidPressDoneButton(_ accessoryView: FormRowAccessoryView) 16 | } 17 | 18 | class FormRowAccessoryView: UIToolbar { 19 | 20 | weak var inputDelegate: FormRowAccessoryViewDelegate? 21 | 22 | override init(frame: CGRect) { 23 | super.init(frame: frame) 24 | 25 | setup() 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | super.init(coder: aDecoder) 30 | 31 | setup() 32 | } 33 | 34 | fileprivate func setup() { 35 | 36 | barStyle = .default 37 | isTranslucent = true 38 | 39 | let previousItem = UIBarButtonItem(image: UIImage(named: "icon-arrow-left"), style: .plain, target: self, action: #selector(FormRowAccessoryView.previousDidPress(_:))) 40 | let nextItem = UIBarButtonItem(image: UIImage(named: "icon-arrow-right"), style: .plain, target: self, action: #selector(FormRowAccessoryView.nextDidPress(_:))) 41 | let doneItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(FormRowAccessoryView.doneDidPress(_:))) 42 | let flexibleItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 43 | 44 | items = [previousItem, nextItem, flexibleItem, doneItem] 45 | tintColor = UIColor.darkGray 46 | } 47 | 48 | @objc func previousDidPress(_ item: UIBarButtonItem) { 49 | inputDelegate?.formRowAccessoryViewDidPressPreviousButton(self) 50 | } 51 | 52 | @objc func nextDidPress(_ item: UIBarButtonItem) { 53 | inputDelegate?.formRowAccessoryViewDidPressNextButton(self) 54 | } 55 | 56 | @objc func doneDidPress(_ item: UIBarButtonItem) { 57 | inputDelegate?.formRowAccessoryViewDidPressDoneButton(self) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CombinationsDemo/FormRowCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormRowCell.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol FormRowCellInputAccessoryDelegate: class { 12 | 13 | func formRowCellDidPressPreviousButton(_ cell: FormRowCell) 14 | func formRowCellDidPressNextButton(_ cell: FormRowCell) 15 | func formRowCellDidPressDoneButton(_ cell: FormRowCell) 16 | } 17 | 18 | class FormRowCell: UITableViewCell { 19 | 20 | weak var inputAccessoryDelegate: FormRowCellInputAccessoryDelegate? 21 | 22 | @IBOutlet var titleLabel: UILabel? 23 | @IBOutlet var textField: UITextField? 24 | 25 | var formRow: FormRow? { 26 | 27 | didSet { 28 | 29 | guard let formRow = formRow else { 30 | return 31 | } 32 | 33 | titleLabel?.text = formRow.title 34 | 35 | if let textField = textField { 36 | 37 | textField.text = formRow.value 38 | textField.placeholder = formRow.placeholder 39 | textField.inputView = nil 40 | 41 | switch formRow.rowType { 42 | case .passcode: 43 | textField.isSecureTextEntry = true 44 | case .multipleChoice: 45 | textField.inputView = optionsView 46 | textField.text = formRow.values?[defaultMultipleChoiceIndex] 47 | case .email: 48 | textField.autocapitalizationType = .none 49 | textField.keyboardType = .emailAddress 50 | default: 51 | textField.keyboardType = .default 52 | } 53 | } 54 | } 55 | } 56 | 57 | var editable: Bool = true { 58 | 59 | didSet { 60 | textField?.isEnabled = editable 61 | } 62 | } 63 | 64 | fileprivate var optionsView: UIPickerView { 65 | 66 | let optionsView = UIPickerView() 67 | optionsView.delegate = self 68 | optionsView.dataSource = self 69 | optionsView.selectRow(defaultMultipleChoiceIndex, inComponent: 0, animated: false) 70 | 71 | return optionsView 72 | } 73 | 74 | fileprivate var defaultMultipleChoiceIndex: Int { 75 | 76 | guard let formRow = formRow else { 77 | return 0 78 | } 79 | 80 | var selectedRow = 0 81 | if ((formRow.values?.count)! > formRow.selectedValueIndex) && (formRow.selectedValueIndex > 0) { 82 | selectedRow = formRow.selectedValueIndex 83 | } 84 | 85 | return selectedRow 86 | } 87 | 88 | override func awakeFromNib() { 89 | super.awakeFromNib() 90 | 91 | let screenFrame = UIScreen.main.bounds 92 | let accessoryViewFrame = CGRect(x: 0, y: 0, width: screenFrame.size.width, height: 44.0) 93 | let accessoryView = FormRowAccessoryView(frame: accessoryViewFrame) 94 | accessoryView.inputDelegate = self 95 | textField?.inputAccessoryView = accessoryView 96 | 97 | selectionStyle = .none 98 | 99 | textField?.addTarget(self, action: #selector(FormRowCell.textFieldDidChange(_:)), for: .editingChanged) 100 | 101 | if textField?.delegate == nil { 102 | textField?.delegate = self 103 | } 104 | } 105 | 106 | override func becomeFirstResponder() -> Bool { 107 | 108 | guard editable else { 109 | return false 110 | } 111 | 112 | return textField?.becomeFirstResponder() ?? false 113 | } 114 | 115 | override func resignFirstResponder() -> Bool { 116 | endEditing(true) 117 | return textField?.resignFirstResponder() ?? false 118 | } 119 | 120 | @objc func textFieldDidChange(_ textField: UITextField) { 121 | formRow?.value = textField.text 122 | } 123 | } 124 | 125 | extension FormRowCell: UIPickerViewDelegate, UIPickerViewDataSource { 126 | 127 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 128 | return formRow?.values?[row] 129 | } 130 | 131 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 132 | textField?.text = formRow?.values![row] 133 | formRow?.selectedValueIndex = row 134 | } 135 | 136 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 137 | return 1 138 | } 139 | 140 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 141 | return formRow?.values?.count ?? 0 142 | } 143 | } 144 | 145 | extension FormRowCell: UITextFieldDelegate { 146 | 147 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 148 | return true 149 | } 150 | 151 | func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { 152 | return true 153 | } 154 | 155 | func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { 156 | return true 157 | } 158 | 159 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 160 | formRowAccessoryViewDidPressNextButton((textField.inputAccessoryView as? FormRowAccessoryView)!) 161 | return false 162 | } 163 | } 164 | 165 | extension FormRowCell: FormRowAccessoryViewDelegate { 166 | 167 | func formRowAccessoryViewDidPressPreviousButton(_ accessoryView: FormRowAccessoryView) { 168 | inputAccessoryDelegate?.formRowCellDidPressPreviousButton(self) 169 | } 170 | 171 | func formRowAccessoryViewDidPressNextButton(_ accessoryView: FormRowAccessoryView) { 172 | inputAccessoryDelegate?.formRowCellDidPressNextButton(self) 173 | } 174 | 175 | func formRowAccessoryViewDidPressDoneButton(_ accessoryView: FormRowAccessoryView) { 176 | inputAccessoryDelegate?.formRowCellDidPressDoneButton(self) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /CombinationsDemo/FormViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormViewController.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Form editing view controller data source 12 | protocol FormViewControllerDataSource: class { 13 | 14 | func formViewController(_ controller: FormViewController, formRowForIndexPath indexPath: IndexPath) -> FormRow 15 | 16 | func formViewController(_ controller: FormViewController, classForFormRowCellAtIndexPath indexPath: IndexPath) -> AnyClass? 17 | } 18 | 19 | /// Form editing view controller 20 | class FormViewController: UITableViewController, FormViewControllerDataSource { 21 | 22 | weak var dataSource: FormViewControllerDataSource? 23 | 24 | /// Auto enable editing for first row on appear 25 | var autoEnableEditing: Bool = false 26 | 27 | // MARK: VC Lifecycle 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | // Set dynamic rows heigh (cell baces sizing) 33 | tableView.estimatedRowHeight = 50 34 | tableView.rowHeight = UITableViewAutomaticDimension 35 | } 36 | 37 | override func viewDidAppear(_ animated: Bool) { 38 | super.viewDidAppear(animated) 39 | 40 | if autoEnableEditing == true { 41 | // Enable editing for first row in form 42 | tableView.cellForRow(at: IndexPath(row: 0, section: 0))?.becomeFirstResponder() 43 | } 44 | } 45 | 46 | override func viewWillDisappear(_ animated: Bool) { 47 | tableView.endEditing(true) 48 | 49 | super.viewWillDisappear(animated) 50 | } 51 | 52 | // MARK: UITableViewDataSource 53 | 54 | override func numberOfSections(in tableView: UITableView) -> Int { 55 | return 1 56 | } 57 | 58 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 59 | return 0 60 | } 61 | 62 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 63 | 64 | let cellClass: AnyClass? = dataSource?.formViewController(self, classForFormRowCellAtIndexPath: indexPath) 65 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: cellClass!)) 66 | 67 | let sectionsCount = tableView.numberOfSections 68 | let rowsCountInSection = tableView.numberOfRows(inSection: (indexPath as NSIndexPath).section) 69 | 70 | if cell!.isKind(of: FormRowCell.self) { 71 | let formRowCell = cell as? FormRowCell 72 | 73 | if ((indexPath as NSIndexPath).section == sectionsCount - 1) && 74 | ((indexPath as NSIndexPath).row == rowsCountInSection - 1) { 75 | formRowCell?.textField?.returnKeyType = .default 76 | } else { 77 | formRowCell?.textField?.returnKeyType = .next 78 | } 79 | 80 | let formRow = dataSource?.formViewController(self, formRowForIndexPath: indexPath) 81 | formRowCell?.formRow = formRow 82 | formRowCell?.inputAccessoryDelegate = self 83 | } 84 | 85 | return cell! 86 | } 87 | 88 | // MARK: UITableViewDelegate 89 | 90 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 91 | 92 | if let cell = tableView.cellForRow(at: indexPath) { 93 | if cell.isKind(of: FormRowCell.self) { 94 | cell.becomeFirstResponder() 95 | } 96 | } 97 | } 98 | 99 | // MARK: Rows Navigation 100 | 101 | fileprivate func nextIndexPath(_ indexPath: IndexPath) -> IndexPath? { 102 | 103 | let numberOfSections = tableView.numberOfSections 104 | let numberOfRowsInSection = tableView.numberOfRows(inSection: (indexPath as NSIndexPath).section) 105 | 106 | if ((indexPath as NSIndexPath).section < numberOfSections - 1) || ((indexPath as NSIndexPath).row < numberOfRowsInSection - 1) { 107 | 108 | if (indexPath as NSIndexPath).row + 1 == numberOfRowsInSection { 109 | return IndexPath(row: 0, section: (indexPath as NSIndexPath).section + 1) 110 | } else { 111 | return IndexPath(row: (indexPath as NSIndexPath).row + 1, section: (indexPath as NSIndexPath).section) 112 | } 113 | } 114 | 115 | return nil 116 | } 117 | 118 | fileprivate func previousIndexPath(_ indexPath: IndexPath) -> IndexPath? { 119 | 120 | if !((indexPath as NSIndexPath).row == 0 && (indexPath as NSIndexPath).section == 0) { 121 | 122 | if (indexPath as NSIndexPath).row > 0 { 123 | return IndexPath(row: (indexPath as NSIndexPath).row - 1, section: (indexPath as NSIndexPath).section) 124 | } else { 125 | let previousSection = (indexPath as NSIndexPath).section - 1 126 | let numberOfRowsInPreviousSection = tableView.numberOfRows(inSection: previousSection) 127 | return IndexPath(row: numberOfRowsInPreviousSection - 1, section: previousSection) 128 | } 129 | } 130 | 131 | return nil 132 | } 133 | 134 | // MARK: FormViewControllerDataSource 135 | 136 | func formViewController(_ controller: FormViewController, classForFormRowCellAtIndexPath indexPath: IndexPath) -> AnyClass? { 137 | return FormRowCell.self 138 | } 139 | 140 | func formViewController(_ controller: FormViewController, formRowForIndexPath indexPath: IndexPath) -> FormRow { 141 | return FormRow() // Provide empty row 142 | } 143 | } 144 | 145 | extension FormViewController: FormRowCellInputAccessoryDelegate { 146 | 147 | // MARK: FormRowCellInputAccessoryDelegate 148 | 149 | final func formRowCellDidPressPreviousButton(_ cell: FormRowCell) { 150 | 151 | let indexPath = tableView.indexPath(for: cell) 152 | let previousIndexPath = self.previousIndexPath(indexPath!) 153 | 154 | if let previousIndexPath = previousIndexPath { 155 | let previousCell = tableView.cellForRow(at: previousIndexPath) as? FormRowCell 156 | if previousCell?.becomeFirstResponder() == false { 157 | _ = cell.resignFirstResponder() 158 | } 159 | } 160 | } 161 | 162 | final func formRowCellDidPressNextButton(_ cell: FormRowCell) { 163 | 164 | let indexPath = tableView.indexPath(for: cell) 165 | let nextIndexPath = self.nextIndexPath(indexPath!) 166 | 167 | if let nextIndexPath = nextIndexPath { 168 | let nextCell = tableView.cellForRow(at: nextIndexPath) as? FormRowCell 169 | if nextCell?.becomeFirstResponder() == false { 170 | _ = cell.resignFirstResponder() 171 | } 172 | } else { 173 | _ = cell.resignFirstResponder() 174 | } 175 | } 176 | 177 | final func formRowCellDidPressDoneButton(_ cell: FormRowCell) { 178 | _ = cell.resignFirstResponder() 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /CombinationsDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CombinationsDemo/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // CombinationsDemo 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MainViewController: FormViewController { 12 | 13 | var rows: [FormRow]? 14 | 15 | var firstNameRow: FormRow!, 16 | emailRow: FormRow!, 17 | passwordRow: FormRow!, 18 | genderRow: FormRow! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | dataSource = self 24 | 25 | tableView.tableFooterView = UIView() 26 | 27 | loadData() 28 | } 29 | 30 | func loadData() { 31 | 32 | firstNameRow = FormRow(title: "First Name", value: nil) 33 | emailRow = FormRow(title: "Email", value: nil) 34 | emailRow.rowType = .email 35 | passwordRow = FormRow(title: "Password", value: nil) 36 | passwordRow.rowType = .passcode 37 | genderRow = FormRow(title: "Gender", values: ["Male", "Female"]) 38 | 39 | rows = [firstNameRow, emailRow, passwordRow, genderRow] 40 | } 41 | 42 | override var preferredStatusBarStyle: UIStatusBarStyle { 43 | return .lightContent 44 | } 45 | 46 | // MARK: FormViewControllerDataSource 47 | 48 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 | return rows?.count ?? 0 50 | } 51 | 52 | override func formViewController(_ controller: FormViewController, formRowForIndexPath indexPath: IndexPath) -> FormRow { 53 | return rows![(indexPath as NSIndexPath).row] 54 | } 55 | 56 | // MARK: Actions 57 | 58 | @IBAction func didPressValidate(_ sender: UIBarButtonItem) { 59 | 60 | print("Validate") 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /CombinationsDemoUITests/CombinationsDemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsDemoUITests.swift 3 | // CombinationsDemoUITests 4 | // 5 | // Created by Alexandru Maimescu on 6/20/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import Combinations 10 | 11 | class CombinationsDemoUITests: CombinationsSpec { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | XCUIApplication().launch() 17 | } 18 | 19 | override class func valuesForCombinations() -> [[Any]]? { 20 | 21 | return [ 22 | ["John Smith", ""], 23 | ["john.smith@example.com", "john.smith@", ""], 24 | ["12345", ""], 25 | ["Male", "Female"] 26 | ] 27 | } 28 | 29 | override func assertCombination(_ combination: [Any]) { 30 | 31 | if let combination = combination as? [String] { 32 | 33 | let firstName = combination[0] 34 | let email = combination[1] 35 | let password = combination[2] 36 | let gender = combination[3] 37 | 38 | let app = XCUIApplication() 39 | let tablesQuery = app.tables 40 | let firstNameCellsQuery = tablesQuery.cells.containing(.staticText, identifier:"First Name") 41 | tapElementAndWaitForKeyboardToAppear(firstNameCellsQuery.children(matching: .textField).element) 42 | firstNameCellsQuery.children(matching: .textField).element.typeText(firstName) 43 | let toolbarsQuery = app.toolbars 44 | let iconArrowRightButton = toolbarsQuery.buttons["icon arrow right"] 45 | iconArrowRightButton.tap() 46 | tablesQuery.cells.containing(.staticText, identifier:"Email").children(matching: .textField).element.typeText(email) 47 | iconArrowRightButton.tap() 48 | tablesQuery.cells.containing(.staticText, identifier:"Password").children(matching: .secureTextField).element.typeText(password) 49 | iconArrowRightButton.tap() 50 | app.pickerWheels.element.adjust(toPickerWheelValue: gender) 51 | app.navigationBars["Demo"].buttons["Submit"].tap() 52 | 53 | // Perform required asserts 54 | 55 | } else { 56 | XCTFail("Something went wrong") 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CombinationsDemoUITests/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 | -------------------------------------------------------------------------------- /CombinationsDemoUITests/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/23/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | extension XCTestCase { 13 | 14 | func tapElementAndWaitForKeyboardToAppear(_ element: XCUIElement) { 15 | let keyboard = XCUIApplication().keyboards.element 16 | while true { 17 | element.tap() 18 | if keyboard.exists { 19 | break 20 | } 21 | RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.5)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CombinationsTests/CombinationsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsTests.swift 3 | // CombinationsTests 4 | // 5 | // Created by Alexandru Maimescu on 6/14/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Combinations 11 | 12 | class CombinationTests1: CombinationsSpec { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | 18 | override class func valuesForCombinations() -> [[Any]]? { 19 | return [ 20 | [1, 2, 3, 4], 21 | ["1", "2", "3"], 22 | [1, 2, 3, 4], 23 | ["1", "2", "3"] 24 | ] 25 | } 26 | 27 | override func assertCombination(_ combination: [Any]) { 28 | 29 | print(combination) 30 | 31 | // Perform required asserts on combination 32 | 33 | XCTAssertTrue(true) 34 | } 35 | } 36 | 37 | class CombinationTests2: CombinationsSpec { 38 | 39 | override class func valuesForCombinations() -> [[Any]]? { 40 | 41 | return [ 42 | [1, 2], 43 | ["hello", "world"] 44 | ] 45 | } 46 | 47 | override func assertCombination(_ combination: [Any]) { 48 | 49 | if let number = combination[0] as? Int, 50 | let string = combination[1] as? String { 51 | 52 | print("\(number) \(string)") 53 | } 54 | 55 | // Perform required asserts on combination 56 | 57 | XCTAssertTrue(true) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CombinationsTests/CombinationsTestsWithKiwi.m: -------------------------------------------------------------------------------- 1 | // 2 | // CombinationsTestsWithKiwi.m 3 | // Combinations 4 | // 5 | // Created by Alexandru Maimescu on 6/24/16. 6 | // Copyright © 2016 Alexandru Maimescu. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import Combinations; 13 | 14 | SPEC_BEGIN(CombinationsAdditiveTests) 15 | 16 | describe(@"CombinationsAdditiveTests", ^{ 17 | 18 | context(@"a state the component is in", ^{ 19 | 20 | NSArray *inputValues = @[@[@1, @2], @[@"1", @"2"]]; 21 | 22 | [CombinationsGenerator combinationsForValues:inputValues assertCombination:^(NSArray * _Nonnull combination) { 23 | 24 | it(@"should do something", ^{ 25 | // Perform required asserts on combination 26 | }); 27 | }]; 28 | }); 29 | }); 30 | 31 | SPEC_END 32 | -------------------------------------------------------------------------------- /CombinationsTests/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alex Maimescu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | inhibit_all_warnings! 2 | 3 | target 'CombinationsTests' do 4 | inherit! :search_paths 5 | 6 | pod "Kiwi", "~>2.4" 7 | end 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kiwi (2.4.0) 3 | 4 | DEPENDENCIES: 5 | - Kiwi (~> 2.4) 6 | 7 | SPEC CHECKSUMS: 8 | Kiwi: f49c9d54b28917df5928fe44968a39ed198cb8a8 9 | 10 | PODFILE CHECKSUM: 3223c02de481102171c7a53bd51a9f42fbb7f11c 11 | 12 | COCOAPODS: 1.3.1 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Combinations 2 | 3 | [![Twitter: @amaimescu](https://img.shields.io/badge/contact-%40amaimescu-blue.svg)](https://twitter.com/amaimescu) 4 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/alexmx/ios-ui-automation-overview/blob/master/LICENSE) 5 | 6 | **Combinations** is an iOS testing utility framework suited for fast **boundary testing**. 7 | 8 | > **Boundary testing** or **boundary value analysis**, is where test cases are generated using the extremes of the input domain, e.g. maximum, minimum, just inside/outside boundaries, typical values, and error values. It is similar to Equivalence Partitioning but focuses on "corner cases". 9 | 10 | The framework does just two simple things: 11 | 12 | 1. Generate a set of combinations from test data input set; 13 | 2. Generate a run-time unit or UI test for each of the combinations; 14 | 15 | Check out the [API reference](http://alexmx.github.io/Combinations/) for more information. 16 | 17 | #### Simple example: 18 | We have an input form with four fields: `First Name`, `Email`, `Password` and `Gender`. 19 | 20 | 21 | 22 | We also have our testing boundary values defined for each field: 23 | * First Name: `John Smith`, `empty string` 24 | * Email: `john.smith@example.com`, `john.smith@`, `empty string` 25 | * Password: `12345`, `~@#123ABC`, `empty string` 26 | * Gender: `Male`, `Female` 27 | 28 | **Combinations** gets a set of input test data values and transforms it in **run-time tests for each generated combination of values**. 29 | 30 | So for our example the combinations will be: 31 | * [`John Smith`, `john.smith@example.com`, `12345`, `Male`] 32 | * [`John Smith`, `john.smith@example.com`, `12345`, `Female`] 33 | * [`John Smith`, `john.smith@example.com`, `~@#123ABC`, `Male`] 34 | * [`John Smith`, `john.smith@example.com`, `~@#123ABC`, `Female`] 35 | * etc. 36 | 37 | For each of this combinations will be generated a test (`XCTestCase`) 38 | 39 | A running example of the UI Tests generated by Combinations: 40 | 41 | ![Combinations](/assets/ui-tests-example.gif) 42 | 43 | ## Installation 44 | 45 | #### Carthage 46 | 47 | If you are using **Carthage**, you can always use it to build the library within your workspace by adding the line below to your `Cartfile`. 48 | 49 | ``` 50 | github "alexmx/Combinations" 51 | ``` 52 | 53 | #### CocoaPods 54 | 55 | If you are using **CocoaPods**, you can as well use it to integrate the library by adding the following lines to your `Podfile`. 56 | 57 | ```ruby 58 | platform :ios, '8.0' 59 | use_frameworks! 60 | 61 | target 'YourAppTarget' do 62 | pod "Combinations" 63 | end 64 | 65 | ``` 66 | 67 | #### Manual installation 68 | 69 | In order to include the **Combinations** library into your project, you need to build a dynamic framework from provided source code and include it into your project, or inlcude the entire **Combinations** library as sub-project by copying it to your project directory or include as Git submodule. 70 | 71 | ## Usage 72 | 73 | The library requires just two methods to be overriden: `valuesForCombinations` and `assertCombination:` 74 | 75 | ```swift 76 | import XCTest 77 | import Combinations 78 | 79 | class MyTests: CombinationsSpec { 80 | 81 | override func setUp() { 82 | super.setUp() 83 | } 84 | 85 | // Provide input values for Combinations 86 | override class func valuesForCombinations() -> [[Any]]? { 87 | return [ 88 | [1, 2, 3, 4], 89 | ["John Smith", "John", ""] 90 | ] 91 | } 92 | 93 | // This method will be called for each generated combination: 94 | // [1, "John Smith"], [1, "John"], [1, ""], [2, "John Smith"], etc. 95 | override func assertCombination(_ combination: [Any]) { 96 | 97 | // Perform required asserts on combination 98 | } 99 | } 100 | ``` 101 | 102 | ## License 103 | This project is licensed under the terms of the MIT license. See the LICENSE file. 104 | -------------------------------------------------------------------------------- /assets/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/assets/form.png -------------------------------------------------------------------------------- /assets/tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/assets/tests.png -------------------------------------------------------------------------------- /assets/ui-tests-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/assets/ui-tests-example.gif -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Combinations Docs (75% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 40 |
41 |
42 |
43 |

Classes

44 |

The following classes are available globally.

45 | 46 |
47 |
48 |
49 |
    50 |
  • 51 |
    52 | 53 | 54 | 55 | CombinationsGenerator 56 | 57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 |

    Undocumented

    64 | 65 | See more 66 |
    67 |
    68 |
    69 |
  • 70 |
71 |
72 |
73 |
74 | 78 |
79 |
80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/Classes/CombinationsGenerator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CombinationsGenerator Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Combinations Docs (75% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 41 |
42 |
43 |
44 |

CombinationsGenerator

45 |

Undocumented

46 | 47 |
48 |
49 |
50 |
    51 |
  • 52 |
    53 | 54 | 55 | 56 | combinations(forValues:) 57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |

    Generate combinations for provided input values.

    65 | 66 |
    67 |
    68 |

    Declaration

    69 |
    70 |

    Swift

    71 |
    public func combinations(forValues values: Matrix) -> Matrix
    72 | 73 |
    74 |
    75 |
    76 |

    Parameters

    77 | 78 | 79 | 80 | 85 | 90 | 91 | 92 |
    81 | 82 | values 83 | 84 | 86 |
    87 |

    The input values used to generate combinations.

    88 |
    89 |
    93 |
    94 |
    95 |

    Return Value

    96 |

    A matrix of generated combinations.

    97 |
    98 |
    99 |
    100 |
  • 101 |
  • 102 |
    103 | 104 | 105 | 106 | combinations(forValues:assertCombination:) 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Generate combinations for provided input values.

    115 | 116 |
    117 |
    118 |

    Declaration

    119 |
    120 |

    Swift

    121 |
    public func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void)
    122 | 123 |
    124 |
    125 |
    126 |

    Parameters

    127 | 128 | 129 | 130 | 135 | 140 | 141 | 142 | 147 | 152 | 153 | 154 |
    131 | 132 | values 133 | 134 | 136 |
    137 |

    The input values used to generate combinations.

    138 |
    139 |
    143 | 144 | assertCombination 145 | 146 | 148 |
    149 |

    The closure which will be invoked for every generated combination.

    150 |
    151 |
    155 |
    156 |
    157 |
    158 |
  • 159 |
  • 160 |
    161 | 162 | 163 | 164 | combinations(forValues:assertCombination:) 165 | 166 |
    167 |
    168 |
    169 |
    170 |
    171 |
    172 |

    Generate combinations for provided input values.

    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public static func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void)
    180 | 181 |
    182 |
    183 |
    184 |

    Parameters

    185 | 186 | 187 | 188 | 193 | 198 | 199 | 200 | 205 | 210 | 211 | 212 |
    189 | 190 | values 191 | 192 | 194 |
    195 |

    The input values used to generate combinations.

    196 |
    197 |
    201 | 202 | assertCombination 203 | 204 | 206 |
    207 |

    The closure which will be invoked for every generated combination.

    208 |
    209 |
    213 |
    214 |
    215 |
    216 |
  • 217 |
218 |
219 |
220 |
221 | 225 |
226 |
227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | documentationdocumentation75%75% -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 60px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; } 223 | .item .declaration-note { 224 | font-size: .85em; 225 | color: gray; 226 | font-style: italic; } 227 | 228 | .pointer-container { 229 | border-bottom: 1px solid #e2e2e2; 230 | left: -23px; 231 | padding-bottom: 13px; 232 | position: relative; 233 | width: 110%; } 234 | 235 | .pointer { 236 | background: #f9f9f9; 237 | border-left: 1px solid #e2e2e2; 238 | border-top: 1px solid #e2e2e2; 239 | height: 12px; 240 | left: 21px; 241 | top: -7px; 242 | -webkit-transform: rotate(45deg); 243 | -moz-transform: rotate(45deg); 244 | -o-transform: rotate(45deg); 245 | transform: rotate(45deg); 246 | position: absolute; 247 | width: 12px; } 248 | 249 | .height-container { 250 | display: none; 251 | left: -25px; 252 | padding: 0 25px; 253 | position: relative; 254 | width: 100%; 255 | overflow: hidden; } 256 | .height-container .section { 257 | background: #f9f9f9; 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -25px; 260 | position: relative; 261 | width: 100%; 262 | padding-top: 10px; 263 | padding-bottom: 5px; } 264 | 265 | .aside, .language { 266 | padding: 6px 12px; 267 | margin: 12px 0; 268 | border-left: 5px solid #dddddd; 269 | overflow-y: hidden; } 270 | .aside .aside-title, .language .aside-title { 271 | font-size: 9px; 272 | letter-spacing: 2px; 273 | text-transform: uppercase; 274 | padding-bottom: 0; 275 | margin: 0; 276 | color: #aaa; 277 | -webkit-user-select: none; } 278 | .aside p:last-child, .language p:last-child { 279 | margin-bottom: 0; } 280 | 281 | .language { 282 | border-left: 5px solid #cde9f4; } 283 | .language .aside-title { 284 | color: #4b8afb; } 285 | 286 | .aside-warning { 287 | border-left: 5px solid #ff6666; } 288 | .aside-warning .aside-title { 289 | color: #ff0000; } 290 | 291 | .graybox { 292 | border-collapse: collapse; 293 | width: 100%; } 294 | .graybox p { 295 | margin: 0; 296 | word-break: break-word; 297 | min-width: 50px; } 298 | .graybox td { 299 | border: 1px solid #e2e2e2; 300 | padding: 5px 25px 5px 10px; 301 | vertical-align: middle; } 302 | .graybox tr td:first-of-type { 303 | text-align: right; 304 | padding: 7px; 305 | vertical-align: top; 306 | word-break: normal; 307 | width: 40px; } 308 | 309 | .slightly-smaller { 310 | font-size: 0.9em; } 311 | 312 | #footer { 313 | position: absolute; 314 | bottom: 10px; 315 | margin-left: 25px; } 316 | #footer p { 317 | margin: 0; 318 | color: #aaa; 319 | font-size: 0.8em; } 320 | 321 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 322 | display: none; } 323 | html.dash .main-content { 324 | width: 980px; 325 | margin-left: 0; 326 | border: none; 327 | width: 100%; 328 | top: 0; 329 | padding-bottom: 0; } 330 | html.dash .height-container { 331 | display: block; } 332 | html.dash .item .token { 333 | margin-left: 0; } 334 | html.dash .content-wrapper { 335 | width: auto; } 336 | html.dash #footer { 337 | position: static; } 338 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.combinations 7 | CFBundleName 8 | Combinations 9 | DocSetPlatformFamily 10 | combinations 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Combinations Docs (75% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 40 |
41 |
42 |
43 |

Classes

44 |

The following classes are available globally.

45 | 46 |
47 |
48 |
49 |
    50 |
  • 51 |
    52 | 53 | 54 | 55 | CombinationsGenerator 56 | 57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 |

    Undocumented

    64 | 65 | See more 66 |
    67 |
    68 |
    69 |
  • 70 |
71 |
72 |
73 |
74 | 78 |
79 |
80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/Classes/CombinationsGenerator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CombinationsGenerator Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Combinations Docs (75% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 41 |
42 |
43 |
44 |

CombinationsGenerator

45 |

Undocumented

46 | 47 |
48 |
49 |
50 |
    51 |
  • 52 |
    53 | 54 | 55 | 56 | combinations(forValues:) 57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |

    Generate combinations for provided input values.

    65 | 66 |
    67 |
    68 |

    Declaration

    69 |
    70 |

    Swift

    71 |
    public func combinations(forValues values: Matrix) -> Matrix
    72 | 73 |
    74 |
    75 |
    76 |

    Parameters

    77 | 78 | 79 | 80 | 85 | 90 | 91 | 92 |
    81 | 82 | values 83 | 84 | 86 |
    87 |

    The input values used to generate combinations.

    88 |
    89 |
    93 |
    94 |
    95 |

    Return Value

    96 |

    A matrix of generated combinations.

    97 |
    98 |
    99 |
    100 |
  • 101 |
  • 102 |
    103 | 104 | 105 | 106 | combinations(forValues:assertCombination:) 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Generate combinations for provided input values.

    115 | 116 |
    117 |
    118 |

    Declaration

    119 |
    120 |

    Swift

    121 |
    public func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void)
    122 | 123 |
    124 |
    125 |
    126 |

    Parameters

    127 | 128 | 129 | 130 | 135 | 140 | 141 | 142 | 147 | 152 | 153 | 154 |
    131 | 132 | values 133 | 134 | 136 |
    137 |

    The input values used to generate combinations.

    138 |
    139 |
    143 | 144 | assertCombination 145 | 146 | 148 |
    149 |

    The closure which will be invoked for every generated combination.

    150 |
    151 |
    155 |
    156 |
    157 |
    158 |
  • 159 |
  • 160 |
    161 | 162 | 163 | 164 | combinations(forValues:assertCombination:) 165 | 166 |
    167 |
    168 |
    169 |
    170 |
    171 |
    172 |

    Generate combinations for provided input values.

    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public static func combinations(forValues values: Matrix, assertCombination: ([AnyObject]) -> Void)
    180 | 181 |
    182 |
    183 |
    184 |

    Parameters

    185 | 186 | 187 | 188 | 193 | 198 | 199 | 200 | 205 | 210 | 211 | 212 |
    189 | 190 | values 191 | 192 | 194 |
    195 |

    The input values used to generate combinations.

    196 |
    197 |
    201 | 202 | assertCombination 203 | 204 | 206 |
    207 |

    The closure which will be invoked for every generated combination.

    208 |
    209 |
    213 |
    214 |
    215 |
    216 |
  • 217 |
218 |
219 |
220 |
221 | 225 |
226 |
227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | documentationdocumentation75%75% -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 60px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; } 223 | .item .declaration-note { 224 | font-size: .85em; 225 | color: gray; 226 | font-style: italic; } 227 | 228 | .pointer-container { 229 | border-bottom: 1px solid #e2e2e2; 230 | left: -23px; 231 | padding-bottom: 13px; 232 | position: relative; 233 | width: 110%; } 234 | 235 | .pointer { 236 | background: #f9f9f9; 237 | border-left: 1px solid #e2e2e2; 238 | border-top: 1px solid #e2e2e2; 239 | height: 12px; 240 | left: 21px; 241 | top: -7px; 242 | -webkit-transform: rotate(45deg); 243 | -moz-transform: rotate(45deg); 244 | -o-transform: rotate(45deg); 245 | transform: rotate(45deg); 246 | position: absolute; 247 | width: 12px; } 248 | 249 | .height-container { 250 | display: none; 251 | left: -25px; 252 | padding: 0 25px; 253 | position: relative; 254 | width: 100%; 255 | overflow: hidden; } 256 | .height-container .section { 257 | background: #f9f9f9; 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -25px; 260 | position: relative; 261 | width: 100%; 262 | padding-top: 10px; 263 | padding-bottom: 5px; } 264 | 265 | .aside, .language { 266 | padding: 6px 12px; 267 | margin: 12px 0; 268 | border-left: 5px solid #dddddd; 269 | overflow-y: hidden; } 270 | .aside .aside-title, .language .aside-title { 271 | font-size: 9px; 272 | letter-spacing: 2px; 273 | text-transform: uppercase; 274 | padding-bottom: 0; 275 | margin: 0; 276 | color: #aaa; 277 | -webkit-user-select: none; } 278 | .aside p:last-child, .language p:last-child { 279 | margin-bottom: 0; } 280 | 281 | .language { 282 | border-left: 5px solid #cde9f4; } 283 | .language .aside-title { 284 | color: #4b8afb; } 285 | 286 | .aside-warning { 287 | border-left: 5px solid #ff6666; } 288 | .aside-warning .aside-title { 289 | color: #ff0000; } 290 | 291 | .graybox { 292 | border-collapse: collapse; 293 | width: 100%; } 294 | .graybox p { 295 | margin: 0; 296 | word-break: break-word; 297 | min-width: 50px; } 298 | .graybox td { 299 | border: 1px solid #e2e2e2; 300 | padding: 5px 25px 5px 10px; 301 | vertical-align: middle; } 302 | .graybox tr td:first-of-type { 303 | text-align: right; 304 | padding: 7px; 305 | vertical-align: top; 306 | word-break: normal; 307 | width: 40px; } 308 | 309 | .slightly-smaller { 310 | font-size: 0.9em; } 311 | 312 | #footer { 313 | position: absolute; 314 | bottom: 10px; 315 | margin-left: 25px; } 316 | #footer p { 317 | margin: 0; 318 | color: #aaa; 319 | font-size: 0.8em; } 320 | 321 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 322 | display: none; } 323 | html.dash .main-content { 324 | width: 980px; 325 | margin-left: 0; 326 | border: none; 327 | width: 100%; 328 | top: 0; 329 | padding-bottom: 0; } 330 | html.dash .height-container { 331 | display: block; } 332 | html.dash .item .token { 333 | margin-left: 0; } 334 | html.dash .content-wrapper { 335 | width: auto; } 336 | html.dash #footer { 337 | position: static; } 338 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/docsets/Combinations.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/docsets/Combinations.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/docsets/Combinations.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Combinations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Combinations Docs (75% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 40 |
41 |
42 |
43 | 44 |

Combinations

45 | 46 |

Twitter: @amaimescu 47 | License

48 | 49 |

Combinations is an iOS testing utility framework suited for fast boundary testing.

50 | 51 |
52 |

Boundary testing or boundary value analysis, is where test cases are generated using the extremes of the input domain, e.g. maximum, minimum, just inside/outside boundaries, typical values, and error values. It is similar to Equivalence Partitioning but focuses on corner cases.

53 |
54 | 55 |

The framework does just two simple things:

56 | 57 |
    58 |
  1. Generate a set of combinations from test data input set;
  2. 59 |
  3. Generate a run-time unit or UI test for each of the combinations;
  4. 60 |
61 | 62 |

Check out the API reference for more information.

63 |

Simple example:

64 | 65 |

We have an input form with four fields: First Name, Email, Password and Gender.

66 | 67 |

68 | 69 |

We also have our testing boundary values defined for each field:

70 | 71 |
    72 |
  • First Name: John Smith, empty string
  • 73 |
  • Email: john.smith@example.com, john.smith@, empty string
  • 74 |
  • Password: 12345, ~@#123ABC, empty string
  • 75 |
  • Gender: Male, Female
  • 76 |
77 | 78 |

Combinations gets a set of input test data values and transforms it in run-time tests for each generated combination of values.

79 | 80 |

So for our example the combinations will be:

81 | 82 |
    83 |
  • [John Smith, john.smith@example.com, 12345, Male]
  • 84 |
  • [John Smith, john.smith@example.com, 12345, Female]
  • 85 |
  • [John Smith, john.smith@example.com, ~@#123ABC, Male]
  • 86 |
  • [John Smith, john.smith@example.com, ~@#123ABC, Female]
  • 87 |
  • etc.
  • 88 |
89 | 90 |

For each of this combinations will be generated a test (XCTestCase)

91 | 92 |

A running example of the UI Tests generated by Combinations:

93 | 94 |

Combinations

95 |

Installation

96 |

Carthage

97 | 98 |

If you are using Carthage, you can always use it to build the library within your workspace by adding the line below to your Cartfile.

99 |
github "alexmx/Combinations"
100 | 
101 |

CocoaPods

102 | 103 |

If you are using CocoaPods, you can as well use it to integrate the library by adding the following lines to your Podfile.

104 |
platform :ios, '8.0'
105 | use_frameworks!
106 | 
107 | target 'YourAppTarget' do
108 |     pod "Combinations"
109 | end
110 | 
111 | 
112 |

Manual installation

113 | 114 |

In order to include the Combinations library into your project, you need to build a dynamic framework from provided source code and include it into your project, or inlcude the entire Combinations library as sub-project by copying it to your project directory or include as Git submodule.

115 |

Usage

116 | 117 |

The library requires just two methods to be overriden: valuesForCombinations and assertCombination:

118 |
import XCTest
119 | import Combinations
120 | 
121 | class MyTests: CombinationsSpec {
122 | 
123 |     override func setUp() {
124 |         super.setUp()
125 |     }
126 | 
127 |     // Provide input values for Combinations
128 |     override class func valuesForCombinations() -> [[Any]]? {
129 |         return [
130 |             [1, 2, 3, 4],
131 |             ["John Smith", "John", ""]
132 |         ]
133 |     }
134 | 
135 |     // This method will be called for each generated combination:
136 |     // [1, "John Smith"], [1, "John"], [1, ""], [2, "John Smith"], etc.
137 |     override func assertCombination(_ combination: [Any]) {
138 | 
139 |         // Perform required asserts on combination
140 |     }
141 | }
142 | 
143 |

License

144 | 145 |

This project is licensed under the terms of the MIT license. See the LICENSE file.

146 | 147 |
148 |
149 | 153 |
154 |
155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | var tokenOffset = "15px"; 27 | var original = link.css('marginLeft') == tokenOffset; 28 | link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); 29 | $content = link.parent().parent().next(); 30 | $content.slideToggle(animationDuration); 31 | 32 | // Keeps the document from jumping to the hash. 33 | var href = $(this).attr('href'); 34 | if (history.pushState) { 35 | history.pushState({}, '', href); 36 | } else { 37 | location.hash = href; 38 | } 39 | event.preventDefault(); 40 | }); 41 | 42 | // Dumb down quotes within code blocks that delimit strings instead of quotations 43 | // https://github.com/realm/jazzy/issues/714 44 | $("code q").replaceWith(function () { 45 | return ["\"", $(this).contents(), "\""]; 46 | }); 47 | -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(im)combinationsForValues:":{"name":"combinations(forValues:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(im)combinationsForValues:assertCombination:":{"name":"combinations(forValues:assertCombination:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(cm)combinationsForValues:assertCombination:":{"name":"combinations(forValues:assertCombination:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html":{"name":"CombinationsGenerator","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/alexmx/Documents/Projects/mine/Combinations/Combinations/CombinationsGenerator.swift", 5 | "line": 14, 6 | "symbol": "CombinationsGenerator", 7 | "symbol_kind": "source.lang.swift.decl.class", 8 | "warning": "undocumented" 9 | } 10 | ], 11 | "source_directory": "/Users/alexmx/Documents/Projects/mine/Combinations" 12 | } -------------------------------------------------------------------------------- /docs/docsets/Combinations.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/docsets/Combinations.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/Combinations.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/docsets/Combinations.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexmx/Combinations/24f57fb289231370cb765fee12538f5264a37bcb/docs/img/gh.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Combinations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Combinations Docs (75% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 40 |
41 |
42 |
43 | 44 |

Combinations

45 | 46 |

Twitter: @amaimescu 47 | License

48 | 49 |

Combinations is an iOS testing utility framework suited for fast boundary testing.

50 | 51 |
52 |

Boundary testing or boundary value analysis, is where test cases are generated using the extremes of the input domain, e.g. maximum, minimum, just inside/outside boundaries, typical values, and error values. It is similar to Equivalence Partitioning but focuses on corner cases.

53 |
54 | 55 |

The framework does just two simple things:

56 | 57 |
    58 |
  1. Generate a set of combinations from test data input set;
  2. 59 |
  3. Generate a run-time unit or UI test for each of the combinations;
  4. 60 |
61 | 62 |

Check out the API reference for more information.

63 |

Simple example:

64 | 65 |

We have an input form with four fields: First Name, Email, Password and Gender.

66 | 67 |

68 | 69 |

We also have our testing boundary values defined for each field:

70 | 71 |
    72 |
  • First Name: John Smith, empty string
  • 73 |
  • Email: john.smith@example.com, john.smith@, empty string
  • 74 |
  • Password: 12345, ~@#123ABC, empty string
  • 75 |
  • Gender: Male, Female
  • 76 |
77 | 78 |

Combinations gets a set of input test data values and transforms it in run-time tests for each generated combination of values.

79 | 80 |

So for our example the combinations will be:

81 | 82 |
    83 |
  • [John Smith, john.smith@example.com, 12345, Male]
  • 84 |
  • [John Smith, john.smith@example.com, 12345, Female]
  • 85 |
  • [John Smith, john.smith@example.com, ~@#123ABC, Male]
  • 86 |
  • [John Smith, john.smith@example.com, ~@#123ABC, Female]
  • 87 |
  • etc.
  • 88 |
89 | 90 |

For each of this combinations will be generated a test (XCTestCase)

91 | 92 |

A running example of the UI Tests generated by Combinations:

93 | 94 |

Combinations

95 |

Installation

96 |

Carthage

97 | 98 |

If you are using Carthage, you can always use it to build the library within your workspace by adding the line below to your Cartfile.

99 |
github "alexmx/Combinations"
100 | 
101 |

CocoaPods

102 | 103 |

If you are using CocoaPods, you can as well use it to integrate the library by adding the following lines to your Podfile.

104 |
platform :ios, '8.0'
105 | use_frameworks!
106 | 
107 | target 'YourAppTarget' do
108 |     pod "Combinations"
109 | end
110 | 
111 | 
112 |

Manual installation

113 | 114 |

In order to include the Combinations library into your project, you need to build a dynamic framework from provided source code and include it into your project, or inlcude the entire Combinations library as sub-project by copying it to your project directory or include as Git submodule.

115 |

Usage

116 | 117 |

The library requires just two methods to be overriden: valuesForCombinations and assertCombination:

118 |
import XCTest
119 | import Combinations
120 | 
121 | class MyTests: CombinationsSpec {
122 | 
123 |     override func setUp() {
124 |         super.setUp()
125 |     }
126 | 
127 |     // Provide input values for Combinations
128 |     override class func valuesForCombinations() -> [[Any]]? {
129 |         return [
130 |             [1, 2, 3, 4],
131 |             ["John Smith", "John", ""]
132 |         ]
133 |     }
134 | 
135 |     // This method will be called for each generated combination:
136 |     // [1, "John Smith"], [1, "John"], [1, ""], [2, "John Smith"], etc.
137 |     override func assertCombination(_ combination: [Any]) {
138 | 
139 |         // Perform required asserts on combination
140 |     }
141 | }
142 | 
143 |

License

144 | 145 |

This project is licensed under the terms of the MIT license. See the LICENSE file.

146 | 147 |
148 |
149 | 153 |
154 |
155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | var tokenOffset = "15px"; 27 | var original = link.css('marginLeft') == tokenOffset; 28 | link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); 29 | $content = link.parent().parent().next(); 30 | $content.slideToggle(animationDuration); 31 | 32 | // Keeps the document from jumping to the hash. 33 | var href = $(this).attr('href'); 34 | if (history.pushState) { 35 | history.pushState({}, '', href); 36 | } else { 37 | location.hash = href; 38 | } 39 | event.preventDefault(); 40 | }); 41 | 42 | // Dumb down quotes within code blocks that delimit strings instead of quotations 43 | // https://github.com/realm/jazzy/issues/714 44 | $("code q").replaceWith(function () { 45 | return ["\"", $(this).contents(), "\""]; 46 | }); 47 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(im)combinationsForValues:":{"name":"combinations(forValues:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(im)combinationsForValues:assertCombination:":{"name":"combinations(forValues:assertCombination:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html#/c:@M@Combinations@objc(cs)CombinationsGenerator(cm)combinationsForValues:assertCombination:":{"name":"combinations(forValues:assertCombination:)","abstract":"

Generate combinations for provided input values.

","parent_name":"CombinationsGenerator"},"Classes/CombinationsGenerator.html":{"name":"CombinationsGenerator","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"}} -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/alexmx/Documents/Projects/mine/Combinations/Combinations/CombinationsGenerator.swift", 5 | "line": 14, 6 | "symbol": "CombinationsGenerator", 7 | "symbol_kind": "source.lang.swift.decl.class", 8 | "warning": "undocumented" 9 | } 10 | ], 11 | "source_directory": "/Users/alexmx/Documents/Projects/mine/Combinations" 12 | } --------------------------------------------------------------------------------