├── .gitignore ├── Example ├── Podfile ├── Podfile.lock ├── SPLUserActivityExample.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── SPLUserActivityExample.xcscheme ├── SPLUserActivityExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── SPLUserActivityExampleTests │ ├── Info.plist │ └── SPLUserActivityExampleTests.m ├── LICENSE ├── SPLUserActivity.podspec ├── SPLUserActivity ├── SPLUserActivity.h ├── SPLUserActivity.m ├── SPLWebActivity.h └── SPLWebActivity.m ├── circle.yml └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | #Ignore user-specific settings 4 | *.mode1v3 5 | *.mode2v3 6 | *.pbxuser 7 | *.perspectivev3 8 | .xcworkspacedata 9 | 10 | build 11 | .gitattributes 12 | *.xcuserdata* 13 | 14 | DerivedData 15 | Pods 16 | *.xcworkspace 17 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | xcodeproj 'SPLUserActivityExample' 4 | 5 | pod 'SPLUserActivity', :path => '../' 6 | 7 | target "SPLUserActivityExampleTests", :exclusive => true do 8 | pod 'SPLUserActivity', :path => '../' 9 | end 10 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SPLUserActivity (0.1.2) 3 | 4 | DEPENDENCIES: 5 | - SPLUserActivity (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | SPLUserActivity: 9 | :path: ../ 10 | 11 | SPEC CHECKSUMS: 12 | SPLUserActivity: 32c59c4dbb0fdbbf428f3ee46d02e609ab2e3119 13 | 14 | COCOAPODS: 0.36.0 15 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E94A6AA51AA400F7001A7C95 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E94A6AA41AA400F7001A7C95 /* main.m */; }; 11 | E94A6AA81AA400F7001A7C95 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E94A6AA71AA400F7001A7C95 /* AppDelegate.m */; }; 12 | E94A6AAB1AA400F7001A7C95 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E94A6AAA1AA400F7001A7C95 /* ViewController.m */; }; 13 | E94A6AB01AA400F7001A7C95 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E94A6AAF1AA400F7001A7C95 /* Images.xcassets */; }; 14 | E94A6ABF1AA400F7001A7C95 /* SPLUserActivityExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E94A6ABE1AA400F7001A7C95 /* SPLUserActivityExampleTests.m */; }; 15 | FBFF3D387DF500B9ED51B44E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAAD6EAAA12844398FA87724 /* libPods.a */; }; 16 | FF73328DEC5671637EA416FD /* libPods-SPLUserActivityExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA45256FCE1E3ECA148DA79 /* libPods-SPLUserActivityExampleTests.a */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | E94A6AB91AA400F7001A7C95 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = E94A6A971AA400F7001A7C95 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = E94A6A9E1AA400F7001A7C95; 25 | remoteInfo = SPLUserActivityExample; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 0B91D930852E2D4909C2576E /* Pods-SPLUserActivityExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SPLUserActivityExampleTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SPLUserActivityExampleTests/Pods-SPLUserActivityExampleTests.release.xcconfig"; sourceTree = ""; }; 31 | 174E74222B7F2CF3BDC64AEC /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 32 | 4AA45256FCE1E3ECA148DA79 /* libPods-SPLUserActivityExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SPLUserActivityExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 857F2CB4A04C4AC648BBACB8 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 34 | DAAD6EAAA12844398FA87724 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | DDFDC63DF854A857FCE60F8B /* Pods-SPLUserActivityExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SPLUserActivityExampleTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SPLUserActivityExampleTests/Pods-SPLUserActivityExampleTests.debug.xcconfig"; sourceTree = ""; }; 36 | E94A6A9F1AA400F7001A7C95 /* SPLUserActivityExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SPLUserActivityExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | E94A6AA31AA400F7001A7C95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | E94A6AA41AA400F7001A7C95 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | E94A6AA61AA400F7001A7C95 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 40 | E94A6AA71AA400F7001A7C95 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 41 | E94A6AA91AA400F7001A7C95 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 42 | E94A6AAA1AA400F7001A7C95 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 43 | E94A6AAF1AA400F7001A7C95 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 44 | E94A6AB81AA400F7001A7C95 /* SPLUserActivityExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SPLUserActivityExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | E94A6ABD1AA400F7001A7C95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | E94A6ABE1AA400F7001A7C95 /* SPLUserActivityExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPLUserActivityExampleTests.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | E94A6A9C1AA400F7001A7C95 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | FBFF3D387DF500B9ED51B44E /* libPods.a in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | E94A6AB51AA400F7001A7C95 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | FF73328DEC5671637EA416FD /* libPods-SPLUserActivityExampleTests.a in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | B5E470505276B842484900BB /* Frameworks */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | DAAD6EAAA12844398FA87724 /* libPods.a */, 73 | 4AA45256FCE1E3ECA148DA79 /* libPods-SPLUserActivityExampleTests.a */, 74 | ); 75 | name = Frameworks; 76 | sourceTree = ""; 77 | }; 78 | D2DC16EDE2B749F55EE7AAB8 /* Pods */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 857F2CB4A04C4AC648BBACB8 /* Pods.debug.xcconfig */, 82 | 174E74222B7F2CF3BDC64AEC /* Pods.release.xcconfig */, 83 | DDFDC63DF854A857FCE60F8B /* Pods-SPLUserActivityExampleTests.debug.xcconfig */, 84 | 0B91D930852E2D4909C2576E /* Pods-SPLUserActivityExampleTests.release.xcconfig */, 85 | ); 86 | name = Pods; 87 | sourceTree = ""; 88 | }; 89 | E94A6A961AA400F7001A7C95 = { 90 | isa = PBXGroup; 91 | children = ( 92 | E94A6AA11AA400F7001A7C95 /* SPLUserActivityExample */, 93 | E94A6ABB1AA400F7001A7C95 /* SPLUserActivityExampleTests */, 94 | E94A6AA01AA400F7001A7C95 /* Products */, 95 | D2DC16EDE2B749F55EE7AAB8 /* Pods */, 96 | B5E470505276B842484900BB /* Frameworks */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | E94A6AA01AA400F7001A7C95 /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | E94A6A9F1AA400F7001A7C95 /* SPLUserActivityExample.app */, 104 | E94A6AB81AA400F7001A7C95 /* SPLUserActivityExampleTests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | E94A6AA11AA400F7001A7C95 /* SPLUserActivityExample */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | E94A6AA61AA400F7001A7C95 /* AppDelegate.h */, 113 | E94A6AA71AA400F7001A7C95 /* AppDelegate.m */, 114 | E94A6AA91AA400F7001A7C95 /* ViewController.h */, 115 | E94A6AAA1AA400F7001A7C95 /* ViewController.m */, 116 | E94A6AAF1AA400F7001A7C95 /* Images.xcassets */, 117 | E94A6AA21AA400F7001A7C95 /* Supporting Files */, 118 | ); 119 | path = SPLUserActivityExample; 120 | sourceTree = ""; 121 | }; 122 | E94A6AA21AA400F7001A7C95 /* Supporting Files */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | E94A6AA31AA400F7001A7C95 /* Info.plist */, 126 | E94A6AA41AA400F7001A7C95 /* main.m */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | E94A6ABB1AA400F7001A7C95 /* SPLUserActivityExampleTests */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | E94A6ABE1AA400F7001A7C95 /* SPLUserActivityExampleTests.m */, 135 | E94A6ABC1AA400F7001A7C95 /* Supporting Files */, 136 | ); 137 | path = SPLUserActivityExampleTests; 138 | sourceTree = ""; 139 | }; 140 | E94A6ABC1AA400F7001A7C95 /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | E94A6ABD1AA400F7001A7C95 /* Info.plist */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | E94A6A9E1AA400F7001A7C95 /* SPLUserActivityExample */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = E94A6AC21AA400F7001A7C95 /* Build configuration list for PBXNativeTarget "SPLUserActivityExample" */; 154 | buildPhases = ( 155 | E7B05B6716B9946DE2556EFA /* Check Pods Manifest.lock */, 156 | E94A6A9B1AA400F7001A7C95 /* Sources */, 157 | E94A6A9C1AA400F7001A7C95 /* Frameworks */, 158 | E94A6A9D1AA400F7001A7C95 /* Resources */, 159 | 8E424B39610AD63D947CDE75 /* Copy Pods Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = SPLUserActivityExample; 166 | productName = SPLUserActivityExample; 167 | productReference = E94A6A9F1AA400F7001A7C95 /* SPLUserActivityExample.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | E94A6AB71AA400F7001A7C95 /* SPLUserActivityExampleTests */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = E94A6AC51AA400F7001A7C95 /* Build configuration list for PBXNativeTarget "SPLUserActivityExampleTests" */; 173 | buildPhases = ( 174 | C2BCBE8FDDF0B40C07474F2B /* Check Pods Manifest.lock */, 175 | E94A6AB41AA400F7001A7C95 /* Sources */, 176 | E94A6AB51AA400F7001A7C95 /* Frameworks */, 177 | E94A6AB61AA400F7001A7C95 /* Resources */, 178 | E596871DDB4F32EAA3A76B0F /* Copy Pods Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | E94A6ABA1AA400F7001A7C95 /* PBXTargetDependency */, 184 | ); 185 | name = SPLUserActivityExampleTests; 186 | productName = SPLUserActivityExampleTests; 187 | productReference = E94A6AB81AA400F7001A7C95 /* SPLUserActivityExampleTests.xctest */; 188 | productType = "com.apple.product-type.bundle.unit-test"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | E94A6A971AA400F7001A7C95 /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | LastUpgradeCheck = 0610; 197 | ORGANIZATIONNAME = Splinesoft; 198 | TargetAttributes = { 199 | E94A6A9E1AA400F7001A7C95 = { 200 | CreatedOnToolsVersion = 6.1.1; 201 | }; 202 | E94A6AB71AA400F7001A7C95 = { 203 | CreatedOnToolsVersion = 6.1.1; 204 | TestTargetID = E94A6A9E1AA400F7001A7C95; 205 | }; 206 | }; 207 | }; 208 | buildConfigurationList = E94A6A9A1AA400F7001A7C95 /* Build configuration list for PBXProject "SPLUserActivityExample" */; 209 | compatibilityVersion = "Xcode 3.2"; 210 | developmentRegion = English; 211 | hasScannedForEncodings = 0; 212 | knownRegions = ( 213 | en, 214 | Base, 215 | ); 216 | mainGroup = E94A6A961AA400F7001A7C95; 217 | productRefGroup = E94A6AA01AA400F7001A7C95 /* Products */; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | E94A6A9E1AA400F7001A7C95 /* SPLUserActivityExample */, 222 | E94A6AB71AA400F7001A7C95 /* SPLUserActivityExampleTests */, 223 | ); 224 | }; 225 | /* End PBXProject section */ 226 | 227 | /* Begin PBXResourcesBuildPhase section */ 228 | E94A6A9D1AA400F7001A7C95 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | E94A6AB01AA400F7001A7C95 /* Images.xcassets in Resources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | E94A6AB61AA400F7001A7C95 /* Resources */ = { 237 | isa = PBXResourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | /* End PBXResourcesBuildPhase section */ 244 | 245 | /* Begin PBXShellScriptBuildPhase section */ 246 | 8E424B39610AD63D947CDE75 /* Copy Pods Resources */ = { 247 | isa = PBXShellScriptBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | inputPaths = ( 252 | ); 253 | name = "Copy Pods Resources"; 254 | outputPaths = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | shellPath = /bin/sh; 258 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 259 | showEnvVarsInLog = 0; 260 | }; 261 | C2BCBE8FDDF0B40C07474F2B /* Check Pods Manifest.lock */ = { 262 | isa = PBXShellScriptBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | inputPaths = ( 267 | ); 268 | name = "Check Pods Manifest.lock"; 269 | outputPaths = ( 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | shellPath = /bin/sh; 273 | 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"; 274 | showEnvVarsInLog = 0; 275 | }; 276 | E596871DDB4F32EAA3A76B0F /* Copy Pods Resources */ = { 277 | isa = PBXShellScriptBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | ); 281 | inputPaths = ( 282 | ); 283 | name = "Copy Pods Resources"; 284 | outputPaths = ( 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | shellPath = /bin/sh; 288 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SPLUserActivityExampleTests/Pods-SPLUserActivityExampleTests-resources.sh\"\n"; 289 | showEnvVarsInLog = 0; 290 | }; 291 | E7B05B6716B9946DE2556EFA /* Check Pods Manifest.lock */ = { 292 | isa = PBXShellScriptBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ); 296 | inputPaths = ( 297 | ); 298 | name = "Check Pods Manifest.lock"; 299 | outputPaths = ( 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | shellPath = /bin/sh; 303 | 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"; 304 | showEnvVarsInLog = 0; 305 | }; 306 | /* End PBXShellScriptBuildPhase section */ 307 | 308 | /* Begin PBXSourcesBuildPhase section */ 309 | E94A6A9B1AA400F7001A7C95 /* Sources */ = { 310 | isa = PBXSourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | E94A6AAB1AA400F7001A7C95 /* ViewController.m in Sources */, 314 | E94A6AA81AA400F7001A7C95 /* AppDelegate.m in Sources */, 315 | E94A6AA51AA400F7001A7C95 /* main.m in Sources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | E94A6AB41AA400F7001A7C95 /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | E94A6ABF1AA400F7001A7C95 /* SPLUserActivityExampleTests.m in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXSourcesBuildPhase section */ 328 | 329 | /* Begin PBXTargetDependency section */ 330 | E94A6ABA1AA400F7001A7C95 /* PBXTargetDependency */ = { 331 | isa = PBXTargetDependency; 332 | target = E94A6A9E1AA400F7001A7C95 /* SPLUserActivityExample */; 333 | targetProxy = E94A6AB91AA400F7001A7C95 /* PBXContainerItemProxy */; 334 | }; 335 | /* End PBXTargetDependency section */ 336 | 337 | /* Begin XCBuildConfiguration section */ 338 | E94A6AC01AA400F7001A7C95 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_WARN_BOOL_CONVERSION = YES; 347 | CLANG_WARN_CONSTANT_CONVERSION = YES; 348 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INT_CONVERSION = YES; 352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 356 | COPY_PHASE_STRIP = NO; 357 | ENABLE_STRICT_OBJC_MSGSEND = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_DYNAMIC_NO_PIC = NO; 360 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 361 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 362 | GCC_OPTIMIZATION_LEVEL = 0; 363 | GCC_PREPROCESSOR_DEFINITIONS = ( 364 | "DEBUG=1", 365 | "$(inherited)", 366 | ); 367 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 375 | MTL_ENABLE_DEBUG_INFO = YES; 376 | ONLY_ACTIVE_ARCH = YES; 377 | SDKROOT = iphoneos; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | }; 380 | name = Debug; 381 | }; 382 | E94A6AC11AA400F7001A7C95 /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ALWAYS_SEARCH_USER_PATHS = NO; 386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 387 | CLANG_CXX_LIBRARY = "libc++"; 388 | CLANG_ENABLE_MODULES = YES; 389 | CLANG_ENABLE_OBJC_ARC = YES; 390 | CLANG_WARN_BOOL_CONVERSION = YES; 391 | CLANG_WARN_CONSTANT_CONVERSION = YES; 392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 393 | CLANG_WARN_EMPTY_BODY = YES; 394 | CLANG_WARN_ENUM_CONVERSION = YES; 395 | CLANG_WARN_INT_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_UNREACHABLE_CODE = YES; 398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 399 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 400 | COPY_PHASE_STRIP = YES; 401 | ENABLE_NS_ASSERTIONS = NO; 402 | ENABLE_STRICT_OBJC_MSGSEND = YES; 403 | GCC_C_LANGUAGE_STANDARD = gnu99; 404 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 405 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 406 | GCC_WARN_UNDECLARED_SELECTOR = YES; 407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 408 | GCC_WARN_UNUSED_FUNCTION = YES; 409 | GCC_WARN_UNUSED_VARIABLE = YES; 410 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 411 | MTL_ENABLE_DEBUG_INFO = NO; 412 | SDKROOT = iphoneos; 413 | TARGETED_DEVICE_FAMILY = "1,2"; 414 | VALIDATE_PRODUCT = YES; 415 | }; 416 | name = Release; 417 | }; 418 | E94A6AC31AA400F7001A7C95 /* Debug */ = { 419 | isa = XCBuildConfiguration; 420 | baseConfigurationReference = 857F2CB4A04C4AC648BBACB8 /* Pods.debug.xcconfig */; 421 | buildSettings = { 422 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 423 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 424 | INFOPLIST_FILE = SPLUserActivityExample/Info.plist; 425 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | }; 428 | name = Debug; 429 | }; 430 | E94A6AC41AA400F7001A7C95 /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | baseConfigurationReference = 174E74222B7F2CF3BDC64AEC /* Pods.release.xcconfig */; 433 | buildSettings = { 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 436 | INFOPLIST_FILE = SPLUserActivityExample/Info.plist; 437 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | }; 440 | name = Release; 441 | }; 442 | E94A6AC61AA400F7001A7C95 /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | baseConfigurationReference = DDFDC63DF854A857FCE60F8B /* Pods-SPLUserActivityExampleTests.debug.xcconfig */; 445 | buildSettings = { 446 | BUNDLE_LOADER = "$(TEST_HOST)"; 447 | FRAMEWORK_SEARCH_PATHS = ( 448 | "$(SDKROOT)/Developer/Library/Frameworks", 449 | "$(inherited)", 450 | ); 451 | GCC_PREPROCESSOR_DEFINITIONS = ( 452 | "DEBUG=1", 453 | "$(inherited)", 454 | ); 455 | INFOPLIST_FILE = SPLUserActivityExampleTests/Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SPLUserActivityExample.app/SPLUserActivityExample"; 459 | }; 460 | name = Debug; 461 | }; 462 | E94A6AC71AA400F7001A7C95 /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | baseConfigurationReference = 0B91D930852E2D4909C2576E /* Pods-SPLUserActivityExampleTests.release.xcconfig */; 465 | buildSettings = { 466 | BUNDLE_LOADER = "$(TEST_HOST)"; 467 | FRAMEWORK_SEARCH_PATHS = ( 468 | "$(SDKROOT)/Developer/Library/Frameworks", 469 | "$(inherited)", 470 | ); 471 | INFOPLIST_FILE = SPLUserActivityExampleTests/Info.plist; 472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 473 | PRODUCT_NAME = "$(TARGET_NAME)"; 474 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SPLUserActivityExample.app/SPLUserActivityExample"; 475 | }; 476 | name = Release; 477 | }; 478 | /* End XCBuildConfiguration section */ 479 | 480 | /* Begin XCConfigurationList section */ 481 | E94A6A9A1AA400F7001A7C95 /* Build configuration list for PBXProject "SPLUserActivityExample" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | E94A6AC01AA400F7001A7C95 /* Debug */, 485 | E94A6AC11AA400F7001A7C95 /* Release */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | E94A6AC21AA400F7001A7C95 /* Build configuration list for PBXNativeTarget "SPLUserActivityExample" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | E94A6AC31AA400F7001A7C95 /* Debug */, 494 | E94A6AC41AA400F7001A7C95 /* Release */, 495 | ); 496 | defaultConfigurationIsVisible = 0; 497 | defaultConfigurationName = Release; 498 | }; 499 | E94A6AC51AA400F7001A7C95 /* Build configuration list for PBXNativeTarget "SPLUserActivityExampleTests" */ = { 500 | isa = XCConfigurationList; 501 | buildConfigurations = ( 502 | E94A6AC61AA400F7001A7C95 /* Debug */, 503 | E94A6AC71AA400F7001A7C95 /* Release */, 504 | ); 505 | defaultConfigurationIsVisible = 0; 506 | defaultConfigurationName = Release; 507 | }; 508 | /* End XCConfigurationList section */ 509 | }; 510 | rootObject = E94A6A971AA400F7001A7C95 /* Project object */; 511 | } 512 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample.xcodeproj/xcshareddata/xcschemes/SPLUserActivityExample.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 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SPLUserActivityExample 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SPLUserActivityExample 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application 20 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | 22 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 23 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 24 | [self.window makeKeyAndVisible]; 25 | 26 | return YES; 27 | } 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "ipad", 6 | "minimum-system-version" : "7.0", 7 | "extent" : "full-screen", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "landscape", 12 | "idiom" : "ipad", 13 | "minimum-system-version" : "7.0", 14 | "extent" : "full-screen", 15 | "scale" : "1x" 16 | }, 17 | { 18 | "orientation" : "landscape", 19 | "idiom" : "ipad", 20 | "minimum-system-version" : "7.0", 21 | "extent" : "full-screen", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "orientation" : "portrait", 26 | "idiom" : "iphone", 27 | "minimum-system-version" : "7.0", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "minimum-system-version" : "7.0", 34 | "subtype" : "retina4", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "orientation" : "portrait", 39 | "idiom" : "ipad", 40 | "minimum-system-version" : "7.0", 41 | "extent" : "full-screen", 42 | "scale" : "1x" 43 | } 44 | ], 45 | "info" : { 46 | "version" : 1, 47 | "author" : "xcode" 48 | } 49 | } -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | SPL.$(PRODUCT_NAME:rfc1034identifier) 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 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SPLUserActivityExample 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SPLUserActivityExample 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | @import WebKit; 11 | 12 | #import 13 | 14 | @interface ViewController () 15 | 16 | @property (nonatomic, strong) WKWebView *webView; 17 | @property (nonatomic, strong) SPLWebActivity *activity; 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (instancetype)init { 24 | if ((self = [super init])) { 25 | 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | _webView = [[WKWebView alloc] initWithFrame:self.view.bounds 35 | configuration:[WKWebViewConfiguration new]]; 36 | self.webView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 37 | self.webView.navigationDelegate = self; 38 | 39 | [self.view addSubview:self.webView]; 40 | 41 | [self.webView loadRequest: 42 | [NSURLRequest requestWithURL: 43 | [NSURL URLWithString:@"http://splinesoft.net"]]]; 44 | 45 | _activity = [SPLWebActivity activityWithWKWebView:self.webView]; 46 | } 47 | 48 | - (void)viewWillDisappear:(BOOL)animated { 49 | [super viewWillDisappear:animated]; 50 | 51 | [self.activity invalidate]; 52 | } 53 | 54 | #pragma mark - WKNavigationDelegate 55 | 56 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 57 | self.title = webView.title; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SPLUserActivityExample 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/SPLUserActivityExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | SPL.$(PRODUCT_NAME:rfc1034identifier) 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/SPLUserActivityExampleTests/SPLUserActivityExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPLUserActivityExampleTests.m 3 | // SPLUserActivityExampleTests 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | @import XCTest; 11 | @import WebKit; 12 | #import 13 | 14 | @interface SPLUserActivityExampleTests : XCTestCase 15 | 16 | @end 17 | 18 | @implementation SPLUserActivityExampleTests 19 | { 20 | SPLWebActivity *webActivity; 21 | WKWebView *webView; 22 | } 23 | 24 | - (void)setUp { 25 | [super setUp]; 26 | 27 | webView = [[WKWebView alloc] initWithFrame:CGRectZero 28 | configuration:[WKWebViewConfiguration new]]; 29 | 30 | webActivity = [SPLWebActivity activityWithWKWebView:webView]; 31 | } 32 | 33 | - (void)tearDown { 34 | [super tearDown]; 35 | 36 | [webActivity invalidate]; 37 | webActivity = nil; 38 | } 39 | 40 | - (void)testInitializable { 41 | XCTAssertNotNil(webActivity, @"activity should initialize"); 42 | XCTAssertNotNil(webActivity.userActivity, @"activity should create a user activity"); 43 | } 44 | 45 | - (void)testNeedsUpdate { 46 | [webActivity setNeedsUpdate]; 47 | XCTAssertTrue(webActivity.userActivity.needsSave, @"Activity should mark as needing update"); 48 | } 49 | 50 | - (void)testActivityURL { 51 | NSURL *url = [NSURL URLWithString:@"http://splinesoft.net"]; 52 | 53 | SPLWebActivity *URLactivity = [SPLWebActivity activityWithURL:url]; 54 | 55 | XCTAssertEqualObjects(url, URLactivity.userActivity.webpageURL, @"URL should match"); 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Jonathan Hersh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /SPLUserActivity.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SPLUserActivity" 3 | s.version = "0.1.2" 4 | s.summary = "Easily adopt Handoff for different types of activities." 5 | s.homepage = "https://github.com/splinesoft/SPLUserActivity" 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { "Jonathan Hersh" => "jon@her.sh" } 8 | s.source = { :git => "https://github.com/splinesoft/SPLUserActivity.git", :tag => s.version.to_s } 9 | s.platform = :ios, '8.0' 10 | s.requires_arc = true 11 | s.source_files = 'SPLUserActivity/*.{h,m}' 12 | s.frameworks = 'Foundation', 'UIKit', 'WebKit' 13 | s.social_media_url = 'https://twitter.com/jhersh' 14 | s.compiler_flags = '-fmodules' 15 | end 16 | -------------------------------------------------------------------------------- /SPLUserActivity/SPLUserActivity.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPLUserActivity.h 3 | // SPLUserActivity 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | /** 12 | * An abstract superclass that manages a user activity for Handoff. 13 | * Don't use this class directly except to subclass - instead, see 14 | * @c SPLWebActivity. 15 | */ 16 | @interface SPLUserActivity : NSObject 17 | 18 | /** 19 | * Initialize a user activity with the specified type. 20 | * 21 | * @param type type of the @c NSUserActivity 22 | * 23 | * @return an initialized activity 24 | */ 25 | - (instancetype)initWithType:(NSString *)type; 26 | 27 | /** 28 | * The user activity managed by this object. 29 | */ 30 | @property (nonatomic, strong, readonly) NSUserActivity *userActivity; 31 | 32 | /** 33 | * Optional block called when the user activity is continued on another device. 34 | */ 35 | @property (nonatomic, copy) dispatch_block_t activityContinuedBlock; 36 | 37 | /** 38 | * Invalidate the activity when it is no longer relevant or needed. 39 | * Once invalidated, the activity cannot be reused nor made current. 40 | */ 41 | - (void) invalidate; 42 | 43 | /** 44 | * Mark the activity as needing to be updated from your `UIWebView` or another 45 | * activity source. 46 | */ 47 | - (void) setNeedsUpdate; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /SPLUserActivity/SPLUserActivity.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPLUserActivity.m 3 | // SPLUserActivity 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SPLUserActivity.h" 10 | 11 | @interface SPLUserActivity () 12 | 13 | @end 14 | 15 | @implementation SPLUserActivity 16 | 17 | - (instancetype)initWithType:(NSString *)type { 18 | if ((self = [super init])) { 19 | _userActivity = [[NSUserActivity alloc] initWithActivityType:type]; 20 | self.userActivity.delegate = self; 21 | } 22 | 23 | return self; 24 | } 25 | 26 | - (void)dealloc { 27 | _activityContinuedBlock = nil; 28 | _userActivity.delegate = nil; 29 | [self invalidate]; 30 | } 31 | 32 | #pragma mark - Lifecycle 33 | 34 | - (void)invalidate { 35 | [self.userActivity invalidate]; 36 | } 37 | 38 | - (void)setNeedsUpdate { 39 | self.userActivity.needsSave = YES; 40 | } 41 | 42 | #pragma mark - NSUserActivityDelegate 43 | 44 | - (void)userActivityWillSave:(NSUserActivity *)userActivity { 45 | 46 | } 47 | 48 | - (void)userActivityWasContinued:(NSUserActivity *)userActivity { 49 | if (self.activityContinuedBlock) { 50 | self.activityContinuedBlock(); 51 | } 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /SPLUserActivity/SPLWebActivity.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPLWebActivity.h 3 | // SPLUserActivity 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SPLUserActivity.h" 10 | @import UIKit; 11 | @import WebKit; 12 | 13 | @interface SPLWebActivity : SPLUserActivity 14 | 15 | /** 16 | * Create an activity that can continue a web browsing session on another 17 | * device. The activity is populated with the current URL and page title from the 18 | * @c WKWebview specified. 19 | * 20 | * The activity uses KVO on the @c WKWebView to automatically update its 21 | * URL and document title as the webview navigates between pages. 22 | * 23 | * @param webView the webview to use for this activity 24 | * 25 | * @return an initialized activity 26 | */ 27 | + (instancetype) activityWithWKWebView:(WKWebView *)webView; 28 | 29 | /** 30 | * Create an activity that can continue a web browsing session on another 31 | * device. The activity is populated with the current URL and page title from the 32 | * @c UIWebview specified. 33 | * 34 | * You must notify the activity using @c setNeedsUpdate when the UIWebView 35 | * navigates to a new page. 36 | * 37 | * @param webView the webview to use for this activity 38 | * 39 | * @return an initialized activity 40 | */ 41 | + (instancetype) activityWithUIWebView:(UIWebView *)webView; 42 | 43 | /** 44 | * Create an activity that can start browsing a URL on another device. 45 | * 46 | * @param url the URL to broadcast with Handoff 47 | * 48 | * @return an initialized activity 49 | */ 50 | + (instancetype) activityWithURL:(NSURL *)url; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /SPLUserActivity/SPLWebActivity.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPLWebActivity.m 3 | // SPLWebActivity 4 | // 5 | // Created by Jonathan Hersh on 3/1/15. 6 | // Copyright (c) 2015 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SPLWebActivity.h" 10 | 11 | static char SPLWebActivityContext; 12 | 13 | @interface SPLWebActivity () 14 | 15 | @property (nonatomic, weak) UIWebView *webView; 16 | @property (nonatomic, weak) WKWebView *webKitWebView; 17 | 18 | @end 19 | 20 | @implementation SPLWebActivity 21 | 22 | + (instancetype)activityWithUIWebView:(UIWebView *)webView { 23 | NSParameterAssert(webView); 24 | 25 | SPLWebActivity *activity = [[self alloc] initWithType:NSUserActivityTypeBrowsingWeb]; 26 | activity.userActivity.webpageURL = webView.request.URL; 27 | activity.userActivity.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 28 | activity.webView = webView; 29 | 30 | [activity.userActivity becomeCurrent]; 31 | 32 | return activity; 33 | } 34 | 35 | + (instancetype)activityWithWKWebView:(WKWebView *)webView { 36 | NSParameterAssert(webView); 37 | 38 | SPLWebActivity *activity = [[self alloc] initWithType:NSUserActivityTypeBrowsingWeb]; 39 | activity.webKitWebView = webView; 40 | 41 | [webView addObserver:activity 42 | forKeyPath:NSStringFromSelector(@selector(URL)) 43 | options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew 44 | context:&SPLWebActivityContext]; 45 | 46 | [webView addObserver:activity 47 | forKeyPath:NSStringFromSelector(@selector(title)) 48 | options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew 49 | context:&SPLWebActivityContext]; 50 | 51 | [activity.userActivity becomeCurrent]; 52 | 53 | return activity; 54 | } 55 | 56 | + (instancetype)activityWithURL:(NSURL *)url { 57 | NSParameterAssert(url); 58 | 59 | SPLWebActivity *activity = [[self alloc] initWithType:NSUserActivityTypeBrowsingWeb]; 60 | activity.userActivity.webpageURL = url; 61 | 62 | [activity.userActivity becomeCurrent]; 63 | 64 | return activity; 65 | } 66 | 67 | #pragma mark - Lifecycle 68 | 69 | - (void)invalidate { 70 | [super invalidate]; 71 | 72 | [self.webKitWebView removeObserver:self 73 | forKeyPath:NSStringFromSelector(@selector(URL)) 74 | context:&SPLWebActivityContext]; 75 | [self.webKitWebView removeObserver:self 76 | forKeyPath:NSStringFromSelector(@selector(title)) 77 | context:&SPLWebActivityContext]; 78 | } 79 | 80 | #pragma mark - NSUserActivityDelegate 81 | 82 | - (void)userActivityWillSave:(NSUserActivity *)userActivity { 83 | [super userActivityWillSave:userActivity]; 84 | 85 | dispatch_async(dispatch_get_main_queue(), ^{ 86 | if (self.webKitWebView) { 87 | self.userActivity.webpageURL = self.webKitWebView.URL; 88 | self.userActivity.title = self.webKitWebView.title; 89 | } 90 | 91 | if (self.webView) { 92 | self.userActivity.webpageURL = self.webView.request.URL; 93 | self.userActivity.title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 94 | } 95 | }); 96 | } 97 | 98 | #pragma mark - KVO 99 | 100 | - (void)observeValueForKeyPath:(NSString *)keyPath 101 | ofObject:(id)object 102 | change:(NSDictionary *)change 103 | context:(void *)context { 104 | 105 | if (context == &SPLWebActivityContext) { 106 | id newValue = change[NSKeyValueChangeNewKey]; 107 | 108 | if (!newValue || [newValue isEqual:[NSNull null]]) { 109 | return; 110 | } 111 | 112 | if ([keyPath isEqualToString:NSStringFromSelector(@selector(URL))]) { 113 | self.userActivity.webpageURL = newValue; 114 | } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(title))]) { 115 | self.userActivity.title = newValue; 116 | } 117 | 118 | [self setNeedsUpdate]; 119 | } else { 120 | [super observeValueForKeyPath:keyPath 121 | ofObject:object 122 | change:change 123 | context:context]; 124 | } 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | environment: 3 | LANG: en_US.UTF-8 4 | LC_CTYPE: en_US.UTF-8 5 | xcode: 6 | version: "6.3.1" 7 | dependencies: 8 | pre: 9 | - xcrun instruments -w "iPhone 6 (8.3 Simulator)" || exit 0 10 | override: 11 | - sudo gem install cocoapods xcpretty obcd -N 12 | - pod install --project-directory=Example 13 | test: 14 | override: 15 | - set -o pipefail && xcodebuild -workspace Example/SPLUserActivityExample.xcworkspace -scheme 16 | SPLUserActivityExample -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6" 17 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES clean test | xcpretty -c 18 | --report junit --output ${CIRCLE_TEST_REPORTS}/junit.xml 19 | - pod lib lint --quick 20 | - obcd --path SPLUserActivity find HeaderStyle 21 | deployment: 22 | codecov: 23 | branch: /.*/ 24 | commands: 25 | - bash <(curl -s https://codecov.io/bash) 26 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SPLUserActivity 2 | 3 | [![Circle CI](https://circleci.com/gh/splinesoft/SPLUserActivity.svg?style=svg&circle-token=d7b0f2b0e1b33492be0a6cee7eb94c6e904ebe17)](https://circleci.com/gh/splinesoft/SPLUserActivity) [![Coverage Status](http://codecov.io/github/splinesoft/SPLUserActivity/coverage.svg?branch=master)](http://codecov.io/github/splinesoft/SPLUserActivity?branch=master) 4 | 5 | [Handoff](https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html) is a powerful new feature in iOS 8 and OS X 10.10 that allows a user to begin an activity on one device and continue it on another device signed into the same iCloud account. 6 | 7 | `SPLUserActivity` is a collection of objects that make it easy to adopt Handoff for different types of activities. 8 | 9 | `SPLUserActivity` powers Handoff for the web browser in my app [MUDRammer - A Modern MUD Client for iPhone and iPad](https://itunes.apple.com/us/app/mudrammer-a-modern-mud-client/id597157072?mt=8). 10 | 11 | ## `SPLWebActivity` 12 | 13 | `SPLWebActivity` powers a Handoff activity for web browsing. It allows a user to continue browsing from the same web page on another device. 14 | 15 | ### Examples 16 | 17 | Check out `Example` for a simple app that displays a `WKWebView` and broadcasts a Handoff activity as the user browses between web pages. Note that Handoff won't function in the simulator - you'll need to run it on a device. 18 | 19 | ### `WKWebView` 20 | 21 | If your app uses a `WKWebView`, initialize a `SPLWebActivity` by passing your webview: 22 | 23 | ```objc 24 | WKWebView *myWebView = ... 25 | 26 | SPLWebActivity *webActivity = [SPLWebActivity activityWithWKWebView:myWebView]; 27 | ``` 28 | 29 | That's it -- you're done! `SPLWebActivity` will start broadcasting a Handoff event right away. Thanks to the magic of KVO, `SPLWebActivity` will observe your `WKWebView` and will automatically update its activity URL and page title as the user navigates between web pages. 30 | 31 | ### `UIWebView` 32 | 33 | If your app uses a `UIWebView`, initialize a `SPLWebActivity` by passing your webview: 34 | 35 | ```objc 36 | UIWebView *myWebView = ... 37 | 38 | self.webActivity = [SPLWebActivity activityWithUIWebView:myWebView]; 39 | ``` 40 | 41 | As with `WKWebView`, `SPLWebActivity` will begin broadcasting a Handoff event right away. However, `UIWebView` does not conform to KVO, so you'll need to tell `SPLWebActivity` to update itself as the user navigates between web pages: 42 | 43 | ```objc 44 | #pragma mark - UIWebViewDelegate 45 | 46 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 47 | [self.webActivity setNeedsUpdate]; 48 | } 49 | ``` 50 | 51 | After receiving a `setNeedsUpdate` message, `SPLWebActivity` will query your `UIWebView` for the user's current URL and update its Handoff activity. 52 | 53 | ### URLs 54 | 55 | `SPLWebActivity` also allows your user to browse any URL on another device -- no webview required. 56 | 57 | ```objc 58 | SPLWebActivity *myActivity = [SPLWebActivity activityWithURL:[NSURL URLWithString:@"http://splinesoft.net"]]; 59 | ``` 60 | 61 | ## Continue Events 62 | 63 | You may optionally specify a block that will be called when the user continues an activity on another device. 64 | 65 | ```objc 66 | myActivity.activityContinuedBlock = ^{ 67 | NSLog(@"I've just continued an activity!"); 68 | }; 69 | ``` 70 | 71 | ## Cleanup 72 | 73 | When your user activity is no longer relevant -- perhaps when your webview is popped off the navigation stack, or when the activity is no longer available -- make sure to invalidate the activity. 74 | 75 | ```objc 76 | [myActivity invalidate]; 77 | ``` 78 | 79 | An invalidated activity can no longer `becomeCurrent` and cannot be reused. If you'd like to broadcast a Handoff event again, create a new instance of `SPLWebActivity`. 80 | 81 | ## Install 82 | 83 | Install with [CocoaPods](http://cocoapods.org). Add to your `Podfile`: 84 | 85 | ``` 86 | pod 'SPLUserActivity' 87 | ``` 88 | 89 | ## Thanks! 90 | 91 | `SPLUserActivity` is a [@jhersh](https://github.com/jhersh) production -- ([electronic mail](mailto:jon@her.sh) | [@jhersh](https://twitter.com/jhersh)) --------------------------------------------------------------------------------