├── .gitignore ├── LICENSE.md ├── README.md ├── mime.podspec ├── mime.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── mime.xcscheme └── mime ├── Controls.swift ├── Gestures.swift ├── Info.plist ├── Mime.swift ├── Target.swift └── mime.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Carthage 4 | Carthage/Build/ 5 | Carthage/Checkouts/ 6 | 7 | # Cocoapods 8 | Pods/ 9 | Podfile 10 | Podfile.lock 11 | mime.xcworkspace 12 | 13 | # Tests 14 | tests/ 15 | 16 | # Build generated 17 | build/ 18 | derivedData/ 19 | 20 | # Other 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcuserdatad 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 James Taylor 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # from target-action to closure! (and beyond 🚀) 2 | **mime** provides an alternate way of interacting with the objective-c style target-action pattern using swift closures 3 | 4 | ### gestures... 5 | target-action 6 | ```swift 7 | let tap = UITapGestureRecognizer(target: self, action: #selector(...handleTap(_:)) 8 | view.addGestureRecognizer(tap) 9 | 10 | dynamic func handleTap(tap: UITapGestureRecognizer) { 11 | print("tap at \(tap.locationInView(view))!") 12 | } 13 | ``` 14 | **mime** 15 | ```swift 16 | let tap = UITapGestureRecognizer() 17 | tap.mime_on { 18 | print("tap at \($0.locationInView($0.view))!") 19 | } 20 | view.addGestureRecognizer(tap) 21 | ``` 22 | 💥**bonus!**💥 **mime** can also do gesture state filtering 23 | ```swift 24 | let pan = UIPanGestureRecognizer() 25 | pan.mime_on([.began, .changed]) { _ in 26 | print("meh... I'm not really interested in being called when the pan ends") 27 | } 28 | view.addGestureRecognizer(pan) 29 | ``` 30 | 31 | ### controls... 32 | ```swift 33 | let button = UIButton() 34 | button.mime_on(.allTouchEvents) { 35 | print("getting all touchy with \($0.currentTitle)...") 36 | } 37 | view.addSubview(button) 38 | ``` 39 | 40 | ### removing a mime closure... 41 | `mime_off` removes all closures set up with `mime_on` 42 | ```swift 43 | tap.mime_off() 44 | button.mime_off() 45 | ``` 46 | 47 | ### a note on retain cycles... 48 | ```swift 49 | // when calling `T.mime_on`, note that `T` will hold a reference to the closure 50 | tap.mime_on { 51 | print("I'm being retained!") 52 | } 53 | 54 | // if you plan on referencing an object that holds reference to `T` from inside the closure, make sure to do so weakly 55 | tap.mime_on { [weak view] in 56 | print("I'm being retained but it's cool cause I don't really care about \(view)") 57 | } 58 | view.addGestureRecognizer(tap) 59 | ``` 60 | 61 | ### installation 62 | `pod 'mime'` and you're set! 63 | -------------------------------------------------------------------------------- /mime.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "mime" 4 | s.version = "1.0.0-beta.1" 5 | s.summary = "from target-action to closure! (and beyond 🚀)" 6 | s.description = <<-EOS 7 | mime provides an alternate way of interacting with the objective-c style target-action 8 | pattern using swift closures 9 | EOS 10 | 11 | s.homepage = "https://github.com/jameslintaylor/mime" 12 | s.license = { :type => "MIT", :file => "LICENSE.md" } 13 | 14 | s.author = "James Taylor" 15 | s.social_media_url = "http://twitter.com/jameslintaylor" 16 | 17 | s.ios.deployment_target = "9.0" 18 | s.tvos.deployment_target = "9.0" 19 | 20 | s.source = { :git => "https://github.com/jameslintaylor/mime.git", :tag => s.version } 21 | s.source_files = "mime/*.swift" 22 | s.framework = "UIKit" 23 | s.dependency "AssociatedObjects", "~> 1.0.0-beta.2" 24 | 25 | end 26 | -------------------------------------------------------------------------------- /mime.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2A4E8BE51D775BD200DBAD53 /* mime.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A4E8BE41D775BD200DBAD53 /* mime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 2A60505E1D77732800970289 /* Gestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A60505D1D77732800970289 /* Gestures.swift */; }; 12 | 2AD6BFD51D77B16C00F5EBAE /* Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6BFD41D77B16C00F5EBAE /* Target.swift */; }; 13 | 2AD6C00B1D77B80B00F5EBAE /* Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6C00A1D77B80B00F5EBAE /* Controls.swift */; }; 14 | 2AD6C00D1D77BFE000F5EBAE /* Mime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6C00C1D77BFE000F5EBAE /* Mime.swift */; }; 15 | A73488907D452D009798A24A /* Pods_mime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF841CD18F21FEA0A901A04D /* Pods_mime.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 2AD6BFF81D77B2C200F5EBAE /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 2AD6BFF41D77B2C200F5EBAE /* tests.xcodeproj */; 22 | proxyType = 2; 23 | remoteGlobalIDString = 2AD6BFDF1D77B2C200F5EBAE; 24 | remoteInfo = tests; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 2A4E8BE11D775BD200DBAD53 /* mime.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = mime.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 2A4E8BE41D775BD200DBAD53 /* mime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mime.h; sourceTree = ""; }; 31 | 2A4E8BE61D775BD200DBAD53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 2A60505D1D77732800970289 /* Gestures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gestures.swift; sourceTree = ""; }; 33 | 2AD6BFD41D77B16C00F5EBAE /* Target.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Target.swift; sourceTree = ""; }; 34 | 2AD6BFF41D77B2C200F5EBAE /* tests.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = tests.xcodeproj; path = tests/tests.xcodeproj; sourceTree = ""; }; 35 | 2AD6C00A1D77B80B00F5EBAE /* Controls.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Controls.swift; sourceTree = ""; }; 36 | 2AD6C00C1D77BFE000F5EBAE /* Mime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mime.swift; sourceTree = ""; }; 37 | 389881A372882E9039D0CACA /* Pods-mime.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mime.release.xcconfig"; path = "Pods/Target Support Files/Pods-mime/Pods-mime.release.xcconfig"; sourceTree = ""; }; 38 | 3B1AE262A1B99631A9A506E0 /* Pods-mime.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mime.debug.xcconfig"; path = "Pods/Target Support Files/Pods-mime/Pods-mime.debug.xcconfig"; sourceTree = ""; }; 39 | DF841CD18F21FEA0A901A04D /* Pods_mime.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_mime.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 2A4E8BDD1D775BD200DBAD53 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | A73488907D452D009798A24A /* Pods_mime.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 17DB13680A780C96ED417D29 /* Frameworks */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | DF841CD18F21FEA0A901A04D /* Pods_mime.framework */, 58 | ); 59 | name = Frameworks; 60 | sourceTree = ""; 61 | }; 62 | 227BF6A3FF96CB450D513724 /* Pods */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 3B1AE262A1B99631A9A506E0 /* Pods-mime.debug.xcconfig */, 66 | 389881A372882E9039D0CACA /* Pods-mime.release.xcconfig */, 67 | ); 68 | name = Pods; 69 | sourceTree = ""; 70 | }; 71 | 2A4E8BD71D775BD200DBAD53 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 2AD6BFF41D77B2C200F5EBAE /* tests.xcodeproj */, 75 | 2A4E8BE31D775BD200DBAD53 /* mime */, 76 | 2A4E8BE21D775BD200DBAD53 /* Products */, 77 | 227BF6A3FF96CB450D513724 /* Pods */, 78 | 17DB13680A780C96ED417D29 /* Frameworks */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 2A4E8BE21D775BD200DBAD53 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 2A4E8BE11D775BD200DBAD53 /* mime.framework */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 2A4E8BE31D775BD200DBAD53 /* mime */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 2A4E8BE41D775BD200DBAD53 /* mime.h */, 94 | 2A4E8BE61D775BD200DBAD53 /* Info.plist */, 95 | 2AD6BFD41D77B16C00F5EBAE /* Target.swift */, 96 | 2AD6C00C1D77BFE000F5EBAE /* Mime.swift */, 97 | 2A60505D1D77732800970289 /* Gestures.swift */, 98 | 2AD6C00A1D77B80B00F5EBAE /* Controls.swift */, 99 | ); 100 | path = mime; 101 | sourceTree = ""; 102 | }; 103 | 2AD6BFF51D77B2C200F5EBAE /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 2AD6BFF91D77B2C200F5EBAE /* tests.app */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | /* End PBXGroup section */ 112 | 113 | /* Begin PBXHeadersBuildPhase section */ 114 | 2A4E8BDE1D775BD200DBAD53 /* Headers */ = { 115 | isa = PBXHeadersBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | 2A4E8BE51D775BD200DBAD53 /* mime.h in Headers */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXHeadersBuildPhase section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | 2A4E8BE01D775BD200DBAD53 /* mime */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = 2A4E8BE91D775BD200DBAD53 /* Build configuration list for PBXNativeTarget "mime" */; 128 | buildPhases = ( 129 | 44B4BE8E958D2C9AB563C876 /* [CP] Check Pods Manifest.lock */, 130 | 2A4E8BDC1D775BD200DBAD53 /* Sources */, 131 | 2A4E8BDD1D775BD200DBAD53 /* Frameworks */, 132 | 2A4E8BDE1D775BD200DBAD53 /* Headers */, 133 | 2A4E8BDF1D775BD200DBAD53 /* Resources */, 134 | 1FE4ACE9836F98D1DAFC2E1F /* [CP] Copy Pods Resources */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = mime; 141 | productName = mime; 142 | productReference = 2A4E8BE11D775BD200DBAD53 /* mime.framework */; 143 | productType = "com.apple.product-type.framework"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 2A4E8BD81D775BD200DBAD53 /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | LastUpgradeCheck = 0730; 152 | ORGANIZATIONNAME = "James Taylor"; 153 | TargetAttributes = { 154 | 2A4E8BE01D775BD200DBAD53 = { 155 | CreatedOnToolsVersion = 7.3.1; 156 | LastSwiftMigration = 0800; 157 | }; 158 | }; 159 | }; 160 | buildConfigurationList = 2A4E8BDB1D775BD200DBAD53 /* Build configuration list for PBXProject "mime" */; 161 | compatibilityVersion = "Xcode 3.2"; 162 | developmentRegion = English; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | ); 167 | mainGroup = 2A4E8BD71D775BD200DBAD53; 168 | productRefGroup = 2A4E8BE21D775BD200DBAD53 /* Products */; 169 | projectDirPath = ""; 170 | projectReferences = ( 171 | { 172 | ProductGroup = 2AD6BFF51D77B2C200F5EBAE /* Products */; 173 | ProjectRef = 2AD6BFF41D77B2C200F5EBAE /* tests.xcodeproj */; 174 | }, 175 | ); 176 | projectRoot = ""; 177 | targets = ( 178 | 2A4E8BE01D775BD200DBAD53 /* mime */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXReferenceProxy section */ 184 | 2AD6BFF91D77B2C200F5EBAE /* tests.app */ = { 185 | isa = PBXReferenceProxy; 186 | fileType = wrapper.application; 187 | path = tests.app; 188 | remoteRef = 2AD6BFF81D77B2C200F5EBAE /* PBXContainerItemProxy */; 189 | sourceTree = BUILT_PRODUCTS_DIR; 190 | }; 191 | /* End PBXReferenceProxy section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 2A4E8BDF1D775BD200DBAD53 /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXResourcesBuildPhase section */ 202 | 203 | /* Begin PBXShellScriptBuildPhase section */ 204 | 1FE4ACE9836F98D1DAFC2E1F /* [CP] Copy Pods Resources */ = { 205 | isa = PBXShellScriptBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | inputPaths = ( 210 | ); 211 | name = "[CP] Copy Pods Resources"; 212 | outputPaths = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | shellPath = /bin/sh; 216 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-mime/Pods-mime-resources.sh\"\n"; 217 | showEnvVarsInLog = 0; 218 | }; 219 | 44B4BE8E958D2C9AB563C876 /* [CP] Check Pods Manifest.lock */ = { 220 | isa = PBXShellScriptBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | ); 224 | inputPaths = ( 225 | ); 226 | name = "[CP] Check Pods Manifest.lock"; 227 | outputPaths = ( 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | shellPath = /bin/sh; 231 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 232 | showEnvVarsInLog = 0; 233 | }; 234 | /* End PBXShellScriptBuildPhase section */ 235 | 236 | /* Begin PBXSourcesBuildPhase section */ 237 | 2A4E8BDC1D775BD200DBAD53 /* Sources */ = { 238 | isa = PBXSourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 2AD6BFD51D77B16C00F5EBAE /* Target.swift in Sources */, 242 | 2AD6C00D1D77BFE000F5EBAE /* Mime.swift in Sources */, 243 | 2AD6C00B1D77B80B00F5EBAE /* Controls.swift in Sources */, 244 | 2A60505E1D77732800970289 /* Gestures.swift in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin XCBuildConfiguration section */ 251 | 2A4E8BE71D775BD200DBAD53 /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | ALWAYS_SEARCH_USER_PATHS = NO; 255 | CLANG_ANALYZER_NONNULL = YES; 256 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 257 | CLANG_CXX_LIBRARY = "libc++"; 258 | CLANG_ENABLE_MODULES = YES; 259 | CLANG_ENABLE_OBJC_ARC = YES; 260 | CLANG_WARN_BOOL_CONVERSION = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 263 | CLANG_WARN_EMPTY_BODY = YES; 264 | CLANG_WARN_ENUM_CONVERSION = YES; 265 | CLANG_WARN_INT_CONVERSION = YES; 266 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 267 | CLANG_WARN_UNREACHABLE_CODE = YES; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 270 | COPY_PHASE_STRIP = NO; 271 | CURRENT_PROJECT_VERSION = 1; 272 | DEBUG_INFORMATION_FORMAT = dwarf; 273 | ENABLE_STRICT_OBJC_MSGSEND = YES; 274 | ENABLE_TESTABILITY = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_DYNAMIC_NO_PIC = NO; 277 | GCC_NO_COMMON_BLOCKS = YES; 278 | GCC_OPTIMIZATION_LEVEL = 0; 279 | GCC_PREPROCESSOR_DEFINITIONS = ( 280 | "DEBUG=1", 281 | "$(inherited)", 282 | ); 283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 285 | GCC_WARN_UNDECLARED_SELECTOR = YES; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 287 | GCC_WARN_UNUSED_FUNCTION = YES; 288 | GCC_WARN_UNUSED_VARIABLE = YES; 289 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 290 | MTL_ENABLE_DEBUG_INFO = YES; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = iphoneos; 293 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 294 | TARGETED_DEVICE_FAMILY = "1,2"; 295 | VERSIONING_SYSTEM = "apple-generic"; 296 | VERSION_INFO_PREFIX = ""; 297 | }; 298 | name = Debug; 299 | }; 300 | 2A4E8BE81D775BD200DBAD53 /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_MODULES = YES; 308 | CLANG_ENABLE_OBJC_ARC = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | CURRENT_PROJECT_VERSION = 1; 321 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 322 | ENABLE_NS_ASSERTIONS = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 333 | MTL_ENABLE_DEBUG_INFO = NO; 334 | SDKROOT = iphoneos; 335 | TARGETED_DEVICE_FAMILY = "1,2"; 336 | VALIDATE_PRODUCT = YES; 337 | VERSIONING_SYSTEM = "apple-generic"; 338 | VERSION_INFO_PREFIX = ""; 339 | }; 340 | name = Release; 341 | }; 342 | 2A4E8BEA1D775BD200DBAD53 /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | baseConfigurationReference = 3B1AE262A1B99631A9A506E0 /* Pods-mime.debug.xcconfig */; 345 | buildSettings = { 346 | CLANG_ENABLE_MODULES = YES; 347 | DEFINES_MODULE = YES; 348 | DYLIB_COMPATIBILITY_VERSION = 1; 349 | DYLIB_CURRENT_VERSION = 1; 350 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 351 | INFOPLIST_FILE = mime/Info.plist; 352 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = com.jameslintaylor.mime; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | SKIP_INSTALL = YES; 357 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 358 | SWIFT_VERSION = 3.0; 359 | }; 360 | name = Debug; 361 | }; 362 | 2A4E8BEB1D775BD200DBAD53 /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = 389881A372882E9039D0CACA /* Pods-mime.release.xcconfig */; 365 | buildSettings = { 366 | CLANG_ENABLE_MODULES = YES; 367 | DEFINES_MODULE = YES; 368 | DYLIB_COMPATIBILITY_VERSION = 1; 369 | DYLIB_CURRENT_VERSION = 1; 370 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 371 | INFOPLIST_FILE = mime/Info.plist; 372 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 374 | PRODUCT_BUNDLE_IDENTIFIER = com.jameslintaylor.mime; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SKIP_INSTALL = YES; 377 | SWIFT_VERSION = 3.0; 378 | }; 379 | name = Release; 380 | }; 381 | /* End XCBuildConfiguration section */ 382 | 383 | /* Begin XCConfigurationList section */ 384 | 2A4E8BDB1D775BD200DBAD53 /* Build configuration list for PBXProject "mime" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 2A4E8BE71D775BD200DBAD53 /* Debug */, 388 | 2A4E8BE81D775BD200DBAD53 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | 2A4E8BE91D775BD200DBAD53 /* Build configuration list for PBXNativeTarget "mime" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | 2A4E8BEA1D775BD200DBAD53 /* Debug */, 397 | 2A4E8BEB1D775BD200DBAD53 /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | /* End XCConfigurationList section */ 403 | }; 404 | rootObject = 2A4E8BD81D775BD200DBAD53 /* Project object */; 405 | } 406 | -------------------------------------------------------------------------------- /mime.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mime.xcodeproj/xcshareddata/xcschemes/mime.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /mime/Controls.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Controls.swift 3 | // mime 4 | // 5 | // Created by James Taylor on 2016-08-31. 6 | // Copyright © 2016 James Taylor. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIControl: Mime {} 12 | 13 | public extension Mime where Self: UIControl { 14 | 15 | /// implements the objc style `addTarget:selector:forControlEvents` method using swift closures 16 | @discardableResult 17 | func mime_on(_ events: UIControlEvents = .allEvents, closure: @escaping (Self) -> ()) -> Self { 18 | // setup the target, casting sender to Self 19 | let target = Target { closure($0 as! Self) } 20 | addTarget(target, action: Target.handler, for: events) 21 | 22 | // retain the target 23 | targetCache.append(target) 24 | return self 25 | } 26 | 27 | /// removes all target/selectors added through `self.mime_on` 28 | func mime_off() { 29 | targetCache.forEach { 30 | removeTarget($0, action: Target.handler, for: .allEvents) 31 | } 32 | targetCache = [] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mime/Gestures.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Gestures.swift 3 | // mime 4 | // 5 | // Created by James Taylor on 2016-08-31. 6 | // Copyright © 2016 James Taylor. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | prefix operator *< 12 | /// maps a `UIGestureRecognizerState` to GestureState by left bit-shifting 1 13 | /// by `UIGestureRecognizerState.rawValue`... 14 | /// 15 | /// bit shifting by the raw value could be problematic, like if all of a sudden 16 | /// one of these `UIGestureRecognizerState` get a `rawValue` greater than 63... 17 | private prefix func *< (rhs: UIGestureRecognizerState) -> GestureStates { 18 | return GestureStates(rawValue: 1 << rhs.rawValue) 19 | } 20 | 21 | /// 1:1 mapping of `UIGestureRecognizerState` in `OptionSetType` form 22 | public struct GestureStates: OptionSet { 23 | 24 | public let rawValue: Int 25 | public init(rawValue: Int) { 26 | self.rawValue = rawValue 27 | } 28 | 29 | public static let possible = *<.possible 30 | public static let began = *<.began 31 | public static let changed = *<.changed 32 | public static let ended = *<.ended 33 | public static let cancelled = *<.cancelled 34 | public static let failed = *<.failed 35 | 36 | public static let all: GestureStates = [.possible, .began, .changed, .ended, .cancelled, .failed] 37 | } 38 | 39 | private extension GestureStates { 40 | 41 | func contains(_ uiMember: UIGestureRecognizerState) -> Bool { 42 | // tests by reversing the bit shifting operation used to create the state 43 | return ((self.rawValue >> uiMember.rawValue) & 1) == 1 44 | } 45 | } 46 | 47 | extension UIGestureRecognizer: Mime {} 48 | 49 | public extension Mime where Self: UIGestureRecognizer { 50 | 51 | /// implements the objc style `addTarget:selector:` method using swift closures 52 | @discardableResult 53 | func mime_on(_ states: GestureStates = .all, closure: @escaping (Self) -> ()) -> Self { 54 | // setup the target, casting sender to Self 55 | let target = Target { 56 | if states.contains($0.state) { 57 | closure($0 as! Self) 58 | } 59 | } 60 | addTarget(target, action: Target.handler) 61 | 62 | // retain the target 63 | targetCache.append(target) 64 | return self 65 | } 66 | 67 | /// removes all target/selectors added through `self.mime_on` 68 | func mime_off() { 69 | targetCache.forEach { 70 | removeTarget($0, action: Target.handler) 71 | } 72 | targetCache = [] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mime/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mime/Mime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mime.swift 3 | // mime 4 | // 5 | // Created by James Taylor on 2016-08-31. 6 | // Copyright © 2016 James Taylor. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AssociatedObjects 11 | 12 | /// you're not meant to implement this protocol, it's just here to make use of swift generics 13 | public protocol Mime: AssociatedObjects {} 14 | 15 | // implement target cache associated object 16 | internal extension Mime { 17 | 18 | typealias TargetCache = [Target] 19 | 20 | var targetCache: TargetCache { 21 | get { 22 | /// return the existing cache if it exists otherwise create a new one 23 | return ao_get(key: "targetCache") as? TargetCache ?? { 24 | let new = TargetCache() 25 | self.targetCache = new 26 | return new 27 | }() 28 | } 29 | set { 30 | ao_set(newValue, key: "targetCache") 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mime/Target.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Target.swift 3 | // mime 4 | // 5 | // Created by James Taylor on 2016-08-31. 6 | // Copyright © 2016 James Taylor. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// used to link between objc style target-selector and swift's closures 12 | /// 13 | /// delegates it's `handle(_:)` method to the `self.delegate` closure 14 | internal class Target { 15 | 16 | let delegate: (AnyObject) -> () 17 | init(delegate: @escaping (AnyObject) -> ()) { 18 | self.delegate = delegate 19 | } 20 | 21 | /// selector pointing to the `handle(_:) method` 22 | static let handler = #selector(handle(_:)) 23 | 24 | /// just calls `self.delegate` 25 | dynamic func handle(_ sender: AnyObject) { 26 | delegate(sender) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /mime/mime.h: -------------------------------------------------------------------------------- 1 | // 2 | // mime.h 3 | // mime 4 | // 5 | // Created by James Taylor on 2016-08-31. 6 | // Copyright © 2016 James Taylor. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for mime. 12 | FOUNDATION_EXPORT double mimeVersionNumber; 13 | 14 | //! Project version string for mime. 15 | FOUNDATION_EXPORT const unsigned char mimeVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | --------------------------------------------------------------------------------