├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── Podfile.lock ├── SwiftString.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── SwiftString-Example.xcscheme ├── SwiftString.xcworkspace │ └── contents.xcworkspacedata ├── SwiftString │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift └── Tests │ ├── Info.plist │ ├── String+HTMLTests.swift │ └── StringExtensionTests.swift ├── LICENSE ├── Pod ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── String+HTML.swift │ └── StringExtensions.swift ├── README.md ├── SwiftString.podspec └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | Pods/ 34 | 35 | Example/Podfile.lock 36 | 37 | *.lock 38 | 39 | *.lock 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | osx_image: xcode7.1 7 | cache: cocoapods 8 | podfile: Example/Podfile 9 | before_install: 10 | - gem install cocoapods # Since Travis is not always on latest version 11 | - pod install --project-directory=Example 12 | 13 | script: 14 | - set -o pipefail && xcodebuild test -workspace Example/SwiftString.xcworkspace -scheme SwiftString-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty 15 | - pod lib lint 16 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | 4 | target 'SwiftString_Example', :exclusive => true do 5 | pod 'SwiftString', :path => '../' 6 | end 7 | 8 | target 'SwiftString_Tests', :exclusive => true do 9 | pod 'SwiftString', :path => '../' 10 | pod 'SwiftHamcrest' 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SwiftHamcrest (0.3) 3 | - SwiftString (0.1) 4 | 5 | DEPENDENCIES: 6 | - SwiftHamcrest 7 | - SwiftString (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | SwiftString: 11 | :path: "../" 12 | 13 | SPEC CHECKSUMS: 14 | SwiftHamcrest: 652d2e197df0957b532f1c66a9112bc809030965 15 | SwiftString: 0a32494e59f63c337ad0b2b5fa8cdba5c536484a 16 | 17 | COCOAPODS: 0.39.0 18 | -------------------------------------------------------------------------------- /Example/SwiftString.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 589430772C3EC1111068D513 /* Pods_SwiftString_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA0E139958C875325F8154C8 /* Pods_SwiftString_Example.framework */; }; 11 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 12 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 13 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 14 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 15 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 16 | 607FACEC1AFB9204008FA782 /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* StringExtensionTests.swift */; }; 17 | C914AF1AC37AD238F5BE89DF /* Pods_SwiftString_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF3B5F0448FBA6B5F3853E10 /* Pods_SwiftString_Tests.framework */; }; 18 | E361734F1C5EC0DA0063AAC7 /* String+HTMLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E361734E1C5EC0DA0063AAC7 /* String+HTMLTests.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 27 | remoteInfo = SwiftString; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 019FA1CE0AA97C15E2CE000C /* Pods-SwiftString_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftString_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftString_Example/Pods-SwiftString_Example.debug.xcconfig"; sourceTree = ""; }; 33 | 0426C5BFC54D8C1FBD78C15F /* Pods-SwiftString_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftString_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftString_Example/Pods-SwiftString_Example.release.xcconfig"; sourceTree = ""; }; 34 | 372CCE3674464B433AA934A9 /* SwiftString.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SwiftString.podspec; path = ../SwiftString.podspec; sourceTree = ""; }; 35 | 43D9619446241F6E12A5D132 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 36 | 607FACD01AFB9204008FA782 /* SwiftString_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftString_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 42 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 43 | 607FACE51AFB9204008FA782 /* SwiftString_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftString_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 607FACEB1AFB9204008FA782 /* StringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = ""; }; 46 | 6F2196573BA624D905B98536 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 47 | AF3B5F0448FBA6B5F3853E10 /* Pods_SwiftString_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftString_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | BA0E139958C875325F8154C8 /* Pods_SwiftString_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftString_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | CAF2F5323C60B18222F42A88 /* Pods-SwiftString_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftString_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftString_Tests/Pods-SwiftString_Tests.release.xcconfig"; sourceTree = ""; }; 50 | DD7662C0FCE9A2A50CCAA5DA /* Pods-SwiftString_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftString_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftString_Tests/Pods-SwiftString_Tests.debug.xcconfig"; sourceTree = ""; }; 51 | E361734E1C5EC0DA0063AAC7 /* String+HTMLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+HTMLTests.swift"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 589430772C3EC1111068D513 /* Pods_SwiftString_Example.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | C914AF1AC37AD238F5BE89DF /* Pods_SwiftString_Tests.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 607FACC71AFB9204008FA782 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 78 | 607FACD21AFB9204008FA782 /* Example for SwiftString */, 79 | 607FACE81AFB9204008FA782 /* Tests */, 80 | 607FACD11AFB9204008FA782 /* Products */, 81 | 90F499C4F609D8EE4B8866F0 /* Pods */, 82 | 9B5FA3D4893E4286D48E5F75 /* Frameworks */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 607FACD11AFB9204008FA782 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 607FACD01AFB9204008FA782 /* SwiftString_Example.app */, 90 | 607FACE51AFB9204008FA782 /* SwiftString_Tests.xctest */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 607FACD21AFB9204008FA782 /* Example for SwiftString */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 99 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 100 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 101 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 102 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 103 | 607FACD31AFB9204008FA782 /* Supporting Files */, 104 | ); 105 | name = "Example for SwiftString"; 106 | path = SwiftString; 107 | sourceTree = ""; 108 | }; 109 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 607FACD41AFB9204008FA782 /* Info.plist */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | 607FACE81AFB9204008FA782 /* Tests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 607FACEB1AFB9204008FA782 /* StringExtensionTests.swift */, 121 | E361734E1C5EC0DA0063AAC7 /* String+HTMLTests.swift */, 122 | 607FACE91AFB9204008FA782 /* Supporting Files */, 123 | ); 124 | path = Tests; 125 | sourceTree = ""; 126 | }; 127 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 607FACEA1AFB9204008FA782 /* Info.plist */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 372CCE3674464B433AA934A9 /* SwiftString.podspec */, 139 | 43D9619446241F6E12A5D132 /* README.md */, 140 | 6F2196573BA624D905B98536 /* LICENSE */, 141 | ); 142 | name = "Podspec Metadata"; 143 | sourceTree = ""; 144 | }; 145 | 90F499C4F609D8EE4B8866F0 /* Pods */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 019FA1CE0AA97C15E2CE000C /* Pods-SwiftString_Example.debug.xcconfig */, 149 | 0426C5BFC54D8C1FBD78C15F /* Pods-SwiftString_Example.release.xcconfig */, 150 | DD7662C0FCE9A2A50CCAA5DA /* Pods-SwiftString_Tests.debug.xcconfig */, 151 | CAF2F5323C60B18222F42A88 /* Pods-SwiftString_Tests.release.xcconfig */, 152 | ); 153 | name = Pods; 154 | sourceTree = ""; 155 | }; 156 | 9B5FA3D4893E4286D48E5F75 /* Frameworks */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | BA0E139958C875325F8154C8 /* Pods_SwiftString_Example.framework */, 160 | AF3B5F0448FBA6B5F3853E10 /* Pods_SwiftString_Tests.framework */, 161 | ); 162 | name = Frameworks; 163 | sourceTree = ""; 164 | }; 165 | /* End PBXGroup section */ 166 | 167 | /* Begin PBXNativeTarget section */ 168 | 607FACCF1AFB9204008FA782 /* SwiftString_Example */ = { 169 | isa = PBXNativeTarget; 170 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftString_Example" */; 171 | buildPhases = ( 172 | 1EED4C78D4534C0B6CCEACEF /* Check Pods Manifest.lock */, 173 | 607FACCC1AFB9204008FA782 /* Sources */, 174 | 607FACCD1AFB9204008FA782 /* Frameworks */, 175 | 607FACCE1AFB9204008FA782 /* Resources */, 176 | 669AE38B7A12FD629F57750E /* Embed Pods Frameworks */, 177 | 2FB142E00953020DAFCDEA76 /* Copy Pods Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = SwiftString_Example; 184 | productName = SwiftString; 185 | productReference = 607FACD01AFB9204008FA782 /* SwiftString_Example.app */; 186 | productType = "com.apple.product-type.application"; 187 | }; 188 | 607FACE41AFB9204008FA782 /* SwiftString_Tests */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftString_Tests" */; 191 | buildPhases = ( 192 | DD43306B24A8C58478E69FA2 /* Check Pods Manifest.lock */, 193 | 607FACE11AFB9204008FA782 /* Sources */, 194 | 607FACE21AFB9204008FA782 /* Frameworks */, 195 | 607FACE31AFB9204008FA782 /* Resources */, 196 | F82078855395DF8B82837C90 /* Embed Pods Frameworks */, 197 | 98E0193430A08EF1190A7DC0 /* Copy Pods Resources */, 198 | ); 199 | buildRules = ( 200 | ); 201 | dependencies = ( 202 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 203 | ); 204 | name = SwiftString_Tests; 205 | productName = Tests; 206 | productReference = 607FACE51AFB9204008FA782 /* SwiftString_Tests.xctest */; 207 | productType = "com.apple.product-type.bundle.unit-test"; 208 | }; 209 | /* End PBXNativeTarget section */ 210 | 211 | /* Begin PBXProject section */ 212 | 607FACC81AFB9204008FA782 /* Project object */ = { 213 | isa = PBXProject; 214 | attributes = { 215 | LastSwiftUpdateCheck = 0720; 216 | LastUpgradeCheck = 0720; 217 | ORGANIZATIONNAME = CocoaPods; 218 | TargetAttributes = { 219 | 607FACCF1AFB9204008FA782 = { 220 | CreatedOnToolsVersion = 6.3.1; 221 | DevelopmentTeam = T6UGWQ58CZ; 222 | }; 223 | 607FACE41AFB9204008FA782 = { 224 | CreatedOnToolsVersion = 6.3.1; 225 | TestTargetID = 607FACCF1AFB9204008FA782; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "SwiftString" */; 230 | compatibilityVersion = "Xcode 3.2"; 231 | developmentRegion = English; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = 607FACC71AFB9204008FA782; 238 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | 607FACCF1AFB9204008FA782 /* SwiftString_Example */, 243 | 607FACE41AFB9204008FA782 /* SwiftString_Tests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 607FACCE1AFB9204008FA782 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 254 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 255 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | 607FACE31AFB9204008FA782 /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXResourcesBuildPhase section */ 267 | 268 | /* Begin PBXShellScriptBuildPhase section */ 269 | 1EED4C78D4534C0B6CCEACEF /* Check Pods Manifest.lock */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | ); 276 | name = "Check Pods Manifest.lock"; 277 | outputPaths = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | shellPath = /bin/sh; 281 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 282 | showEnvVarsInLog = 0; 283 | }; 284 | 2FB142E00953020DAFCDEA76 /* Copy Pods Resources */ = { 285 | isa = PBXShellScriptBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | inputPaths = ( 290 | ); 291 | name = "Copy Pods Resources"; 292 | outputPaths = ( 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | shellPath = /bin/sh; 296 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftString_Example/Pods-SwiftString_Example-resources.sh\"\n"; 297 | showEnvVarsInLog = 0; 298 | }; 299 | 669AE38B7A12FD629F57750E /* Embed Pods Frameworks */ = { 300 | isa = PBXShellScriptBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | inputPaths = ( 305 | ); 306 | name = "Embed Pods Frameworks"; 307 | outputPaths = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | shellPath = /bin/sh; 311 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftString_Example/Pods-SwiftString_Example-frameworks.sh\"\n"; 312 | showEnvVarsInLog = 0; 313 | }; 314 | 98E0193430A08EF1190A7DC0 /* Copy Pods Resources */ = { 315 | isa = PBXShellScriptBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | ); 319 | inputPaths = ( 320 | ); 321 | name = "Copy Pods Resources"; 322 | outputPaths = ( 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | shellPath = /bin/sh; 326 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftString_Tests/Pods-SwiftString_Tests-resources.sh\"\n"; 327 | showEnvVarsInLog = 0; 328 | }; 329 | DD43306B24A8C58478E69FA2 /* Check Pods Manifest.lock */ = { 330 | isa = PBXShellScriptBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | ); 334 | inputPaths = ( 335 | ); 336 | name = "Check Pods Manifest.lock"; 337 | outputPaths = ( 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | shellPath = /bin/sh; 341 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 342 | showEnvVarsInLog = 0; 343 | }; 344 | F82078855395DF8B82837C90 /* Embed Pods Frameworks */ = { 345 | isa = PBXShellScriptBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | inputPaths = ( 350 | ); 351 | name = "Embed Pods Frameworks"; 352 | outputPaths = ( 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | shellPath = /bin/sh; 356 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftString_Tests/Pods-SwiftString_Tests-frameworks.sh\"\n"; 357 | showEnvVarsInLog = 0; 358 | }; 359 | /* End PBXShellScriptBuildPhase section */ 360 | 361 | /* Begin PBXSourcesBuildPhase section */ 362 | 607FACCC1AFB9204008FA782 /* Sources */ = { 363 | isa = PBXSourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 367 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | 607FACE11AFB9204008FA782 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 607FACEC1AFB9204008FA782 /* StringExtensionTests.swift in Sources */, 376 | E361734F1C5EC0DA0063AAC7 /* String+HTMLTests.swift in Sources */, 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | /* End PBXSourcesBuildPhase section */ 381 | 382 | /* Begin PBXTargetDependency section */ 383 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 384 | isa = PBXTargetDependency; 385 | target = 607FACCF1AFB9204008FA782 /* SwiftString_Example */; 386 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 387 | }; 388 | /* End PBXTargetDependency section */ 389 | 390 | /* Begin PBXVariantGroup section */ 391 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 392 | isa = PBXVariantGroup; 393 | children = ( 394 | 607FACDA1AFB9204008FA782 /* Base */, 395 | ); 396 | name = Main.storyboard; 397 | sourceTree = ""; 398 | }; 399 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 400 | isa = PBXVariantGroup; 401 | children = ( 402 | 607FACDF1AFB9204008FA782 /* Base */, 403 | ); 404 | name = LaunchScreen.xib; 405 | sourceTree = ""; 406 | }; 407 | /* End PBXVariantGroup section */ 408 | 409 | /* Begin XCBuildConfiguration section */ 410 | 607FACED1AFB9204008FA782 /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 415 | CLANG_CXX_LIBRARY = "libc++"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 428 | COPY_PHASE_STRIP = NO; 429 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 430 | ENABLE_STRICT_OBJC_MSGSEND = YES; 431 | ENABLE_TESTABILITY = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu99; 433 | GCC_DYNAMIC_NO_PIC = NO; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | GCC_OPTIMIZATION_LEVEL = 0; 436 | GCC_PREPROCESSOR_DEFINITIONS = ( 437 | "DEBUG=1", 438 | "$(inherited)", 439 | ); 440 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = iphoneos; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | }; 453 | name = Debug; 454 | }; 455 | 607FACEE1AFB9204008FA782 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 460 | CLANG_CXX_LIBRARY = "libc++"; 461 | CLANG_ENABLE_MODULES = YES; 462 | CLANG_ENABLE_OBJC_ARC = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_CONSTANT_CONVERSION = YES; 465 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 466 | CLANG_WARN_EMPTY_BODY = YES; 467 | CLANG_WARN_ENUM_CONVERSION = YES; 468 | CLANG_WARN_INT_CONVERSION = YES; 469 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 475 | ENABLE_NS_ASSERTIONS = NO; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | GCC_C_LANGUAGE_STANDARD = gnu99; 478 | GCC_NO_COMMON_BLOCKS = YES; 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 486 | MTL_ENABLE_DEBUG_INFO = NO; 487 | SDKROOT = iphoneos; 488 | VALIDATE_PRODUCT = YES; 489 | }; 490 | name = Release; 491 | }; 492 | 607FACF01AFB9204008FA782 /* Debug */ = { 493 | isa = XCBuildConfiguration; 494 | baseConfigurationReference = 019FA1CE0AA97C15E2CE000C /* Pods-SwiftString_Example.debug.xcconfig */; 495 | buildSettings = { 496 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 497 | CODE_SIGN_IDENTITY = "iPhone Developer"; 498 | INFOPLIST_FILE = SwiftString/Info.plist; 499 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 500 | MODULE_NAME = ExampleApp; 501 | PRODUCT_BUNDLE_IDENTIFIER = "com.redbricklabs.SwiftString-Example"; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | }; 504 | name = Debug; 505 | }; 506 | 607FACF11AFB9204008FA782 /* Release */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 0426C5BFC54D8C1FBD78C15F /* Pods-SwiftString_Example.release.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CODE_SIGN_IDENTITY = "iPhone Developer"; 512 | INFOPLIST_FILE = SwiftString/Info.plist; 513 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 514 | MODULE_NAME = ExampleApp; 515 | PRODUCT_BUNDLE_IDENTIFIER = "com.redbricklabs.SwiftString-Example"; 516 | PRODUCT_NAME = "$(TARGET_NAME)"; 517 | }; 518 | name = Release; 519 | }; 520 | 607FACF31AFB9204008FA782 /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = DD7662C0FCE9A2A50CCAA5DA /* Pods-SwiftString_Tests.debug.xcconfig */; 523 | buildSettings = { 524 | BUNDLE_LOADER = "$(TEST_HOST)"; 525 | FRAMEWORK_SEARCH_PATHS = ( 526 | "$(SDKROOT)/Developer/Library/Frameworks", 527 | "$(inherited)", 528 | ); 529 | GCC_PREPROCESSOR_DEFINITIONS = ( 530 | "DEBUG=1", 531 | "$(inherited)", 532 | ); 533 | INFOPLIST_FILE = Tests/Info.plist; 534 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 535 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 536 | PRODUCT_NAME = "$(TARGET_NAME)"; 537 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftString_Example.app/SwiftString_Example"; 538 | }; 539 | name = Debug; 540 | }; 541 | 607FACF41AFB9204008FA782 /* Release */ = { 542 | isa = XCBuildConfiguration; 543 | baseConfigurationReference = CAF2F5323C60B18222F42A88 /* Pods-SwiftString_Tests.release.xcconfig */; 544 | buildSettings = { 545 | BUNDLE_LOADER = "$(TEST_HOST)"; 546 | FRAMEWORK_SEARCH_PATHS = ( 547 | "$(SDKROOT)/Developer/Library/Frameworks", 548 | "$(inherited)", 549 | ); 550 | INFOPLIST_FILE = Tests/Info.plist; 551 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 552 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftString_Example.app/SwiftString_Example"; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "SwiftString" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 607FACED1AFB9204008FA782 /* Debug */, 565 | 607FACEE1AFB9204008FA782 /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftString_Example" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 607FACF01AFB9204008FA782 /* Debug */, 574 | 607FACF11AFB9204008FA782 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftString_Tests" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 607FACF31AFB9204008FA782 /* Debug */, 583 | 607FACF41AFB9204008FA782 /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | /* End XCConfigurationList section */ 589 | }; 590 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 591 | } 592 | -------------------------------------------------------------------------------- /Example/SwiftString.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/SwiftString.xcodeproj/xcshareddata/xcschemes/SwiftString-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/SwiftString.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/SwiftString/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftString 4 | // 5 | // Created by Andrew Mayne on 01/31/2016. 6 | // Copyright (c) 2016 Andrew Mayne. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // 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. 24 | // 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. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/SwiftString/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/SwiftString/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/SwiftString/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "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 | } 39 | -------------------------------------------------------------------------------- /Example/SwiftString/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 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/SwiftString/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftString 4 | // 5 | // Created by Andrew Mayne on 01/31/2016. 6 | // Copyright (c) 2016 Andrew Mayne. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/String+HTMLTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Hamcrest 3 | import SwiftString 4 | 5 | class StringHTMLTests: XCTestCase { 6 | 7 | func testDecodeHTML() { 8 | assertThat("The Weekend ‘King Of The Fall’".decodeHTML(), presentAnd(equalTo("The Weekend ‘King Of The Fall’"))) 9 | assertThat(" 4 < 5 & 3 > 2 . Price: 12 €. @ ".decodeHTML(), presentAnd(equalTo(" 4 < 5 & 3 > 2 . Price: 12 €. @ "))) 10 | assertThat("this is so "good"".decodeHTML(), presentAnd(equalTo("this is so \"good\""))) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Example/Tests/StringExtensionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Hamcrest 3 | import SwiftString 4 | 5 | class SwiftStringTests: XCTestCase { 6 | 7 | func testBetween() { 8 | assertThat("foo".between("", ""), presentAnd(equalTo("foo"))) 9 | assertThat("foo".between("", ""), presentAnd(equalTo("foo"))) 10 | assertThat("foo".between("", ""), nilValue()) 11 | assertThat("Some strings } are very {weird}, dont you think?".between("{", "}"), presentAnd(equalTo("weird"))) 12 | assertThat("".between("", ""), nilValue()) 13 | assertThat("foo".between("", ""), nilValue()) 14 | } 15 | 16 | func testCamelize() { 17 | assertThat("os version".camelize() == "osVersion") 18 | assertThat("HelloWorld".camelize() == "helloWorld") 19 | assertThat("someword With Characters".camelize() == "somewordWithCharacters") 20 | assertThat("data_rate".camelize() == "dataRate") 21 | assertThat("background-color".camelize() == "backgroundColor") 22 | } 23 | 24 | func testCapitalize() { 25 | assertThat("hello world".capitalize() == "Hello World") 26 | } 27 | 28 | func testChompLeft() { 29 | assertThat("foobar".chompLeft("foo") == "bar") 30 | assertThat("foobar".chompLeft("bar") == "foo") 31 | } 32 | 33 | func testChompRight() { 34 | assertThat("foobar".chompRight("bar") == "foo") 35 | assertThat("foobar".chompRight("foo") == "bar") 36 | } 37 | 38 | func testClean() { 39 | assertThat("thisoneistwoathreetest".clean(with: " ", allOf: "one", "two", "three") == "this is a test") 40 | } 41 | 42 | func testCollapseWhitespace() { 43 | assertThat(" String \t libraries are \n\n\t fun\n! ".collapseWhitespace() == "String libraries are fun !") 44 | } 45 | 46 | func testContains() { 47 | assertThat("foobar".contains("foo") == true) 48 | assertThat("foobar".contains("ba") == true) 49 | assertThat("foobar".contains("something") == false) 50 | } 51 | 52 | func testCount() { 53 | assertThat("hi hi ho hey hihey".count("hi") == 3) 54 | } 55 | 56 | func testEndsWith() { 57 | assertThat("hello world".endsWith("world") == true) 58 | assertThat("hello world".endsWith("foo") == false) 59 | } 60 | 61 | func testEnsureLeft() { 62 | assertThat("/subdir".ensureLeft("/") == "/subdir") 63 | assertThat("subdir".ensureLeft("/") == "/subdir") 64 | } 65 | 66 | func testEnsureRight() { 67 | assertThat("subdir/".ensureRight("/") == "subdir/") 68 | assertThat("subdir".ensureRight("/") == "subdir/") 69 | } 70 | 71 | func testIndexOf() { 72 | assertThat("hello".indexOf("hell"), presentAnd(equalTo(0))) 73 | assertThat("hello".indexOf("lo"), presentAnd(equalTo(3))) 74 | assertThat("hello".indexOf("world"), nilValue()) 75 | } 76 | 77 | func testInitials() { 78 | assertThat("First".initials() == "F") 79 | assertThat("First Last".initials() == "FL") 80 | assertThat("First Middle1 Middle2 Middle3 Last".initials() == "FMMML") 81 | } 82 | 83 | func testInitialsFirstAndLast() { 84 | assertThat("First Last".initialsFirstAndLast() == "FL") 85 | assertThat("First Middle1 Middle2 Middle3 Last".initialsFirstAndLast() == "FL") 86 | } 87 | 88 | func testIsAlpha() { 89 | assertThat("fdafaf3".isAlpha() == false) 90 | assertThat("afaf".isAlpha() == true) 91 | assertThat("dfdf--dfd".isAlpha() == false) 92 | } 93 | 94 | func testIsAlphaNumeric() { 95 | assertThat("afaf35353afaf".isAlphaNumeric() == true) 96 | assertThat("FFFF99fff".isAlphaNumeric() == true) 97 | assertThat("99".isAlphaNumeric() == true) 98 | assertThat("afff".isAlphaNumeric() == true) 99 | assertThat("-33".isAlphaNumeric() == false) 100 | assertThat("aaff..".isAlphaNumeric() == false) 101 | } 102 | 103 | func testIsEmpty() { 104 | assertThat("".isEmpty() == true) 105 | assertThat(" ".isEmpty() == true) 106 | assertThat("\t\t\t ".isEmpty() == true) 107 | assertThat("\n\n".isEmpty() == true) 108 | assertThat("helo".isEmpty() == false) 109 | } 110 | 111 | func testIsNumeric() { 112 | assertThat("abc".isNumeric() == false) 113 | assertThat("123a".isNumeric() == false) 114 | assertThat("1".isNumeric() == true) 115 | assertThat("22".isNumeric() == true) 116 | assertThat("33.0".isNumeric() == true) 117 | assertThat("-63.0".isNumeric() == true) 118 | } 119 | 120 | func testJoin() { 121 | assertThat(",".join([1,2,3]) == "1,2,3") 122 | assertThat(",".join([]) == "") 123 | assertThat(",".join(["a","b","c"]) == "a,b,c") 124 | assertThat("! ".join(["hey","who are you?"]) == "hey! who are you?") 125 | } 126 | 127 | func testLatinize() { 128 | assertThat("šÜįéïöç".latinize() == "sUieioc") 129 | assertThat("crème brûlée".latinize() == "creme brulee") 130 | } 131 | 132 | func testLines() { 133 | assertThat("test".lines() == ["test"]) 134 | assertThat("test\nsentence".lines() == ["test", "sentence"]) 135 | assertThat("test \nsentence".lines() == ["test ", "sentence"]) 136 | //Test Carriage return instead of just newlines 137 | assertThat("test\rsentence".lines() == ["test", "sentence"]) 138 | } 139 | 140 | func testPad() { 141 | assertThat("hello".pad(2) == " hello ") 142 | assertThat("hello".pad(1, "\t") == "\thello\t") 143 | } 144 | 145 | func testPadLeft() { 146 | assertThat("hello".padLeft(10) == " hello") 147 | assertThat("what?".padLeft(2, "!") == "!!what?") 148 | } 149 | 150 | func testPadRight() { 151 | assertThat("hello".padRight(10) == "hello ") 152 | assertThat("hello".padRight(2, "!") == "hello!!") 153 | } 154 | 155 | func testStartsWith() { 156 | assertThat("hello world".startsWith("hello") == true) 157 | assertThat("hello world".startsWith("foo") == false) 158 | } 159 | 160 | func testSplit() { 161 | assertThat("hello world".split(" ")[0] == "hello") 162 | assertThat("hello world".split(" ")[1] == "world") 163 | assertThat("helloworld".split(" ")[0] == "helloworld") 164 | } 165 | 166 | func testTimes() { 167 | assertThat("hi".times(3) == "hihihi") 168 | assertThat(" ".times(10) == " ") 169 | } 170 | 171 | func testTrimmedLeft() { 172 | assertThat(" How are you? ".trimmedLeft() == "How are you? ") 173 | } 174 | 175 | func testTrimmedRight() { 176 | assertThat(" How are you? ".trimmedRight() == " How are you?") 177 | } 178 | 179 | func testTrimmed() { 180 | assertThat(" How are you? ".trimmed() == "How are you?") 181 | //Added per Issue #9 - https://github.com/amayne/SwiftString/issues/9 182 | assertThat(" ".trimmed().characters.count == 0) 183 | assertThat(" ".trimmed().characters.count == 0) 184 | assertThat(" ".trimmed().characters.count == 0) 185 | } 186 | 187 | func testToBool() { 188 | assertThat("asdwads".toBool(), nilValue()) 189 | assertThat("true".toBool(), presentAnd(equalTo(true))) 190 | assertThat("false".toBool(), presentAnd(equalTo(false))) 191 | } 192 | 193 | func testToFloat() { 194 | assertThat("asdwads".toFloat(), nilValue()) 195 | assertThat("2.00".toFloat(), presentAnd(equalTo(2.0))) 196 | assertThat("2".toFloat(), presentAnd(equalTo(2.0))) 197 | } 198 | 199 | func testToInt() { 200 | assertThat("asdwads".toInt(), nilValue()) 201 | assertThat("2.00".toInt(), presentAnd(equalTo(2))) 202 | assertThat("2".toInt(), presentAnd(equalTo(2))) 203 | } 204 | 205 | func testToDate() { 206 | assertThat("asdwads".toDate(), nilValue()) 207 | 208 | let calendar = NSCalendar.currentCalendar() 209 | let components = calendar.components([.Day , .Month , .Year], fromDate: "2014-06-03".toDate()!) 210 | 211 | assertThat(components.year == 2014) 212 | assertThat(components.month == 6) 213 | assertThat(components.day == 3) 214 | } 215 | 216 | func testToDateTime() { 217 | assertThat("asdwads".toDateTime(), nilValue()) 218 | let calendar = NSCalendar.currentCalendar() 219 | let components = calendar.components([.Day , .Month , .Year, .Hour, .Minute, .Second], fromDate: "2014-06-03 13:15:01".toDateTime()!) 220 | 221 | assertThat(components.year == 2014) 222 | assertThat(components.month == 6) 223 | assertThat(components.day == 3) 224 | assertThat(components.hour == 13) 225 | assertThat(components.minute == 15) 226 | assertThat(components.second == 1) 227 | } 228 | 229 | func testToDouble() { 230 | assertThat("asdwads".toDouble(), nilValue()) 231 | assertThat("2.00".toDouble(), presentAnd(equalTo(2.0))) 232 | assertThat("2".toDouble(), presentAnd(equalTo(2.0))) 233 | } 234 | 235 | func testSlugify() { 236 | assertThat("Global Thermonuclear Warfare".slugify() == "global-thermonuclear-warfare") 237 | assertThat("Global Thermonuclear Warfare".slugify(withSeparator: "_") == "global_thermonuclear_warfare") 238 | assertThat("Crème brûlée".slugify() == "creme-brulee") 239 | } 240 | 241 | func testStripPunctuation() { 242 | assertThat("My, st[ring] *full* of %punct)".stripPunctuation() == "My string full of punct") 243 | } 244 | 245 | func testSubstring() { 246 | let subject = "hello world" 247 | assertThat(subject.substring(0, length: 1) == "h") 248 | assertThat(subject.substring(0, length: 11) == "hello world") 249 | } 250 | 251 | func testSubscripts() { 252 | let subject = "hello world" 253 | assertThat(subject[0...1] == "he") 254 | assertThat(subject[0..<1] == "h") 255 | assertThat(subject[0] == "h") 256 | assertThat(subject[0...10] == "hello world") 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Andrew Mayne 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | 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 OR 14 | 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 FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amayne/SwiftString/3ac8ced3b3484d36fc900a3e13778d58e082d42d/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amayne/SwiftString/3ac8ced3b3484d36fc900a3e13778d58e082d42d/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/String+HTML.swift: -------------------------------------------------------------------------------- 1 | // adapted from https://gist.github.com/mwaterfall/25b4a6a06dc3309d9555 2 | 3 | public extension String { 4 | 5 | private struct HTMLEntities { 6 | static let characterEntities : [String: Character] = [ 7 | 8 | // XML predefined entities: 9 | """ : "\"", 10 | "&" : "&", 11 | "'" : "'", 12 | "<" : "<", 13 | ">" : ">", 14 | 15 | // HTML character entity references: 16 | " " : "\u{00A0}", 17 | "¡" : "\u{00A1}", 18 | "¢" : "\u{00A2}", 19 | "£" : "\u{00A3}", 20 | "¤" : "\u{00A4}", 21 | "¥" : "\u{00A5}", 22 | "¦" : "\u{00A6}", 23 | "§" : "\u{00A7}", 24 | "¨" : "\u{00A8}", 25 | "©" : "\u{00A9}", 26 | "ª" : "\u{00AA}", 27 | "«" : "\u{00AB}", 28 | "¬" : "\u{00AC}", 29 | "­" : "\u{00AD}", 30 | "®" : "\u{00AE}", 31 | "¯" : "\u{00AF}", 32 | "°" : "\u{00B0}", 33 | "±" : "\u{00B1}", 34 | "²" : "\u{00B2}", 35 | "³" : "\u{00B3}", 36 | "´" : "\u{00B4}", 37 | "µ" : "\u{00B5}", 38 | "¶" : "\u{00B6}", 39 | "·" : "\u{00B7}", 40 | "¸" : "\u{00B8}", 41 | "¹" : "\u{00B9}", 42 | "º" : "\u{00BA}", 43 | "»" : "\u{00BB}", 44 | "¼" : "\u{00BC}", 45 | "½" : "\u{00BD}", 46 | "¾" : "\u{00BE}", 47 | "¿" : "\u{00BF}", 48 | "À" : "\u{00C0}", 49 | "Á" : "\u{00C1}", 50 | "Â" : "\u{00C2}", 51 | "Ã" : "\u{00C3}", 52 | "Ä" : "\u{00C4}", 53 | "Å" : "\u{00C5}", 54 | "Æ" : "\u{00C6}", 55 | "Ç" : "\u{00C7}", 56 | "È" : "\u{00C8}", 57 | "É" : "\u{00C9}", 58 | "Ê" : "\u{00CA}", 59 | "Ë" : "\u{00CB}", 60 | "Ì" : "\u{00CC}", 61 | "Í" : "\u{00CD}", 62 | "Î" : "\u{00CE}", 63 | "Ï" : "\u{00CF}", 64 | "Ð" : "\u{00D0}", 65 | "Ñ" : "\u{00D1}", 66 | "Ò" : "\u{00D2}", 67 | "Ó" : "\u{00D3}", 68 | "Ô" : "\u{00D4}", 69 | "Õ" : "\u{00D5}", 70 | "Ö" : "\u{00D6}", 71 | "×" : "\u{00D7}", 72 | "Ø" : "\u{00D8}", 73 | "Ù" : "\u{00D9}", 74 | "Ú" : "\u{00DA}", 75 | "Û" : "\u{00DB}", 76 | "Ü" : "\u{00DC}", 77 | "Ý" : "\u{00DD}", 78 | "Þ" : "\u{00DE}", 79 | "ß" : "\u{00DF}", 80 | "à" : "\u{00E0}", 81 | "á" : "\u{00E1}", 82 | "â" : "\u{00E2}", 83 | "ã" : "\u{00E3}", 84 | "ä" : "\u{00E4}", 85 | "å" : "\u{00E5}", 86 | "æ" : "\u{00E6}", 87 | "ç" : "\u{00E7}", 88 | "è" : "\u{00E8}", 89 | "é" : "\u{00E9}", 90 | "ê" : "\u{00EA}", 91 | "ë" : "\u{00EB}", 92 | "ì" : "\u{00EC}", 93 | "í" : "\u{00ED}", 94 | "î" : "\u{00EE}", 95 | "ï" : "\u{00EF}", 96 | "ð" : "\u{00F0}", 97 | "ñ" : "\u{00F1}", 98 | "ò" : "\u{00F2}", 99 | "ó" : "\u{00F3}", 100 | "ô" : "\u{00F4}", 101 | "õ" : "\u{00F5}", 102 | "ö" : "\u{00F6}", 103 | "÷" : "\u{00F7}", 104 | "ø" : "\u{00F8}", 105 | "ù" : "\u{00F9}", 106 | "ú" : "\u{00FA}", 107 | "û" : "\u{00FB}", 108 | "ü" : "\u{00FC}", 109 | "ý" : "\u{00FD}", 110 | "þ" : "\u{00FE}", 111 | "ÿ" : "\u{00FF}", 112 | "Œ" : "\u{0152}", 113 | "œ" : "\u{0153}", 114 | "Š" : "\u{0160}", 115 | "š" : "\u{0161}", 116 | "Ÿ" : "\u{0178}", 117 | "ƒ" : "\u{0192}", 118 | "ˆ" : "\u{02C6}", 119 | "˜" : "\u{02DC}", 120 | "Α" : "\u{0391}", 121 | "Β" : "\u{0392}", 122 | "Γ" : "\u{0393}", 123 | "Δ" : "\u{0394}", 124 | "Ε" : "\u{0395}", 125 | "Ζ" : "\u{0396}", 126 | "Η" : "\u{0397}", 127 | "Θ" : "\u{0398}", 128 | "Ι" : "\u{0399}", 129 | "Κ" : "\u{039A}", 130 | "Λ" : "\u{039B}", 131 | "Μ" : "\u{039C}", 132 | "Ν" : "\u{039D}", 133 | "Ξ" : "\u{039E}", 134 | "Ο" : "\u{039F}", 135 | "Π" : "\u{03A0}", 136 | "Ρ" : "\u{03A1}", 137 | "Σ" : "\u{03A3}", 138 | "Τ" : "\u{03A4}", 139 | "Υ" : "\u{03A5}", 140 | "Φ" : "\u{03A6}", 141 | "Χ" : "\u{03A7}", 142 | "Ψ" : "\u{03A8}", 143 | "Ω" : "\u{03A9}", 144 | "α" : "\u{03B1}", 145 | "β" : "\u{03B2}", 146 | "γ" : "\u{03B3}", 147 | "δ" : "\u{03B4}", 148 | "ε" : "\u{03B5}", 149 | "ζ" : "\u{03B6}", 150 | "η" : "\u{03B7}", 151 | "θ" : "\u{03B8}", 152 | "ι" : "\u{03B9}", 153 | "κ" : "\u{03BA}", 154 | "λ" : "\u{03BB}", 155 | "μ" : "\u{03BC}", 156 | "ν" : "\u{03BD}", 157 | "ξ" : "\u{03BE}", 158 | "ο" : "\u{03BF}", 159 | "π" : "\u{03C0}", 160 | "ρ" : "\u{03C1}", 161 | "ς" : "\u{03C2}", 162 | "σ" : "\u{03C3}", 163 | "τ" : "\u{03C4}", 164 | "υ" : "\u{03C5}", 165 | "φ" : "\u{03C6}", 166 | "χ" : "\u{03C7}", 167 | "ψ" : "\u{03C8}", 168 | "ω" : "\u{03C9}", 169 | "ϑ" : "\u{03D1}", 170 | "ϒ" : "\u{03D2}", 171 | "ϖ" : "\u{03D6}", 172 | " " : "\u{2002}", 173 | " " : "\u{2003}", 174 | " " : "\u{2009}", 175 | "‌" : "\u{200C}", 176 | "‍" : "\u{200D}", 177 | "‎" : "\u{200E}", 178 | "‏" : "\u{200F}", 179 | "–" : "\u{2013}", 180 | "—" : "\u{2014}", 181 | "‘" : "\u{2018}", 182 | "’" : "\u{2019}", 183 | "‚" : "\u{201A}", 184 | "“" : "\u{201C}", 185 | "”" : "\u{201D}", 186 | "„" : "\u{201E}", 187 | "†" : "\u{2020}", 188 | "‡" : "\u{2021}", 189 | "•" : "\u{2022}", 190 | "…" : "\u{2026}", 191 | "‰" : "\u{2030}", 192 | "′" : "\u{2032}", 193 | "″" : "\u{2033}", 194 | "‹" : "\u{2039}", 195 | "›" : "\u{203A}", 196 | "‾" : "\u{203E}", 197 | "⁄" : "\u{2044}", 198 | "€" : "\u{20AC}", 199 | "ℑ" : "\u{2111}", 200 | "℘" : "\u{2118}", 201 | "ℜ" : "\u{211C}", 202 | "™" : "\u{2122}", 203 | "ℵ" : "\u{2135}", 204 | "←" : "\u{2190}", 205 | "↑" : "\u{2191}", 206 | "→" : "\u{2192}", 207 | "↓" : "\u{2193}", 208 | "↔" : "\u{2194}", 209 | "↵" : "\u{21B5}", 210 | "⇐" : "\u{21D0}", 211 | "⇑" : "\u{21D1}", 212 | "⇒" : "\u{21D2}", 213 | "⇓" : "\u{21D3}", 214 | "⇔" : "\u{21D4}", 215 | "∀" : "\u{2200}", 216 | "∂" : "\u{2202}", 217 | "∃" : "\u{2203}", 218 | "∅" : "\u{2205}", 219 | "∇" : "\u{2207}", 220 | "∈" : "\u{2208}", 221 | "∉" : "\u{2209}", 222 | "∋" : "\u{220B}", 223 | "∏" : "\u{220F}", 224 | "∑" : "\u{2211}", 225 | "−" : "\u{2212}", 226 | "∗" : "\u{2217}", 227 | "√" : "\u{221A}", 228 | "∝" : "\u{221D}", 229 | "∞" : "\u{221E}", 230 | "∠" : "\u{2220}", 231 | "∧" : "\u{2227}", 232 | "∨" : "\u{2228}", 233 | "∩" : "\u{2229}", 234 | "∪" : "\u{222A}", 235 | "∫" : "\u{222B}", 236 | "∴" : "\u{2234}", 237 | "∼" : "\u{223C}", 238 | "≅" : "\u{2245}", 239 | "≈" : "\u{2248}", 240 | "≠" : "\u{2260}", 241 | "≡" : "\u{2261}", 242 | "≤" : "\u{2264}", 243 | "≥" : "\u{2265}", 244 | "⊂" : "\u{2282}", 245 | "⊃" : "\u{2283}", 246 | "⊄" : "\u{2284}", 247 | "⊆" : "\u{2286}", 248 | "⊇" : "\u{2287}", 249 | "⊕" : "\u{2295}", 250 | "⊗" : "\u{2297}", 251 | "⊥" : "\u{22A5}", 252 | "⋅" : "\u{22C5}", 253 | "⌈" : "\u{2308}", 254 | "⌉" : "\u{2309}", 255 | "⌊" : "\u{230A}", 256 | "⌋" : "\u{230B}", 257 | "⟨" : "\u{2329}", 258 | "⟩" : "\u{232A}", 259 | "◊" : "\u{25CA}", 260 | "♠" : "\u{2660}", 261 | "♣" : "\u{2663}", 262 | "♥" : "\u{2665}", 263 | "♦" : "\u{2666}", 264 | ] 265 | } 266 | 267 | // Convert the number in the string to the corresponding 268 | // Unicode character, e.g. 269 | // decodeNumeric("64", 10) --> "@" 270 | // decodeNumeric("20ac", 16) --> "€" 271 | private func decodeNumeric(string : String, base : Int32) -> Character? { 272 | let code = UInt32(strtoul(string, nil, base)) 273 | return Character(UnicodeScalar(code)) 274 | } 275 | 276 | // Decode the HTML character entity to the corresponding 277 | // Unicode character, return `nil` for invalid input. 278 | // decode("@") --> "@" 279 | // decode("€") --> "€" 280 | // decode("<") --> "<" 281 | // decode("&foo;") --> nil 282 | private func decode(entity : String) -> Character? { 283 | if entity.hasPrefix("&#x") || entity.hasPrefix("&#X"){ 284 | return decodeNumeric(entity.substringFromIndex(entity.startIndex.advancedBy(3)), base: 16) 285 | } else if entity.hasPrefix("&#") { 286 | return decodeNumeric(entity.substringFromIndex(entity.startIndex.advancedBy(2)), base: 10) 287 | } else { 288 | return HTMLEntities.characterEntities[entity] 289 | } 290 | } 291 | 292 | 293 | /// Returns a new string made by replacing in the `String` 294 | /// all HTML character entity references with the corresponding 295 | /// character. 296 | func decodeHTML() -> String { 297 | var result = "" 298 | var position = startIndex 299 | 300 | // Find the next '&' and copy the characters preceding it to `result`: 301 | while let ampRange = self.rangeOfString("&", range: position ..< endIndex) { 302 | result.appendContentsOf(self[position ..< ampRange.startIndex]) 303 | position = ampRange.startIndex 304 | 305 | // Find the next ';' and copy everything from '&' to ';' into `entity` 306 | if let semiRange = self.rangeOfString(";", range: position ..< endIndex) { 307 | let entity = self[position ..< semiRange.endIndex] 308 | position = semiRange.endIndex 309 | 310 | if let decoded = decode(entity) { 311 | // Replace by decoded character: 312 | result.append(decoded) 313 | } else { 314 | // Invalid entity, copy verbatim: 315 | result.appendContentsOf(entity) 316 | } 317 | } else { 318 | // No matching ';'. 319 | break 320 | } 321 | } 322 | // Copy remaining characters to `result`: 323 | result.appendContentsOf(self[position ..< endIndex]) 324 | return result 325 | } 326 | } -------------------------------------------------------------------------------- /Pod/Classes/StringExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftString.swift 3 | // SwiftString 4 | // 5 | // Created by Andrew Mayne on 30/01/2016. 6 | // Copyright © 2016 Red Brick Labs. All rights reserved. 7 | // 8 | 9 | public extension String { 10 | 11 | /// Finds the string between two bookend strings if it can be found. 12 | /// 13 | /// - parameter left: The left bookend 14 | /// - parameter right: The right bookend 15 | /// 16 | /// - returns: The string between the two bookends, or nil if the bookends cannot be found, the bookends are the same or appear contiguously. 17 | func between(left: String, _ right: String) -> String? { 18 | guard 19 | let leftRange = rangeOfString(left), rightRange = rangeOfString(right, options: .BackwardsSearch) 20 | where left != right && leftRange.endIndex != rightRange.startIndex 21 | else { return nil } 22 | 23 | return self[leftRange.endIndex...rightRange.startIndex.predecessor()] 24 | 25 | } 26 | 27 | // https://gist.github.com/stevenschobert/540dd33e828461916c11 28 | func camelize() -> String { 29 | let source = clean(with: " ", allOf: "-", "_") 30 | if source.characters.contains(" ") { 31 | let first = source.substringToIndex(source.startIndex.advancedBy(1)) 32 | let cammel = NSString(format: "%@", (source as NSString).capitalizedString.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)) as String 33 | let rest = String(cammel.characters.dropFirst()) 34 | return "\(first)\(rest)" 35 | } else { 36 | let first = (source as NSString).lowercaseString.substringToIndex(source.startIndex.advancedBy(1)) 37 | let rest = String(source.characters.dropFirst()) 38 | return "\(first)\(rest)" 39 | } 40 | } 41 | 42 | func capitalize() -> String { 43 | return capitalizedString 44 | } 45 | 46 | func contains(substring: String) -> Bool { 47 | return rangeOfString(substring) != nil 48 | } 49 | 50 | func chompLeft(prefix: String) -> String { 51 | if let prefixRange = rangeOfString(prefix) { 52 | if prefixRange.endIndex >= endIndex { 53 | return self[startIndex.. String { 62 | if let suffixRange = rangeOfString(suffix, options: .BackwardsSearch) { 63 | if suffixRange.endIndex >= endIndex { 64 | return self[startIndex.. String { 73 | let components = componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).filter { !$0.isEmpty } 74 | return components.joinWithSeparator(" ") 75 | } 76 | 77 | func clean(with with: String, allOf: String...) -> String { 78 | var string = self 79 | for target in allOf { 80 | string = string.stringByReplacingOccurrencesOfString(target, withString: with) 81 | } 82 | return string 83 | } 84 | 85 | func count(substring: String) -> Int { 86 | return componentsSeparatedByString(substring).count-1 87 | } 88 | 89 | func endsWith(suffix: String) -> Bool { 90 | return hasSuffix(suffix) 91 | } 92 | 93 | func ensureLeft(prefix: String) -> String { 94 | if startsWith(prefix) { 95 | return self 96 | } else { 97 | return "\(prefix)\(self)" 98 | } 99 | } 100 | 101 | func ensureRight(suffix: String) -> String { 102 | if endsWith(suffix) { 103 | return self 104 | } else { 105 | return "\(self)\(suffix)" 106 | } 107 | } 108 | 109 | func indexOf(substring: String) -> Int? { 110 | if let range = rangeOfString(substring) { 111 | return startIndex.distanceTo(range.startIndex) 112 | } 113 | return nil 114 | } 115 | 116 | func initials() -> String { 117 | let words = self.componentsSeparatedByString(" ") 118 | return words.reduce(""){$0 + $1[0...0]} 119 | } 120 | 121 | func initialsFirstAndLast() -> String { 122 | let words = self.componentsSeparatedByString(" ") 123 | return words.reduce("") { ($0 == "" ? "" : $0[0...0]) + $1[0...0]} 124 | } 125 | 126 | func isAlpha() -> Bool { 127 | for chr in characters { 128 | if (!(chr >= "a" && chr <= "z") && !(chr >= "A" && chr <= "Z") ) { 129 | return false 130 | } 131 | } 132 | return true 133 | } 134 | 135 | func isAlphaNumeric() -> Bool { 136 | let alphaNumeric = NSCharacterSet.alphanumericCharacterSet() 137 | return componentsSeparatedByCharactersInSet(alphaNumeric).joinWithSeparator("").length == 0 138 | } 139 | 140 | func isEmpty() -> Bool { 141 | return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).length == 0 142 | } 143 | 144 | func isNumeric() -> Bool { 145 | if let _ = defaultNumberFormatter().numberFromString(self) { 146 | return true 147 | } 148 | return false 149 | } 150 | 151 | func join(elements: S) -> String { 152 | return elements.map{String($0)}.joinWithSeparator(self) 153 | } 154 | 155 | func latinize() -> String { 156 | return self.stringByFoldingWithOptions(.DiacriticInsensitiveSearch, locale: NSLocale.currentLocale()) 157 | } 158 | 159 | func lines() -> [String] { 160 | return self.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) 161 | } 162 | 163 | var length: Int { 164 | get { 165 | return self.characters.count 166 | } 167 | } 168 | 169 | func pad(n: Int, _ string: String = " ") -> String { 170 | return "".join([string.times(n), self, string.times(n)]) 171 | } 172 | 173 | func padLeft(n: Int, _ string: String = " ") -> String { 174 | return "".join([string.times(n), self]) 175 | } 176 | 177 | func padRight(n: Int, _ string: String = " ") -> String { 178 | return "".join([self, string.times(n)]) 179 | } 180 | 181 | func slugify(withSeparator separator: Character = "-") -> String { 182 | let slugCharacterSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\(separator)") 183 | return latinize() 184 | .lowercaseString 185 | .componentsSeparatedByCharactersInSet(slugCharacterSet.invertedSet) 186 | .filter { $0 != "" } 187 | .joinWithSeparator(String(separator)) 188 | } 189 | 190 | func split(separator: Character) -> [String] { 191 | return characters.split{$0 == separator}.map(String.init) 192 | } 193 | 194 | func startsWith(prefix: String) -> Bool { 195 | return hasPrefix(prefix) 196 | } 197 | 198 | func stripPunctuation() -> String { 199 | return componentsSeparatedByCharactersInSet(.punctuationCharacterSet()) 200 | .joinWithSeparator("") 201 | .componentsSeparatedByString(" ") 202 | .filter { $0 != "" } 203 | .joinWithSeparator(" ") 204 | } 205 | 206 | func times(n: Int) -> String { 207 | return (0.. Float? { 211 | if let number = defaultNumberFormatter().numberFromString(self) { 212 | return number.floatValue 213 | } 214 | return nil 215 | } 216 | 217 | func toInt() -> Int? { 218 | if let number = defaultNumberFormatter().numberFromString(self) { 219 | return number.integerValue 220 | } 221 | return nil 222 | } 223 | 224 | func toDouble(locale: NSLocale = NSLocale.systemLocale()) -> Double? { 225 | let nf = localeNumberFormatter(locale) 226 | 227 | if let number = nf.numberFromString(self) { 228 | return number.doubleValue 229 | } 230 | return nil 231 | } 232 | 233 | func toBool() -> Bool? { 234 | let trimmed = self.trimmed().lowercaseString 235 | if trimmed == "true" || trimmed == "false" { 236 | return (trimmed as NSString).boolValue 237 | } 238 | return nil 239 | } 240 | 241 | func toDate(format: String = "yyyy-MM-dd") -> NSDate? { 242 | return dateFormatter(format).dateFromString(self) 243 | } 244 | 245 | func toDateTime(format: String = "yyyy-MM-dd HH:mm:ss") -> NSDate? { 246 | return toDate(format) 247 | } 248 | 249 | func trimmedLeft() -> String { 250 | if let range = rangeOfCharacterFromSet(NSCharacterSet.whitespaceAndNewlineCharacterSet().invertedSet) { 251 | return self[range.startIndex.. String { 257 | if let range = rangeOfCharacterFromSet(NSCharacterSet.whitespaceAndNewlineCharacterSet().invertedSet, options: NSStringCompareOptions.BackwardsSearch) { 258 | return self[startIndex.. String { 264 | return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 265 | } 266 | 267 | subscript(r: Range) -> String { 268 | get { 269 | let startIndex = self.startIndex.advancedBy(r.startIndex) 270 | let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex) 271 | return self[startIndex.. String { 276 | let start = self.startIndex.advancedBy(startIndex) 277 | let end = self.startIndex.advancedBy(startIndex + length) 278 | return self[start.. Character { 282 | get { 283 | let index = self.startIndex.advancedBy(i) 284 | return self[index] 285 | } 286 | } 287 | } 288 | 289 | private enum ThreadLocalIdentifier { 290 | case DateFormatter(String) 291 | 292 | case DefaultNumberFormatter 293 | case LocaleNumberFormatter(NSLocale) 294 | 295 | var objcDictKey: String { 296 | switch self { 297 | case .DateFormatter(let format): 298 | return "SS\(self)\(format)" 299 | case .LocaleNumberFormatter(let l): 300 | return "SS\(self)\(l.localeIdentifier)" 301 | default: 302 | return "SS\(self)" 303 | } 304 | } 305 | } 306 | 307 | private func threadLocalInstance(identifier: ThreadLocalIdentifier, @autoclosure initialValue: () -> T) -> T { 308 | let storage = NSThread.currentThread().threadDictionary 309 | let k = identifier.objcDictKey 310 | 311 | let instance: T = storage[k] as? T ?? initialValue() 312 | if storage[k] == nil { 313 | storage[k] = instance 314 | } 315 | 316 | return instance 317 | } 318 | 319 | private func dateFormatter(format: String) -> NSDateFormatter { 320 | return threadLocalInstance(.DateFormatter(format), initialValue: { 321 | let df = NSDateFormatter() 322 | df.dateFormat = format 323 | return df 324 | }()) 325 | } 326 | 327 | private func defaultNumberFormatter() -> NSNumberFormatter { 328 | return threadLocalInstance(.DefaultNumberFormatter, initialValue: NSNumberFormatter()) 329 | } 330 | 331 | private func localeNumberFormatter(locale: NSLocale) -> NSNumberFormatter { 332 | return threadLocalInstance(.LocaleNumberFormatter(locale), initialValue: { 333 | let nf = NSNumberFormatter() 334 | nf.locale = locale 335 | return nf 336 | }()) 337 | } 338 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftString 2 | 3 | [![CI Status](http://img.shields.io/travis/amayne/SwiftString.svg?style=flat)](https://travis-ci.org/amayne/SwiftString) 4 | [![Version](https://img.shields.io/cocoapods/v/SwiftString.svg?style=flat)](http://cocoapods.org/pods/SwiftString) 5 | [![License](https://img.shields.io/cocoapods/l/SwiftString.svg?style=flat)](http://cocoapods.org/pods/SwiftString) 6 | [![Platform](https://img.shields.io/cocoapods/p/SwiftString.svg?style=flat)](http://cocoapods.org/pods/SwiftString) 7 | [![Swift-2.1](http://img.shields.io/badge/Swift-2.1-blue.svg)]() 8 | 9 | SwiftString is a lightweight string extension for Swift. 10 | This library was motivated by having to search StackOverflow for common string operations, 11 | and wanting them to be in one place with test coverage. 12 | 13 | ## Installation 14 | 15 | SwiftString is available through [CocoaPods](http://cocoapods.org). To install 16 | it, simply add the following line to your Podfile: 17 | 18 | ```ruby 19 | pod "SwiftString" 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```swift 25 | import SwiftString 26 | ``` 27 | 28 | ## Methods 29 | 30 | **between(left, right)** 31 | ```swift 32 | "foo".between("", "") // "foo" 33 | "foo".between("", "") // "foo" 34 | "foo".between("", "") // nil 35 | "Some strings } are very {weird}, dont you think?".between("{", "}") // "weird" 36 | "".between("", "") // nil 37 | "foo".between("", "") // nil 38 | ``` 39 | 40 | **camelize()** 41 | ```swift 42 | "os version".camelize() // "osVersion" 43 | "HelloWorld".camelize() // "helloWorld" 44 | "someword With Characters".camelize() // "somewordWithCharacters" 45 | "data_rate".camelize() // "dataRate" 46 | "background-color".camelize() // "backgroundColor" 47 | ``` 48 | 49 | 50 | **capitalize()** 51 | ```swift 52 | "hello world".capitalize() // "Hello World" 53 | ``` 54 | 55 | **chompLeft(string)** 56 | ```swift 57 | "foobar".chompLeft("foo") // "bar" 58 | "foobar".chompLeft("bar") // "foo" 59 | ``` 60 | 61 | **chompRight(string)** 62 | ```swift 63 | "foobar".chompRight("bar") // "foo" 64 | "foobar".chompRight("foo") // "bar" 65 | ``` 66 | 67 | **collapseWhitespace()** 68 | ```swift 69 | " String \t libraries are \n\n\t fun\n! ".collapseWhitespace() // "String libraries are fun !") 70 | ``` 71 | 72 | **contains(substring)** 73 | ```swift 74 | "foobar".contains("foo") // true 75 | "foobar".contains("bar") // true 76 | "foobar".contains("something") // false 77 | ``` 78 | 79 | **count(string)** 80 | ```swift 81 | "hi hi ho hey hihey".count("hi") // 3 82 | ``` 83 | 84 | **decodeHTML()** 85 | ```swift 86 | "The Weekend ‘King Of The Fall’".decodeHTML() // "The Weekend ‘King Of The Fall’" 87 | " 4 < 5 & 3 > 2 . Price: 12 €. @ ".decodeHTML() // " 4 < 5 & 3 > 2 . Price: 12 €. @ " 88 | "this is so "good"".decodeHTML() // "this is so \"good\"" 89 | ``` 90 | 91 | **endsWith(suffix)** 92 | ```swift 93 | "hello world".endsWith("world") // true 94 | "hello world".endsWith("foo") // false 95 | ``` 96 | 97 | **ensureLeft(prefix)** 98 | ```swift 99 | "/subdir".ensureLeft("/") // "/subdir" 100 | "subdir".ensureLeft("/") // "/subdir" 101 | ``` 102 | 103 | **ensureRight(suffix)** 104 | ```swift 105 | "subdir/".ensureRight("/") // "subdir/" 106 | "subdir".ensureRight("/") // "subdir/" 107 | ``` 108 | 109 | **indexOf(substring)** 110 | ```swift 111 | "hello".indexOf("hell"), // 0 112 | "hello".indexOf("lo"), // 3 113 | "hello".indexOf("world") // nil 114 | ``` 115 | 116 | **initials()** 117 | ```swift 118 | "First".initials(), // "F" 119 | "First Last".initials(), // "FL" 120 | "First Middle1 Middle2 Middle3 Last".initials() // "FMMML" 121 | ``` 122 | 123 | **initialsFirstAndLast()** 124 | ```swift 125 | "First Last".initialsFirstAndLast(), // "FL" 126 | "First Middle1 Middle2 Middle3 Last".initialsFirstAndLast() // "FL" 127 | ``` 128 | 129 | **isAlpha()** 130 | ```swift 131 | "fdafaf3".isAlpha() // false 132 | "afaf".isAlpha() // true 133 | "dfdf--dfd".isAlpha() // false 134 | ``` 135 | 136 | **isAlphaNumeric()** 137 | ```swift 138 | "afaf35353afaf".isAlphaNumeric() // true 139 | "FFFF99fff".isAlphaNumeric() // true 140 | "99".isAlphaNumeric() // true 141 | "afff".isAlphaNumeric() // true 142 | "-33".isAlphaNumeric() // false 143 | "aaff..".isAlphaNumeric() // false 144 | ``` 145 | 146 | **isEmpty()** 147 | ```swift 148 | " ".isEmpty() // true 149 | "\t\t\t ".isEmpty() // true 150 | "\n\n".isEmpty() // true 151 | "helo".isEmpty() // false 152 | ``` 153 | 154 | **isNumeric()** 155 | ```swift 156 | "abc".isNumeric() // false 157 | "123a".isNumeric() // false 158 | "1".isNumeric() // true 159 | "22".isNumeric() // true 160 | "33.0".isNumeric() // true 161 | "-63.0".isNumeric() // true 162 | ``` 163 | 164 | **join(sequence)** 165 | ```swift 166 | ",".join([1,2,3]) // "1,2,3" 167 | ",".join([]) // "" 168 | ",".join(["a","b","c"]) // "a,b,c" 169 | "! ".join(["hey","who are you?"]) // "hey! who are you?" 170 | ``` 171 | 172 | **latinize()** 173 | ```swift 174 | "šÜįéïöç".latinize() // "sUieioc" 175 | "crème brûlée".latinize() // "creme brulee" 176 | ``` 177 | 178 | **lines()** 179 | ```swift 180 | "test".lines() // ["test"] 181 | "test\nsentence".lines() // ["test", "sentence"] 182 | "test \nsentence".lines() // ["test ", "sentence"] 183 | ``` 184 | 185 | **pad(n, string)** 186 | ```swift 187 | "hello".pad(2) // " hello " 188 | "hello".pad(1, "\t") // "\thello\t" 189 | ``` 190 | 191 | **padLeft(n, string)** 192 | ```swift 193 | "hello".padLeft(10) // " hello" 194 | "what?".padLeft(2, "!") // "!!what?" 195 | ``` 196 | 197 | **padRight(n, string)** 198 | ```swift 199 | "hello".padRight(10) // "hello " 200 | "hello".padRight(2, "!") // "hello!!" 201 | ``` 202 | 203 | **startsWith(prefix)** 204 | ```swift 205 | "hello world".startsWith("hello") // true 206 | "hello world".startsWith("foo") // false 207 | ``` 208 | 209 | **split(separator)** 210 | ```swift 211 | "hello world".split(" ")[0] // "hello" 212 | "hello world".split(" ")[1] // "world" 213 | "helloworld".split(" ")[0] // "helloworld" 214 | ``` 215 | 216 | **times(n)** 217 | ```swift 218 | "hi".times(3) // "hihihi" 219 | " ".times(10) // " " 220 | ``` 221 | 222 | **toBool()** 223 | ```swift 224 | "asdwads".toBool() // nil 225 | "true".toBool() // true 226 | "false".toBool() // false 227 | ``` 228 | 229 | **toFloat()** 230 | ```swift 231 | "asdwads".toFloat() // nil 232 | "2.00".toFloat() // 2.0 233 | "2".toFloat() // 2.0 234 | ``` 235 | 236 | **toInt()** 237 | ```swift 238 | "asdwads".toInt() // nil 239 | "2.00".toInt() // 2 240 | "2".toInt() // 2 241 | ``` 242 | 243 | **toDate()** 244 | ```swift 245 | "asdwads".toDate() // nil 246 | "2014-06-03".toDate() // NSDate 247 | ``` 248 | 249 | **toDateTime()** 250 | ```swift 251 | "asdwads".toDateTime() // nil 252 | "2014-06-03 13:15:01".toDateTime() // NSDate 253 | ``` 254 | 255 | **toDouble()** 256 | ```swift 257 | "asdwads".toDouble() // nil 258 | "2.00".toDouble() // 2.0 259 | "2".toDouble() // 2.0 260 | ``` 261 | 262 | **trimmedLeft()** 263 | ```swift 264 | " How are you? ".trimmedLeft() // "How are you? " 265 | ``` 266 | 267 | **trimmedRight()** 268 | ```swift 269 | " How are you? ".trimmedRight() // " How are you?" 270 | ``` 271 | 272 | **trimmed()** 273 | ```swift 274 | " How are you? ".trimmed() // "How are you?" 275 | ``` 276 | 277 | **slugify()** 278 | ```swift 279 | "Global Thermonuclear Warfare".slugify() // "global-thermonuclear-warfare" 280 | "Crème brûlée".slugify() // "creme-brulee" 281 | ``` 282 | 283 | **stripPunctuation()** 284 | ```swift 285 | "My, st[ring] *full* of %punct)".stripPunctuation() // "My string full of punct" 286 | ``` 287 | 288 | **substring(startIndex, length)** 289 | ```swift 290 | "hello world".substring(0, length: 1) // "h" 291 | "hello world".substring(0, length: 11) // "hello world" 292 | ``` 293 | 294 | **[subscript]** 295 | ```swift 296 | "hello world"[0...1] // "he" 297 | "hello world"[0..<1] // "h" 298 | "hello world"[0] // "h" 299 | "hello world"[0...10] // "hello world" 300 | ``` 301 | 302 | ## Author 303 | 304 | Andrew Mayne, andrew@redbricklab.com 305 | 306 | ## License 307 | 308 | SwiftString is available under the MIT license. See the LICENSE file for more info. 309 | -------------------------------------------------------------------------------- /SwiftString.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwiftString" 3 | s.version = "0.5.1" 4 | s.summary = "A lightweight string extension for Swift" 5 | 6 | s.description = <<-DESC 7 | Common methods and String operations for Swift 8 | DESC 9 | 10 | s.homepage = "https://github.com/amayne/SwiftString" 11 | s.license = 'MIT' 12 | s.author = { "Andrew Mayne" => "andrew@redbricklab.com" } 13 | s.source = { :git => "https://github.com/amayne/SwiftString.git", :tag => s.version.to_s } 14 | s.social_media_url = 'https://twitter.com/amayne' 15 | 16 | s.platform = :ios, '8.0' 17 | s.requires_arc = true 18 | 19 | s.source_files = 'Pod/Classes/**/*' 20 | end 21 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------