diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f067d67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa + +# Bundler +.bundle + +Carthage +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +# Note: if you ignore the Pods directory, make sure to uncomment +# `pod install` in .travis.yml +# +# Pods/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d3f73cd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +# references: +# * http://www.objc.io/issue-6/travis-ci.html +# * https://github.com/supermarin/xcpretty#usage + +language: objective-c +# cache: cocoapods +# podfile: Example/Podfile +# before_install: +# - gem install cocoapods # Since Travis is not always on latest version +# - pod install --project-directory=Example +script: +- set -o pipefail && xcodebuild test -workspace Example/RoutePatterns.xcworkspace -scheme RoutePatterns-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty +- pod lib lint diff --git a/Example/Info.plist b/Example/Info.plist new file mode 100644 index 0000000..eb18faa --- /dev/null +++ b/Example/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + + + diff --git a/Example/Podfile b/Example/Podfile new file mode 100644 index 0000000..bb22962 --- /dev/null +++ b/Example/Podfile @@ -0,0 +1,11 @@ +use_frameworks! + +target 'URLPatterns_Example', :exclusive => true do + pod 'URLPatterns', :path => '../' +end + +target 'URLPatterns_Tests', :exclusive => true do + pod 'URLPatterns', :path => '../' + + +end diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 0000000..da92bcb --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,14 @@ +PODS: + - URLPatterns (0.1.0) + +DEPENDENCIES: + - URLPatterns (from `../`) + +EXTERNAL SOURCES: + URLPatterns: + :path: "../" + +SPEC CHECKSUMS: + URLPatterns: f01472d7655c87767efb582362ef017c3cc1004c + +COCOAPODS: 0.39.0 diff --git a/Example/RoutePatterns.xcworkspace/contents.xcworkspacedata b/Example/RoutePatterns.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..bbe9ab9 --- /dev/null +++ b/Example/RoutePatterns.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/Source/AppDelegate.swift b/Example/Source/AppDelegate.swift new file mode 100644 index 0000000..caef501 --- /dev/null +++ b/Example/Source/AppDelegate.swift @@ -0,0 +1,39 @@ +// +// AppDelegate.swift +// URLPatterns +// +// Created by johnmorgan on 04/29/2016. +// Copyright (c) 2016 johnmorgan. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(application: UIApplication, didFinishLaunchingWithOptions options: [NSObject: AnyObject]?) -> Bool { + + if let url = options?[UIApplicationLaunchOptionsURLKey] as? NSURL, link = DeepLink(url: url) { + DeepLinker.open(link) + } + + let testURL = NSURL(string: "myscheme://myhost/users/john_morgan12/profile")! + + if let link = DeepLink(url: testURL) { + DeepLinker.open(link) + } + + return true + } + + func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { + + if let link = DeepLink(url: url) { + return DeepLinker.open(link) + } + return false + } +} + diff --git a/Example/Source/Base.lproj/LaunchScreen.xib b/Example/Source/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000..8038e5f --- /dev/null +++ b/Example/Source/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Source/Base.lproj/Main.storyboard b/Example/Source/Base.lproj/Main.storyboard new file mode 100644 index 0000000..1140508 --- /dev/null +++ b/Example/Source/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Source/DeepLink.swift b/Example/Source/DeepLink.swift new file mode 100644 index 0000000..f3a2f99 --- /dev/null +++ b/Example/Source/DeepLink.swift @@ -0,0 +1,39 @@ +// +// DeepLink.swift +// URLPatterns +// +// Created by John Morgan on 29/04/2016. +// Copyright © 2016 CocoaPods. All rights reserved. +// + +import Foundation +import URLPatterns + +enum DeepLink { + + case Home, History, Settings, Terms, News + case Chat(room: String) + case Profile(userId: String) +} + +extension DeepLink { + + init?(url: NSURL) { + + guard url.scheme == "myscheme" else { return nil } + guard url.host == "myhost" else { return nil } + + switch url.countedPathComponents() { + + case .N0, .N1(""): self = .Home + case .N1("history"): self = .History + case .N2(_, "settings"): self = .Settings + case .N2("chat", let room): self = .Chat(room: room) + case .N3("users", let userId, "profile"): self = .Profile(userId: userId) + case Begins("news", "latest"): self = .News + case Ends("terms"): self = .Terms + + default: return nil + } + } +} diff --git a/Example/Source/DeepLinker.swift b/Example/Source/DeepLinker.swift new file mode 100644 index 0000000..b65be75 --- /dev/null +++ b/Example/Source/DeepLinker.swift @@ -0,0 +1,34 @@ +// +// DeepLinker.swift +// URLPatterns +// +// Created by John Morgan on 29/04/2016. +// Copyright © 2016 CocoaPods. All rights reserved. +// + +import Foundation + +struct DeepLinker { + + static func open(link: DeepLink, animated: Bool = true) -> Bool { + + switch link { + + case .Profile(let userId): + print("Opening profile for \(userId)") + case .History: + print("Opening history") + case .Chat(let room): + print("Opening chat room \(room)") + case .Home: + print("Opening home") + case .Settings: + print("Opening settings") + case .News: + print("Opening news") + case .Terms: + print("Opening terms") + } + return true + } +} diff --git a/Example/Source/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/Source/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d3942e9 --- /dev/null +++ b/Example/Source/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/TODO.md b/Example/TODO.md new file mode 100644 index 0000000..a82d2ee --- /dev/null +++ b/Example/TODO.md @@ -0,0 +1,5 @@ +## Possible future additions + +- Make Counted conform to CollectionType? +- Add matching for Contains +- Add Regex matching for individual path elements diff --git a/Example/Tests/Info.plist b/Example/Tests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Example/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift new file mode 100644 index 0000000..8d6f265 --- /dev/null +++ b/Example/Tests/Tests.swift @@ -0,0 +1,29 @@ +import UIKit +import XCTest +import URLPatterns + +class Tests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + XCTAssert(true, "Pass") + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock() { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Example/URLPatterns.xcodeproj/project.pbxproj b/Example/URLPatterns.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a1558ed --- /dev/null +++ b/Example/URLPatterns.xcodeproj/project.pbxproj @@ -0,0 +1,1311 @@ + + + + + archiveVersion + 1 + classes + + objectVersion + 46 + objects + + 04AC2450233B25049373E0FD + + fileRef + 6D7553EA99BA34A221C8FF5A + isa + PBXBuildFile + + 083BBF27E4A3856F9B8FA743 + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Embed Pods Frameworks + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + "${SRCROOT}/Pods/Target Support Files/Pods-URLPatterns_Tests/Pods-URLPatterns_Tests-frameworks.sh" + + showEnvVarsInLog + 0 + + 19BD0D0E3E101FBDBD3AE2AC + + children + + 420EF5F92CB2F4AAC83EE210 + 7A3E559FD770C00738A65287 + 60961F768D76907B370B2372 + 6D7553EA99BA34A221C8FF5A + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + 31F14D6FF664B9445D965A9B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-RoutePatterns_Tests.debug.xcconfig + path + Pods/Target Support Files/Pods-RoutePatterns_Tests/Pods-RoutePatterns_Tests.debug.xcconfig + sourceTree + <group> + + 328A1D340421006AEE73F2E5 + + fileRef + 60961F768D76907B370B2372 + isa + PBXBuildFile + + 3341D564529E3FB8309A0E12 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-RoutePatterns_Example.debug.xcconfig + path + Pods/Target Support Files/Pods-RoutePatterns_Example/Pods-RoutePatterns_Example.debug.xcconfig + sourceTree + <group> + + 420EF5F92CB2F4AAC83EE210 + + explicitFileType + wrapper.framework + includeInIndex + 0 + isa + PBXFileReference + path + Pods_RoutePatterns_Example.framework + sourceTree + BUILT_PRODUCTS_DIR + + 4B1B919AF1DD570060652FEE + + fileRef + 7A3E559FD770C00738A65287 + isa + PBXBuildFile + + 52484F0E1CD4086500D65195 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + sourcecode.swift + path + DeepLink.swift + sourceTree + <group> + + 52484F0F1CD4086500D65195 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + sourcecode.swift + path + DeepLinker.swift + sourceTree + <group> + + 52484F101CD4086500D65195 + + fileRef + 52484F0E1CD4086500D65195 + isa + PBXBuildFile + + 52484F111CD4086500D65195 + + fileRef + 52484F0F1CD4086500D65195 + isa + PBXBuildFile + + 52A2D3D31CE6834B0047B233 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + net.daringfireball.markdown + path + TODO.md + sourceTree + <group> + + 52A2D3D41CE6834B0047B233 + + fileRef + 52A2D3D31CE6834B0047B233 + isa + PBXBuildFile + + 607FACC71AFB9204008FA782 + + children + + 607FACF51AFB993E008FA782 + 607FACD21AFB9204008FA782 + 607FACE81AFB9204008FA782 + 607FACD11AFB9204008FA782 + 9DCD43277AC5972AD780930E + 19BD0D0E3E101FBDBD3AE2AC + + isa + PBXGroup + sourceTree + <group> + + 607FACC81AFB9204008FA782 + + attributes + + LastSwiftUpdateCheck + 0720 + LastUpgradeCheck + 0720 + ORGANIZATIONNAME + CocoaPods + TargetAttributes + + 607FACCF1AFB9204008FA782 + + CreatedOnToolsVersion + 6.3.1 + + 607FACE41AFB9204008FA782 + + CreatedOnToolsVersion + 6.3.1 + TestTargetID + 607FACCF1AFB9204008FA782 + + + + buildConfigurationList + 607FACCB1AFB9204008FA782 + compatibilityVersion + Xcode 3.2 + developmentRegion + English + hasScannedForEncodings + 0 + isa + PBXProject + knownRegions + + en + Base + + mainGroup + 607FACC71AFB9204008FA782 + productRefGroup + 607FACD11AFB9204008FA782 + projectDirPath + + projectReferences + + projectRoot + + targets + + 607FACCF1AFB9204008FA782 + 607FACE41AFB9204008FA782 + + + 607FACCB1AFB9204008FA782 + + buildConfigurations + + 607FACED1AFB9204008FA782 + 607FACEE1AFB9204008FA782 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 607FACCC1AFB9204008FA782 + + buildActionMask + 2147483647 + files + + 607FACD61AFB9204008FA782 + 52484F111CD4086500D65195 + 52484F101CD4086500D65195 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACCD1AFB9204008FA782 + + buildActionMask + 2147483647 + files + + CE3632B9B3A9FB34CBCC0F4B + 328A1D340421006AEE73F2E5 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACCE1AFB9204008FA782 + + buildActionMask + 2147483647 + files + + 607FACDB1AFB9204008FA782 + 607FACE01AFB9204008FA782 + 52A2D3D41CE6834B0047B233 + 607FACDD1AFB9204008FA782 + + isa + PBXResourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACCF1AFB9204008FA782 + + buildConfigurationList + 607FACEF1AFB9204008FA782 + buildPhases + + F199345C9703A1736DBE77C4 + 607FACCC1AFB9204008FA782 + 607FACCD1AFB9204008FA782 + 607FACCE1AFB9204008FA782 + 8FB60C215339BABAF14E2D62 + 9518AD4E4BDA9F0E3FB9183B + + buildRules + + dependencies + + isa + PBXNativeTarget + name + URLPatterns_Example + productName + RoutePatterns + productReference + 607FACD01AFB9204008FA782 + productType + com.apple.product-type.application + + 607FACD01AFB9204008FA782 + + explicitFileType + wrapper.application + includeInIndex + 0 + isa + PBXFileReference + path + URLPatterns_Example.app + sourceTree + BUILT_PRODUCTS_DIR + + 607FACD11AFB9204008FA782 + + children + + 607FACD01AFB9204008FA782 + 607FACE51AFB9204008FA782 + + isa + PBXGroup + name + Products + sourceTree + <group> + + 607FACD21AFB9204008FA782 + + children + + 607FACD51AFB9204008FA782 + 52484F0E1CD4086500D65195 + 52484F0F1CD4086500D65195 + 607FACD91AFB9204008FA782 + 607FACDC1AFB9204008FA782 + 607FACDE1AFB9204008FA782 + 607FACD31AFB9204008FA782 + + isa + PBXGroup + path + Source + sourceTree + <group> + + 607FACD31AFB9204008FA782 + + children + + 607FACD41AFB9204008FA782 + + isa + PBXGroup + name + Supporting Files + sourceTree + <group> + + 607FACD41AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + text.plist.xml + name + Info.plist + path + ../Info.plist + sourceTree + <group> + + 607FACD51AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + sourcecode.swift + path + AppDelegate.swift + sourceTree + <group> + + 607FACD61AFB9204008FA782 + + fileRef + 607FACD51AFB9204008FA782 + isa + PBXBuildFile + + 607FACD91AFB9204008FA782 + + children + + 607FACDA1AFB9204008FA782 + + isa + PBXVariantGroup + name + Main.storyboard + sourceTree + <group> + + 607FACDA1AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + file.storyboard + name + Base + path + Base.lproj/Main.storyboard + sourceTree + <group> + + 607FACDB1AFB9204008FA782 + + fileRef + 607FACD91AFB9204008FA782 + isa + PBXBuildFile + + 607FACDC1AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + folder.assetcatalog + path + Images.xcassets + sourceTree + <group> + + 607FACDD1AFB9204008FA782 + + fileRef + 607FACDC1AFB9204008FA782 + isa + PBXBuildFile + + 607FACDE1AFB9204008FA782 + + children + + 607FACDF1AFB9204008FA782 + + isa + PBXVariantGroup + name + LaunchScreen.xib + sourceTree + <group> + + 607FACDF1AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + file.xib + name + Base + path + Base.lproj/LaunchScreen.xib + sourceTree + <group> + + 607FACE01AFB9204008FA782 + + fileRef + 607FACDE1AFB9204008FA782 + isa + PBXBuildFile + + 607FACE11AFB9204008FA782 + + buildActionMask + 2147483647 + files + + 607FACEC1AFB9204008FA782 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACE21AFB9204008FA782 + + buildActionMask + 2147483647 + files + + 4B1B919AF1DD570060652FEE + 04AC2450233B25049373E0FD + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACE31AFB9204008FA782 + + buildActionMask + 2147483647 + files + + isa + PBXResourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 607FACE41AFB9204008FA782 + + buildConfigurationList + 607FACF21AFB9204008FA782 + buildPhases + + FF5124D308E46B52037DF9F1 + 607FACE11AFB9204008FA782 + 607FACE21AFB9204008FA782 + 607FACE31AFB9204008FA782 + 083BBF27E4A3856F9B8FA743 + E2C2146117CCD0A6BAC5ED48 + + buildRules + + dependencies + + 607FACE71AFB9204008FA782 + + isa + PBXNativeTarget + name + URLPatterns_Tests + productName + Tests + productReference + 607FACE51AFB9204008FA782 + productType + com.apple.product-type.bundle.unit-test + + 607FACE51AFB9204008FA782 + + explicitFileType + wrapper.cfbundle + includeInIndex + 0 + isa + PBXFileReference + path + URLPatterns_Example.xctest + sourceTree + BUILT_PRODUCTS_DIR + + 607FACE61AFB9204008FA782 + + containerPortal + 607FACC81AFB9204008FA782 + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 607FACCF1AFB9204008FA782 + remoteInfo + RoutePatterns + + 607FACE71AFB9204008FA782 + + isa + PBXTargetDependency + target + 607FACCF1AFB9204008FA782 + targetProxy + 607FACE61AFB9204008FA782 + + 607FACE81AFB9204008FA782 + + children + + 607FACEB1AFB9204008FA782 + 607FACE91AFB9204008FA782 + + isa + PBXGroup + path + Tests + sourceTree + <group> + + 607FACE91AFB9204008FA782 + + children + + 607FACEA1AFB9204008FA782 + + isa + PBXGroup + name + Supporting Files + sourceTree + <group> + + 607FACEA1AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + text.plist.xml + path + Info.plist + sourceTree + <group> + + 607FACEB1AFB9204008FA782 + + isa + PBXFileReference + lastKnownFileType + sourcecode.swift + path + Tests.swift + sourceTree + <group> + + 607FACEC1AFB9204008FA782 + + fileRef + 607FACEB1AFB9204008FA782 + isa + PBXBuildFile + + 607FACED1AFB9204008FA782 + + buildSettings + + ALWAYS_SEARCH_USER_PATHS + NO + CLANG_CXX_LANGUAGE_STANDARD + gnu++0x + CLANG_CXX_LIBRARY + libc++ + CLANG_ENABLE_MODULES + YES + CLANG_ENABLE_OBJC_ARC + YES + CLANG_WARN_BOOL_CONVERSION + YES + CLANG_WARN_CONSTANT_CONVERSION + YES + CLANG_WARN_DIRECT_OBJC_ISA_USAGE + YES_ERROR + CLANG_WARN_EMPTY_BODY + YES + CLANG_WARN_ENUM_CONVERSION + YES + CLANG_WARN_INT_CONVERSION + YES + CLANG_WARN_OBJC_ROOT_CLASS + YES_ERROR + CLANG_WARN_UNREACHABLE_CODE + YES + CLANG_WARN__DUPLICATE_METHOD_MATCH + YES + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + COPY_PHASE_STRIP + NO + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + ENABLE_TESTABILITY + YES + GCC_C_LANGUAGE_STANDARD + gnu99 + GCC_DYNAMIC_NO_PIC + NO + GCC_NO_COMMON_BLOCKS + YES + GCC_OPTIMIZATION_LEVEL + 0 + GCC_PREPROCESSOR_DEFINITIONS + + DEBUG=1 + $(inherited) + + GCC_SYMBOLS_PRIVATE_EXTERN + NO + GCC_WARN_64_TO_32_BIT_CONVERSION + YES + GCC_WARN_ABOUT_RETURN_TYPE + YES_ERROR + GCC_WARN_UNDECLARED_SELECTOR + YES + GCC_WARN_UNINITIALIZED_AUTOS + YES_AGGRESSIVE + GCC_WARN_UNUSED_FUNCTION + YES + GCC_WARN_UNUSED_VARIABLE + YES + IPHONEOS_DEPLOYMENT_TARGET + 8.3 + MTL_ENABLE_DEBUG_INFO + YES + ONLY_ACTIVE_ARCH + YES + SDKROOT + iphoneos + SWIFT_OPTIMIZATION_LEVEL + -Onone + + isa + XCBuildConfiguration + name + Debug + + 607FACEE1AFB9204008FA782 + + buildSettings + + ALWAYS_SEARCH_USER_PATHS + NO + CLANG_CXX_LANGUAGE_STANDARD + gnu++0x + CLANG_CXX_LIBRARY + libc++ + CLANG_ENABLE_MODULES + YES + CLANG_ENABLE_OBJC_ARC + YES + CLANG_WARN_BOOL_CONVERSION + YES + CLANG_WARN_CONSTANT_CONVERSION + YES + CLANG_WARN_DIRECT_OBJC_ISA_USAGE + YES_ERROR + CLANG_WARN_EMPTY_BODY + YES + CLANG_WARN_ENUM_CONVERSION + YES + CLANG_WARN_INT_CONVERSION + YES + CLANG_WARN_OBJC_ROOT_CLASS + YES_ERROR + CLANG_WARN_UNREACHABLE_CODE + YES + CLANG_WARN__DUPLICATE_METHOD_MATCH + YES + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + COPY_PHASE_STRIP + NO + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_NS_ASSERTIONS + NO + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_C_LANGUAGE_STANDARD + gnu99 + GCC_NO_COMMON_BLOCKS + YES + GCC_WARN_64_TO_32_BIT_CONVERSION + YES + GCC_WARN_ABOUT_RETURN_TYPE + YES_ERROR + GCC_WARN_UNDECLARED_SELECTOR + YES + GCC_WARN_UNINITIALIZED_AUTOS + YES_AGGRESSIVE + GCC_WARN_UNUSED_FUNCTION + YES + GCC_WARN_UNUSED_VARIABLE + YES + IPHONEOS_DEPLOYMENT_TARGET + 8.3 + MTL_ENABLE_DEBUG_INFO + NO + SDKROOT + iphoneos + VALIDATE_PRODUCT + YES + + isa + XCBuildConfiguration + name + Release + + 607FACEF1AFB9204008FA782 + + buildConfigurations + + 607FACF01AFB9204008FA782 + 607FACF11AFB9204008FA782 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 607FACF01AFB9204008FA782 + + baseConfigurationReference + 82207C030FAC5BD36B749741 + buildSettings + + ASSETCATALOG_COMPILER_APPICON_NAME + AppIcon + INFOPLIST_FILE + Info.plist + LD_RUNPATH_SEARCH_PATHS + $(inherited) @executable_path/Frameworks + MODULE_NAME + ExampleApp + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier) + PRODUCT_NAME + URLPatterns_Example + + isa + XCBuildConfiguration + name + Debug + + 607FACF11AFB9204008FA782 + + baseConfigurationReference + 77F91B464F092D4B8B42EE69 + buildSettings + + ASSETCATALOG_COMPILER_APPICON_NAME + AppIcon + INFOPLIST_FILE + Info.plist + LD_RUNPATH_SEARCH_PATHS + $(inherited) @executable_path/Frameworks + MODULE_NAME + ExampleApp + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier) + PRODUCT_NAME + URLPatterns_Example + + isa + XCBuildConfiguration + name + Release + + 607FACF21AFB9204008FA782 + + buildConfigurations + + 607FACF31AFB9204008FA782 + 607FACF41AFB9204008FA782 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 607FACF31AFB9204008FA782 + + baseConfigurationReference + CC9BAB497C95F30DBFA0BE84 + buildSettings + + FRAMEWORK_SEARCH_PATHS + + $(SDKROOT)/Developer/Library/Frameworks + $(inherited) + + GCC_PREPROCESSOR_DEFINITIONS + + DEBUG=1 + $(inherited) + + INFOPLIST_FILE + Tests/Info.plist + LD_RUNPATH_SEARCH_PATHS + $(inherited) @executable_path/Frameworks @loader_path/Frameworks + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.$(PRODUCT_NAME:rfc1034identifier) + PRODUCT_NAME + URLPatterns_Example + + isa + XCBuildConfiguration + name + Debug + + 607FACF41AFB9204008FA782 + + baseConfigurationReference + 7CEDBEB38D0715BCD182B958 + buildSettings + + FRAMEWORK_SEARCH_PATHS + + $(SDKROOT)/Developer/Library/Frameworks + $(inherited) + + INFOPLIST_FILE + Tests/Info.plist + LD_RUNPATH_SEARCH_PATHS + $(inherited) @executable_path/Frameworks @loader_path/Frameworks + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.$(PRODUCT_NAME:rfc1034identifier) + PRODUCT_NAME + URLPatterns_Example + + isa + XCBuildConfiguration + name + Release + + 607FACF51AFB993E008FA782 + + children + + B29E1194CFEFFC4565D88853 + 52A2D3D31CE6834B0047B233 + C6FA60F6465206D70E1FBBF2 + BE59EFF3174E9618FC1B3145 + + isa + PBXGroup + name + Podspec Metadata + sourceTree + <group> + + 60961F768D76907B370B2372 + + explicitFileType + wrapper.framework + includeInIndex + 0 + isa + PBXFileReference + path + Pods_URLPatterns_Example.framework + sourceTree + BUILT_PRODUCTS_DIR + + 6D7553EA99BA34A221C8FF5A + + explicitFileType + wrapper.framework + includeInIndex + 0 + isa + PBXFileReference + path + Pods_URLPatterns_Tests.framework + sourceTree + BUILT_PRODUCTS_DIR + + 6F960D29C60F05E780539907 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-RoutePatterns_Tests.release.xcconfig + path + Pods/Target Support Files/Pods-RoutePatterns_Tests/Pods-RoutePatterns_Tests.release.xcconfig + sourceTree + <group> + + 77F91B464F092D4B8B42EE69 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-URLPatterns_Example.release.xcconfig + path + Pods/Target Support Files/Pods-URLPatterns_Example/Pods-URLPatterns_Example.release.xcconfig + sourceTree + <group> + + 7A3E559FD770C00738A65287 + + explicitFileType + wrapper.framework + includeInIndex + 0 + isa + PBXFileReference + path + Pods_RoutePatterns_Tests.framework + sourceTree + BUILT_PRODUCTS_DIR + + 7CEDBEB38D0715BCD182B958 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-URLPatterns_Tests.release.xcconfig + path + Pods/Target Support Files/Pods-URLPatterns_Tests/Pods-URLPatterns_Tests.release.xcconfig + sourceTree + <group> + + 82207C030FAC5BD36B749741 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-URLPatterns_Example.debug.xcconfig + path + Pods/Target Support Files/Pods-URLPatterns_Example/Pods-URLPatterns_Example.debug.xcconfig + sourceTree + <group> + + 8FB60C215339BABAF14E2D62 + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Embed Pods Frameworks + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + "${SRCROOT}/Pods/Target Support Files/Pods-URLPatterns_Example/Pods-URLPatterns_Example-frameworks.sh" + + showEnvVarsInLog + 0 + + 9518AD4E4BDA9F0E3FB9183B + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Copy Pods Resources + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + "${SRCROOT}/Pods/Target Support Files/Pods-URLPatterns_Example/Pods-URLPatterns_Example-resources.sh" + + showEnvVarsInLog + 0 + + 9DCD43277AC5972AD780930E + + children + + 3341D564529E3FB8309A0E12 + B18781699E0A2D9305FD283F + 31F14D6FF664B9445D965A9B + 6F960D29C60F05E780539907 + 82207C030FAC5BD36B749741 + 77F91B464F092D4B8B42EE69 + CC9BAB497C95F30DBFA0BE84 + 7CEDBEB38D0715BCD182B958 + + isa + PBXGroup + name + Pods + sourceTree + <group> + + B18781699E0A2D9305FD283F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-RoutePatterns_Example.release.xcconfig + path + Pods/Target Support Files/Pods-RoutePatterns_Example/Pods-RoutePatterns_Example.release.xcconfig + sourceTree + <group> + + B29E1194CFEFFC4565D88853 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text + name + URLPatterns.podspec + path + ../URLPatterns.podspec + sourceTree + <group> + + BE59EFF3174E9618FC1B3145 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text + name + LICENSE + path + ../LICENSE + sourceTree + <group> + + C6FA60F6465206D70E1FBBF2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + net.daringfireball.markdown + name + README.md + path + ../README.md + sourceTree + <group> + + CC9BAB497C95F30DBFA0BE84 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + Pods-URLPatterns_Tests.debug.xcconfig + path + Pods/Target Support Files/Pods-URLPatterns_Tests/Pods-URLPatterns_Tests.debug.xcconfig + sourceTree + <group> + + CE3632B9B3A9FB34CBCC0F4B + + fileRef + 420EF5F92CB2F4AAC83EE210 + isa + PBXBuildFile + + E2C2146117CCD0A6BAC5ED48 + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Copy Pods Resources + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + "${SRCROOT}/Pods/Target Support Files/Pods-URLPatterns_Tests/Pods-URLPatterns_Tests-resources.sh" + + showEnvVarsInLog + 0 + + F199345C9703A1736DBE77C4 + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Check Pods Manifest.lock + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null +if [[ $? != 0 ]] ; then + cat << EOM +error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. +EOM + exit 1 +fi + + showEnvVarsInLog + 0 + + FF5124D308E46B52037DF9F1 + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + name + Check Pods Manifest.lock + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null +if [[ $? != 0 ]] ; then + cat << EOM +error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. +EOM + exit 1 +fi + + showEnvVarsInLog + 0 + + + rootObject + 607FACC81AFB9204008FA782 + + diff --git a/Example/URLPatterns.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/URLPatterns.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..f59e7a5 --- /dev/null +++ b/Example/URLPatterns.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/URLPatterns.xcworkspace/contents.xcworkspacedata b/Example/URLPatterns.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..0581840 --- /dev/null +++ b/Example/URLPatterns.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6429757 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 johnmorgan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1057b5a --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# 🎯 URLPatterns 🎯 + +[![Version](https://img.shields.io/cocoapods/v/URLPatterns.svg?style=flat)](http://cocoapods.org/pods/URLPatterns) +[![License](https://img.shields.io/cocoapods/l/URLPatterns.svg?style=flat)](http://cocoapods.org/pods/URLPatterns) + +URLPatterns is a small library to enable more idiomatic Swift pattern matching of URL path elements. + +`NSURL` is extended with the method `countedPathElements()`, which converts the URL's' array of path elements into a `Counted` enum. Each case in `Counted` has a different number of associated values, which makes pattern-matching each element easy: + +```swift +if case .N4("user", let userId, "profile", _) = url.countedPathElements() { + // show profile for userId +} +``` + + `Counted` enables us to pattern match paths with any number of elements, and supports **expression**, **wildcard** and **value-binding** patterns for its associated values. It can also match based on `Begins` and `Ends`, which match based on the first/ last elements only. Here's an example of a DeepLink enum which has a failable initializer that takes an `NSURL`: + + +```swift +enum DeepLink { + + case Home, History, Settings, Terms, News + case Chat(room: String) + case Profile(userId: String) +} + +extension DeepLink { + + init?(url: NSURL) { + + guard url.scheme == "myscheme" else { return nil } + guard url.host == "myhost" else { return nil } + + switch url.countedPathComponents() { + + case .N0, .N1(""): self = .Home + case .N1("history"): self = .History + case .N2(_, "settings"): self = .Settings + case .N2("chat", let room): self = .Chat(room: room) + case .N3("users", let userId, "profile"): self = .Profile(userId: userId) + case Begins("news", "latest"): self = .News + case Ends("terms"): self = .Terms + + default: return nil + } + } +} +``` + +## Installation + +URLPatterns is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod "URLPatterns" +``` + +## Author + +johnmorgan, johnpatrickmorganuk@gmail.com + +## License + +URLPatterns is available under the MIT license. See the LICENSE file for more info. diff --git a/TODO b/TODO new file mode 100644 index 0000000..f584ddd --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ + +- Add pattern matching for .Starts(Counted) and .Ends(Counted) +- CollectionType \ No newline at end of file diff --git a/URLPatterns.podspec b/URLPatterns.podspec new file mode 100644 index 0000000..1383414 --- /dev/null +++ b/URLPatterns.podspec @@ -0,0 +1,23 @@ + +Pod::Spec.new do |s| + s.name = "URLPatterns" + s.version = "0.1.0" + s.summary = "A small library to enable more idiomatic Swift pattern matching of URL path elements." + + s.description = <<-DESC + URLPatterns is a small library to enable more idiomatic Swift pattern matching of URL path elements. + `NSURL` is extended with the method `countedPathElements()`, which converts the URL's' array of path elements + into a `Counted` enum. Each case in `Counted` has a different number of associated values, which makes + pattern-matching each element easy, including wildcard, value-binding, begins and ends patterns. + DESC + + s.homepage = "https://github.com/johnpatrickmorgan/URLPatterns" + s.license = 'MIT' + s.author = { "johnmorgan" => "johnpatrickmorganuk@gmail.com" } + s.source = { :git => "https://github.com//johnpatrickmorgan/URLPatterns.git", :tag => s.version.to_s } + s.social_media_url = 'https://twitter.com/jpmmusic' + + s.ios.deployment_target = '8.0' + + s.source_files = 'URLPatterns/Classes/**/*' +end diff --git a/URLPatterns/Assets/.gitkeep b/URLPatterns/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/URLPatterns/Classes/.gitkeep b/URLPatterns/Classes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/URLPatterns/Classes/Counted.swift b/URLPatterns/Classes/Counted.swift new file mode 100644 index 0000000..fc6d565 --- /dev/null +++ b/URLPatterns/Classes/Counted.swift @@ -0,0 +1,151 @@ +// +// Counted.swift +// Pods +// +// Created by John Morgan on 29/04/2016. +// +// + +public enum Counted { + + public typealias E = Element + + case N0 + case N1(E) + case N2(E, E) + case N3(E, E, E) + case N4(E, E, E, E) + case N5(E, E, E, E, E) + case N6(E, E, E, E, E, E) + case N7(E, E, E, E, E, E, E) + case N8(E, E, E, E, E, E, E, E) + case N9(E, E, E, E, E, E, E, E, E) + case N10(E, E, E, E, E, E, E, E, E, E) + + indirect case N10Plus(E, E, E, E, E, E, E, E, E, E, Counted) + + public init(_ elements: [Element]) { + + let e = elements + + switch e.count { + case 0: + self = .N0 + case 1: + self = .N1(e[0]) + case 2: + self = .N2(e[0], e[1]) + case 3: + self = .N3(e[0], e[1], e[2]) + case 4: + self = .N4(e[0], e[1], e[2], e[3]) + case 5: + self = .N5(e[0], e[1], e[2], e[3], e[4]) + case 6: + self = .N6(e[0], e[1], e[2], e[3], e[4], e[5]) + case 7: + self = .N7(e[0], e[1], e[2], e[3], e[4], e[5], e[6]) + case 8: + self = .N8(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7]) + case 9: + self = .N9(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8]) + case 10: + self = .N10(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9]) + default: + self = .N10Plus(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9], Counted(Array(e[10..(sequence: S) { + + self.init(Array(sequence)) + } + + public var elements: [Element] { + + switch self { + case .N0: + return [] + case let .N1(e1): + return [e1] + case let .N2(e1, e2): + return [e1, e2] + case let .N3(e1, e2, e3): + return [e1, e2, e3] + case let .N4(e1, e2, e3, e4): + return [e1, e2, e3, e4] + case let .N5(e1, e2, e3, e4, e5): + return [e1, e2, e3, e4, e5] + case let .N6(e1, e2, e3, e4, e5, e6): + return [e1, e2, e3, e4, e5, e6] + case let .N7(e1, e2, e3, e4, e5, e6, e7): + return [e1, e2, e3, e4, e5, e6, e7] + case let .N8(e1, e2, e3, e4, e5, e6, e7, e8): + return [e1, e2, e3, e4, e5, e6, e7, e8] + case let .N9(e1, e2, e3, e4, e5, e6, e7, e8, e9): + return [e1, e2, e3, e4, e5, e6, e7, e8, e9] + case let .N10(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10): + return [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10] + case let .N10Plus(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, remainder): + return [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10] + remainder.elements + } + } + + public mutating func append(x: Element) { + + self = Counted(elements + [x]) + } + + public mutating func removeLast() -> Element? { + + switch self { + case .N0: return nil + default: + var elements = self.elements + let popped = elements.removeLast() + self = Counted(elements) + return popped + } + } + + public mutating func removeFirst() -> Element? { + + switch self { + case .N0: return nil + default: + var elements = self.elements + let popped = elements.removeFirst() + self = Counted(elements) + return popped + } + } + + public subscript(index: Int) -> Element { + + return elements[index] + } +} + +extension Counted: SequenceType { + + public func generate() -> AnyGenerator { + + var current = self + return AnyGenerator { + current.removeFirst() + } + } +} + +extension Counted: ArrayLiteralConvertible { + + public init(arrayLiteral elements: Element...) { + + self.init(elements) + } +} diff --git a/URLPatterns/Classes/CountedExpressionMatching.swift b/URLPatterns/Classes/CountedExpressionMatching.swift new file mode 100644 index 0000000..0e51b2a --- /dev/null +++ b/URLPatterns/Classes/CountedExpressionMatching.swift @@ -0,0 +1,39 @@ +// +// CountedExpressionMatching.swift +// Pods +// +// Created by John Morgan on 14/05/2016. +// +// + +import Foundation + +public struct Begins { + + var elements: [E] + + public init(_ elements: E...) { + + self.elements = elements + } +} + +public func ~=(pattern: Begins, value: Counted) -> Bool { + + return Array(value.elements.prefix(pattern.elements.count)) == pattern.elements +} + +public struct Ends { + + var elements: [E] + + public init(_ elements: E...) { + + self.elements = elements + } +} + +public func ~=(pattern: Ends, value: Counted) -> Bool { + + return Array(value.elements.suffix(pattern.elements.count)) == pattern.elements +} diff --git a/URLPatterns/Classes/FoundationExtensions.swift b/URLPatterns/Classes/FoundationExtensions.swift new file mode 100644 index 0000000..3e7e0cc --- /dev/null +++ b/URLPatterns/Classes/FoundationExtensions.swift @@ -0,0 +1,63 @@ +// +// FoundationExtensions.swift +// Pods +// +// Created by John Morgan on 29/04/2016. +// +// + +import Foundation + +extension NSURLComponents { + + public func queryArguments() -> [String : String?] { + + var arguments = [String : String?]() + + for item in queryItems ?? [] { + arguments[item.name] = item.value + } + return arguments + } + + public func flatQueryArguments() -> [String : String] { + + var arguments = [String : String]() + + for (key, value) in queryArguments() { + if value != nil { + arguments[key] = value + } + } + return arguments + } +} + +extension NSURL { + + public func countedPathComponents(excludingLeadingBackslash: Bool = true) -> Counted { + + var components = pathComponents ?? [] + if let first = components.first where excludingLeadingBackslash && first == "/" { + components.removeFirst() + } + return Counted(components) + } +} + +extension NSURL { + + public func queryArguments() -> [String : String?] { + + guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: true) else { return [:] } + + return components.queryArguments() + } + + public func flatQueryArguments() -> [String : String] { + + guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: true) else { return [:] } + + return components.flatQueryArguments() + } +} diff --git a/_Pods.xcodeproj b/_Pods.xcodeproj new file mode 120000 index 0000000..3c5a8e7 --- /dev/null +++ b/_Pods.xcodeproj @@ -0,0 +1 @@ +Example/Pods/Pods.xcodeproj \ No newline at end of file