├── .gitignore ├── LICENSE.md ├── Package.swift ├── PasswordRules.xcodeproj ├── PasswordRulesTests_Info.plist ├── PasswordRules_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── PasswordRules-Package.xcscheme ├── README.md ├── Sources └── PasswordRules │ └── PasswordRules.swift └── Tests ├── LinuxMain.swift └── PasswordRulesTests ├── PasswordRulesTests.swift └── XCTestManifests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Read Evaluate Press, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "PasswordRules", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "PasswordRules", 12 | targets: ["PasswordRules"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "PasswordRules", 23 | dependencies: []), 24 | .testTarget( 25 | name: "PasswordRulesTests", 26 | dependencies: ["PasswordRules"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/PasswordRulesTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/PasswordRules_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "PasswordRules::PasswordRulesPackageTests::ProductTarget" /* PasswordRulesPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_31 /* Build configuration list for PBXAggregateTarget "PasswordRulesPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_34 /* PBXTargetDependency */, 17 | ); 18 | name = PasswordRulesPackageTests; 19 | productName = PasswordRulesPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | OBJ_22 /* PasswordRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* PasswordRules.swift */; }; 25 | OBJ_29 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 26 | OBJ_40 /* PasswordRulesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* PasswordRulesTests.swift */; }; 27 | OBJ_41 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* XCTestManifests.swift */; }; 28 | OBJ_43 /* PasswordRules.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PasswordRules::PasswordRules::Product" /* PasswordRules.framework */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | F8532BFD210F1AA300A3A56D /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = OBJ_1 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = "PasswordRules::PasswordRules"; 37 | remoteInfo = PasswordRules; 38 | }; 39 | F8532BFE210F1AA400A3A56D /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = OBJ_1 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = "PasswordRules::PasswordRulesTests"; 44 | remoteInfo = PasswordRulesTests; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | OBJ_12 /* PasswordRulesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordRulesTests.swift; sourceTree = ""; }; 50 | OBJ_13 /* XCTestManifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestManifests.swift; sourceTree = ""; }; 51 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 52 | OBJ_9 /* PasswordRules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordRules.swift; sourceTree = ""; }; 53 | "PasswordRules::PasswordRules::Product" /* PasswordRules.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = PasswordRules.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | "PasswordRules::PasswordRulesTests::Product" /* PasswordRulesTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = PasswordRulesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | OBJ_23 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 0; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | OBJ_42 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 0; 68 | files = ( 69 | OBJ_43 /* PasswordRules.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | OBJ_10 /* Tests */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | OBJ_11 /* PasswordRulesTests */, 80 | ); 81 | name = Tests; 82 | sourceTree = SOURCE_ROOT; 83 | }; 84 | OBJ_11 /* PasswordRulesTests */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | OBJ_12 /* PasswordRulesTests.swift */, 88 | OBJ_13 /* XCTestManifests.swift */, 89 | ); 90 | name = PasswordRulesTests; 91 | path = Tests/PasswordRulesTests; 92 | sourceTree = SOURCE_ROOT; 93 | }; 94 | OBJ_14 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | "PasswordRules::PasswordRulesTests::Product" /* PasswordRulesTests.xctest */, 98 | "PasswordRules::PasswordRules::Product" /* PasswordRules.framework */, 99 | ); 100 | name = Products; 101 | sourceTree = BUILT_PRODUCTS_DIR; 102 | }; 103 | OBJ_5 /* */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | OBJ_6 /* Package.swift */, 107 | OBJ_7 /* Sources */, 108 | OBJ_10 /* Tests */, 109 | OBJ_14 /* Products */, 110 | ); 111 | name = ""; 112 | sourceTree = ""; 113 | }; 114 | OBJ_7 /* Sources */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | OBJ_8 /* PasswordRules */, 118 | ); 119 | name = Sources; 120 | sourceTree = SOURCE_ROOT; 121 | }; 122 | OBJ_8 /* PasswordRules */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | OBJ_9 /* PasswordRules.swift */, 126 | ); 127 | name = PasswordRules; 128 | path = Sources/PasswordRules; 129 | sourceTree = SOURCE_ROOT; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXNativeTarget section */ 134 | "PasswordRules::PasswordRules" /* PasswordRules */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = OBJ_18 /* Build configuration list for PBXNativeTarget "PasswordRules" */; 137 | buildPhases = ( 138 | OBJ_21 /* Sources */, 139 | OBJ_23 /* Frameworks */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | ); 145 | name = PasswordRules; 146 | productName = PasswordRules; 147 | productReference = "PasswordRules::PasswordRules::Product" /* PasswordRules.framework */; 148 | productType = "com.apple.product-type.framework"; 149 | }; 150 | "PasswordRules::PasswordRulesTests" /* PasswordRulesTests */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = OBJ_36 /* Build configuration list for PBXNativeTarget "PasswordRulesTests" */; 153 | buildPhases = ( 154 | OBJ_39 /* Sources */, 155 | OBJ_42 /* Frameworks */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | OBJ_44 /* PBXTargetDependency */, 161 | ); 162 | name = PasswordRulesTests; 163 | productName = PasswordRulesTests; 164 | productReference = "PasswordRules::PasswordRulesTests::Product" /* PasswordRulesTests.xctest */; 165 | productType = "com.apple.product-type.bundle.unit-test"; 166 | }; 167 | "PasswordRules::SwiftPMPackageDescription" /* PasswordRulesPackageDescription */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = OBJ_25 /* Build configuration list for PBXNativeTarget "PasswordRulesPackageDescription" */; 170 | buildPhases = ( 171 | OBJ_28 /* Sources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | ); 177 | name = PasswordRulesPackageDescription; 178 | productName = PasswordRulesPackageDescription; 179 | productType = "com.apple.product-type.framework"; 180 | }; 181 | /* End PBXNativeTarget section */ 182 | 183 | /* Begin PBXProject section */ 184 | OBJ_1 /* Project object */ = { 185 | isa = PBXProject; 186 | attributes = { 187 | LastUpgradeCheck = 9999; 188 | }; 189 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "PasswordRules" */; 190 | compatibilityVersion = "Xcode 3.2"; 191 | developmentRegion = English; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | ); 196 | mainGroup = OBJ_5 /* */; 197 | productRefGroup = OBJ_14 /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | "PasswordRules::PasswordRules" /* PasswordRules */, 202 | "PasswordRules::SwiftPMPackageDescription" /* PasswordRulesPackageDescription */, 203 | "PasswordRules::PasswordRulesPackageTests::ProductTarget" /* PasswordRulesPackageTests */, 204 | "PasswordRules::PasswordRulesTests" /* PasswordRulesTests */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | OBJ_21 /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 0; 213 | files = ( 214 | OBJ_22 /* PasswordRules.swift in Sources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | OBJ_28 /* Sources */ = { 219 | isa = PBXSourcesBuildPhase; 220 | buildActionMask = 0; 221 | files = ( 222 | OBJ_29 /* Package.swift in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | OBJ_39 /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 0; 229 | files = ( 230 | OBJ_40 /* PasswordRulesTests.swift in Sources */, 231 | OBJ_41 /* XCTestManifests.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin PBXTargetDependency section */ 238 | OBJ_34 /* PBXTargetDependency */ = { 239 | isa = PBXTargetDependency; 240 | target = "PasswordRules::PasswordRulesTests" /* PasswordRulesTests */; 241 | targetProxy = F8532BFE210F1AA400A3A56D /* PBXContainerItemProxy */; 242 | }; 243 | OBJ_44 /* PBXTargetDependency */ = { 244 | isa = PBXTargetDependency; 245 | target = "PasswordRules::PasswordRules" /* PasswordRules */; 246 | targetProxy = F8532BFD210F1AA300A3A56D /* PBXContainerItemProxy */; 247 | }; 248 | /* End PBXTargetDependency section */ 249 | 250 | /* Begin XCBuildConfiguration section */ 251 | OBJ_19 /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | ENABLE_TESTABILITY = YES; 255 | FRAMEWORK_SEARCH_PATHS = ( 256 | "$(inherited)", 257 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 258 | ); 259 | HEADER_SEARCH_PATHS = "$(inherited)"; 260 | INFOPLIST_FILE = PasswordRules.xcodeproj/PasswordRules_Info.plist; 261 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 262 | OTHER_CFLAGS = "$(inherited)"; 263 | OTHER_LDFLAGS = "$(inherited)"; 264 | OTHER_SWIFT_FLAGS = "$(inherited)"; 265 | PRODUCT_BUNDLE_IDENTIFIER = PasswordRules; 266 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 267 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 268 | SKIP_INSTALL = YES; 269 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 270 | SWIFT_VERSION = 4.2; 271 | TARGET_NAME = PasswordRules; 272 | }; 273 | name = Debug; 274 | }; 275 | OBJ_20 /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ENABLE_TESTABILITY = YES; 279 | FRAMEWORK_SEARCH_PATHS = ( 280 | "$(inherited)", 281 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 282 | ); 283 | HEADER_SEARCH_PATHS = "$(inherited)"; 284 | INFOPLIST_FILE = PasswordRules.xcodeproj/PasswordRules_Info.plist; 285 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 286 | OTHER_CFLAGS = "$(inherited)"; 287 | OTHER_LDFLAGS = "$(inherited)"; 288 | OTHER_SWIFT_FLAGS = "$(inherited)"; 289 | PRODUCT_BUNDLE_IDENTIFIER = PasswordRules; 290 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 291 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 292 | SKIP_INSTALL = YES; 293 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 294 | SWIFT_VERSION = 4.2; 295 | TARGET_NAME = PasswordRules; 296 | }; 297 | name = Release; 298 | }; 299 | OBJ_26 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | LD = /usr/bin/true; 303 | OTHER_SWIFT_FLAGS = "-swift-version 4.2 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; 304 | SWIFT_VERSION = 4.2; 305 | }; 306 | name = Debug; 307 | }; 308 | OBJ_27 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | LD = /usr/bin/true; 312 | OTHER_SWIFT_FLAGS = "-swift-version 4.2 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; 313 | SWIFT_VERSION = 4.2; 314 | }; 315 | name = Release; 316 | }; 317 | OBJ_3 /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | COMBINE_HIDPI_IMAGES = YES; 322 | COPY_PHASE_STRIP = NO; 323 | DEBUG_INFORMATION_FORMAT = dwarf; 324 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 325 | ENABLE_NS_ASSERTIONS = YES; 326 | GCC_OPTIMIZATION_LEVEL = 0; 327 | GCC_PREPROCESSOR_DEFINITIONS = ( 328 | "DEBUG=1", 329 | "$(inherited)", 330 | ); 331 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 332 | MACOSX_DEPLOYMENT_TARGET = 10.10; 333 | ONLY_ACTIVE_ARCH = YES; 334 | OTHER_SWIFT_FLAGS = "-DXcode"; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | SDKROOT = iphoneos; 337 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 338 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG"; 339 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 340 | TVOS_DEPLOYMENT_TARGET = 10.0; 341 | USE_HEADERMAP = NO; 342 | WATCHOS_DEPLOYMENT_TARGET = 3.0; 343 | }; 344 | name = Debug; 345 | }; 346 | OBJ_32 /* Debug */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | }; 350 | name = Debug; 351 | }; 352 | OBJ_33 /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | }; 356 | name = Release; 357 | }; 358 | OBJ_37 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | CLANG_ENABLE_MODULES = YES; 362 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 363 | FRAMEWORK_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 366 | ); 367 | HEADER_SEARCH_PATHS = "$(inherited)"; 368 | INFOPLIST_FILE = PasswordRules.xcodeproj/PasswordRulesTests_Info.plist; 369 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 370 | OTHER_CFLAGS = "$(inherited)"; 371 | OTHER_LDFLAGS = "$(inherited)"; 372 | OTHER_SWIFT_FLAGS = "$(inherited)"; 373 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 374 | SWIFT_VERSION = 4.2; 375 | TARGET_NAME = PasswordRulesTests; 376 | }; 377 | name = Debug; 378 | }; 379 | OBJ_38 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | CLANG_ENABLE_MODULES = YES; 383 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 384 | FRAMEWORK_SEARCH_PATHS = ( 385 | "$(inherited)", 386 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 387 | ); 388 | HEADER_SEARCH_PATHS = "$(inherited)"; 389 | INFOPLIST_FILE = PasswordRules.xcodeproj/PasswordRulesTests_Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 391 | OTHER_CFLAGS = "$(inherited)"; 392 | OTHER_LDFLAGS = "$(inherited)"; 393 | OTHER_SWIFT_FLAGS = "$(inherited)"; 394 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 395 | SWIFT_VERSION = 4.2; 396 | TARGET_NAME = PasswordRulesTests; 397 | }; 398 | name = Release; 399 | }; 400 | OBJ_4 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | COMBINE_HIDPI_IMAGES = YES; 405 | COPY_PHASE_STRIP = YES; 406 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 407 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 408 | GCC_OPTIMIZATION_LEVEL = s; 409 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 410 | MACOSX_DEPLOYMENT_TARGET = 10.10; 411 | OTHER_SWIFT_FLAGS = "-DXcode"; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | SDKROOT = iphoneos; 414 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 415 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 416 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 417 | TVOS_DEPLOYMENT_TARGET = 10.0; 418 | USE_HEADERMAP = NO; 419 | WATCHOS_DEPLOYMENT_TARGET = 3.0; 420 | }; 421 | name = Release; 422 | }; 423 | /* End XCBuildConfiguration section */ 424 | 425 | /* Begin XCConfigurationList section */ 426 | OBJ_18 /* Build configuration list for PBXNativeTarget "PasswordRules" */ = { 427 | isa = XCConfigurationList; 428 | buildConfigurations = ( 429 | OBJ_19 /* Debug */, 430 | OBJ_20 /* Release */, 431 | ); 432 | defaultConfigurationIsVisible = 0; 433 | defaultConfigurationName = Release; 434 | }; 435 | OBJ_2 /* Build configuration list for PBXProject "PasswordRules" */ = { 436 | isa = XCConfigurationList; 437 | buildConfigurations = ( 438 | OBJ_3 /* Debug */, 439 | OBJ_4 /* Release */, 440 | ); 441 | defaultConfigurationIsVisible = 0; 442 | defaultConfigurationName = Release; 443 | }; 444 | OBJ_25 /* Build configuration list for PBXNativeTarget "PasswordRulesPackageDescription" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | OBJ_26 /* Debug */, 448 | OBJ_27 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | OBJ_31 /* Build configuration list for PBXAggregateTarget "PasswordRulesPackageTests" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | OBJ_32 /* Debug */, 457 | OBJ_33 /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | OBJ_36 /* Build configuration list for PBXNativeTarget "PasswordRulesTests" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | OBJ_37 /* Debug */, 466 | OBJ_38 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | /* End XCConfigurationList section */ 472 | }; 473 | rootObject = OBJ_1 /* Project object */; 474 | } 475 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /PasswordRules.xcodeproj/xcshareddata/xcschemes/PasswordRules-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PasswordRules 2 | 3 | A Swift library for defining strong password generator rules. 4 | 5 | This functionality is discussed in the NSHipster article 6 | [Password Rules / UIText​Input​Password​Rules](https://nshipster.com/uitextinputpasswordrules/). 7 | 8 | --- 9 | 10 | ## Requirements 11 | 12 | - Swift 4.0+ 13 | 14 | ## Installation 15 | 16 | ### Swift Package Manager 17 | 18 | Add the PasswordRules package to your target dependencies in `Package.swift`: 19 | 20 | ```swift 21 | import PackageDescription 22 | 23 | let package = Package( 24 | name: "YourProject", 25 | dependencies: [ 26 | .package( 27 | url: "https://github.com/NSHipster/PasswordRules", 28 | from: "1.0.0" 29 | ), 30 | ] 31 | ) 32 | ``` 33 | 34 | Then run the `swift build` command to build your project. 35 | 36 | ### Carthage 37 | 38 | To use PasswordRules in your Xcode project using Carthage, 39 | specify it in `Cartfile`: 40 | 41 | ``` 42 | github "NSHipster/PasswordRules" ~> 1.0.0 43 | ``` 44 | 45 | Then run the `carthage update` command to build the framework, 46 | and drag the built PasswordRules.framework into your Xcode project. 47 | 48 | ## Usage 49 | 50 | Define an array of `PasswordRules` cases 51 | and use the `descriptor(for:)` method to create a string representation: 52 | 53 | ```swift 54 | import PasswordRules 55 | 56 | let rules: [PasswordRule] = [ .required(.upper), 57 | .required(.lower), 58 | .required(.special), 59 | .minLength(20) ] 60 | 61 | let descriptor = PasswordRule.descriptor(for: rules) 62 | // "required: upper; required: lower; required: special; minlength: 20;" 63 | ``` 64 | 65 | Initialize a `UITextInputPasswordRules` object using the descriptor 66 | and pass that to the `passwordRules` property of a secure text field: 67 | 68 | ```swift 69 | import UIKit 70 | 71 | guard #available(iOS 12, *) else { 72 | fatalError() 73 | } 74 | 75 | self.passwordTextField.passwordRules = 76 | UITextInputPasswordRules(descriptor: descriptor) 77 | ``` 78 | 79 | ## License 80 | 81 | MIT 82 | 83 | ## Contact 84 | 85 | NSHipster ([@NSHipster](https://twitter.com/NSHipster)) 86 | -------------------------------------------------------------------------------- /Sources/PasswordRules/PasswordRules.swift: -------------------------------------------------------------------------------- 1 | public enum PasswordRule { 2 | public enum CharacterClass { 3 | case upper 4 | case lower 5 | case digits 6 | case special 7 | case asciiPrintable 8 | case unicode 9 | case custom(Set) 10 | } 11 | 12 | case required(CharacterClass) 13 | case allowed(CharacterClass) 14 | case maxConsecutive(UInt) 15 | case minLength(UInt) 16 | case maxLength(UInt) 17 | 18 | public static func descriptor(for rules: [PasswordRule]) -> String { 19 | return rules.map{ "\($0.description);" }.joined(separator: " ") 20 | } 21 | } 22 | 23 | extension PasswordRule: CustomStringConvertible { 24 | public var description: String { 25 | switch self { 26 | case .required(let characterClass): 27 | return "required: \(characterClass)" 28 | case .allowed(let characterClass): 29 | return "allowed: \(characterClass)" 30 | case .maxConsecutive(let length): 31 | return "max-consecutive: \(length)" 32 | case .minLength(let length): 33 | return "minlength: \(length)" 34 | case .maxLength(let length): 35 | return "maxlength: \(length)" 36 | } 37 | } 38 | } 39 | 40 | extension PasswordRule.CharacterClass: CustomStringConvertible { 41 | public var description: String { 42 | switch self { 43 | case .upper: return "upper" 44 | case .lower: return "lower" 45 | case .digits: return "digits" 46 | case .special: return "special" 47 | case .asciiPrintable: return "ascii-printable" 48 | case .unicode: return "unicode" 49 | case .custom(let characters): 50 | return "[" + String(characters) + "]" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import PasswordRulesTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += PasswordRulesTests.allTests() 7 | XCTMain(tests) -------------------------------------------------------------------------------- /Tests/PasswordRulesTests/PasswordRulesTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import PasswordRules 3 | 4 | final class PasswordRulesTests: XCTestCase { 5 | func testPasswordRules() { 6 | let rules: [PasswordRule] = [ .required(.upper), 7 | .required(.lower), 8 | .required(.special), 9 | .allowed(.asciiPrintable), 10 | .minLength(20) ] 11 | 12 | XCTAssertEqual(PasswordRule.descriptor(for: rules), "required: upper; required: lower; required: special; allowed: ascii-printable; minlength: 20;") 13 | } 14 | 15 | static var allTests = [ 16 | ("testPasswordRules", testPasswordRules), 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /Tests/PasswordRulesTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !os(macOS) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(PasswordRulesTests.allTests), 7 | ] 8 | } 9 | #endif --------------------------------------------------------------------------------