├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcuserdata │ │ └── aemaeth.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── Example.xcscheme │ │ └── xcschememanagement.plist ├── Example │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift └── ExampleTests │ ├── ExampleTests.swift │ └── Info.plist ├── LICENSE ├── README.md └── SwiftWeiboKit ├── SwiftWeiboKit.swift └── SwiftyJSON.swift /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 17C966F41957EB4300853D20 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C966F31957EB4300853D20 /* SwiftyJSON.swift */; }; 11 | 17EC791E194C04750049F1A3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17EC791D194C04750049F1A3 /* AppDelegate.swift */; }; 12 | 17EC7920194C04750049F1A3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17EC791F194C04750049F1A3 /* ViewController.swift */; }; 13 | 17EC7923194C04750049F1A3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 17EC7921194C04750049F1A3 /* Main.storyboard */; }; 14 | 17EC7925194C04750049F1A3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 17EC7924194C04750049F1A3 /* Images.xcassets */; }; 15 | 17EC7931194C04750049F1A3 /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17EC7930194C04750049F1A3 /* ExampleTests.swift */; }; 16 | 17EC793C194C04B20049F1A3 /* SwiftWeiboKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17EC793B194C04B20049F1A3 /* SwiftWeiboKit.swift */; }; 17 | 17EC793D194C04B20049F1A3 /* SwiftWeiboKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17EC793B194C04B20049F1A3 /* SwiftWeiboKit.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 17EC792B194C04750049F1A3 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 17EC7910194C04750049F1A3 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 17EC7917194C04750049F1A3; 26 | remoteInfo = Example; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 17C966F31957EB4300853D20 /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = ../SwiftWeiboKit/SwiftyJSON.swift; sourceTree = ""; }; 32 | 17EC7918194C04750049F1A3 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 17EC791C194C04750049F1A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 17EC791D194C04750049F1A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 17EC791F194C04750049F1A3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | 17EC7922194C04750049F1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | 17EC7924194C04750049F1A3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 38 | 17EC792A194C04750049F1A3 /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 17EC792F194C04750049F1A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 17EC7930194C04750049F1A3 /* ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTests.swift; sourceTree = ""; }; 41 | 17EC793B194C04B20049F1A3 /* SwiftWeiboKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftWeiboKit.swift; path = ../SwiftWeiboKit/SwiftWeiboKit.swift; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 17EC7915194C04750049F1A3 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | 17EC7927194C04750049F1A3 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 17EC790F194C04750049F1A3 = { 63 | isa = PBXGroup; 64 | children = ( 65 | 17EC793A194C04930049F1A3 /* SwiftWeiboKit */, 66 | 17EC791A194C04750049F1A3 /* Example */, 67 | 17EC792D194C04750049F1A3 /* ExampleTests */, 68 | 17EC7919194C04750049F1A3 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 17EC7919194C04750049F1A3 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 17EC7918194C04750049F1A3 /* Example.app */, 76 | 17EC792A194C04750049F1A3 /* ExampleTests.xctest */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 17EC791A194C04750049F1A3 /* Example */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 17EC791D194C04750049F1A3 /* AppDelegate.swift */, 85 | 17EC791F194C04750049F1A3 /* ViewController.swift */, 86 | 17EC7921194C04750049F1A3 /* Main.storyboard */, 87 | 17EC7924194C04750049F1A3 /* Images.xcassets */, 88 | 17EC791B194C04750049F1A3 /* Supporting Files */, 89 | ); 90 | path = Example; 91 | sourceTree = ""; 92 | }; 93 | 17EC791B194C04750049F1A3 /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 17EC791C194C04750049F1A3 /* Info.plist */, 97 | ); 98 | name = "Supporting Files"; 99 | sourceTree = ""; 100 | }; 101 | 17EC792D194C04750049F1A3 /* ExampleTests */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 17EC7930194C04750049F1A3 /* ExampleTests.swift */, 105 | 17EC792E194C04750049F1A3 /* Supporting Files */, 106 | ); 107 | path = ExampleTests; 108 | sourceTree = ""; 109 | }; 110 | 17EC792E194C04750049F1A3 /* Supporting Files */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 17EC792F194C04750049F1A3 /* Info.plist */, 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | 17EC793A194C04930049F1A3 /* SwiftWeiboKit */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 17C966F31957EB4300853D20 /* SwiftyJSON.swift */, 122 | 17EC793B194C04B20049F1A3 /* SwiftWeiboKit.swift */, 123 | ); 124 | name = SwiftWeiboKit; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 17EC7917194C04750049F1A3 /* Example */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 17EC7934194C04750049F1A3 /* Build configuration list for PBXNativeTarget "Example" */; 133 | buildPhases = ( 134 | 17EC7914194C04750049F1A3 /* Sources */, 135 | 17EC7915194C04750049F1A3 /* Frameworks */, 136 | 17EC7916194C04750049F1A3 /* Resources */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | ); 142 | name = Example; 143 | productName = Example; 144 | productReference = 17EC7918194C04750049F1A3 /* Example.app */; 145 | productType = "com.apple.product-type.application"; 146 | }; 147 | 17EC7929194C04750049F1A3 /* ExampleTests */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 17EC7937194C04750049F1A3 /* Build configuration list for PBXNativeTarget "ExampleTests" */; 150 | buildPhases = ( 151 | 17EC7926194C04750049F1A3 /* Sources */, 152 | 17EC7927194C04750049F1A3 /* Frameworks */, 153 | 17EC7928194C04750049F1A3 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | 17EC792C194C04750049F1A3 /* PBXTargetDependency */, 159 | ); 160 | name = ExampleTests; 161 | productName = ExampleTests; 162 | productReference = 17EC792A194C04750049F1A3 /* ExampleTests.xctest */; 163 | productType = "com.apple.product-type.bundle.unit-test"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 17EC7910194C04750049F1A3 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastUpgradeCheck = 0600; 172 | ORGANIZATIONNAME = "Ruoyu Fu"; 173 | TargetAttributes = { 174 | 17EC7917194C04750049F1A3 = { 175 | CreatedOnToolsVersion = 6.0; 176 | }; 177 | 17EC7929194C04750049F1A3 = { 178 | CreatedOnToolsVersion = 6.0; 179 | TestTargetID = 17EC7917194C04750049F1A3; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 17EC7913194C04750049F1A3 /* Build configuration list for PBXProject "Example" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | Base, 190 | ); 191 | mainGroup = 17EC790F194C04750049F1A3; 192 | productRefGroup = 17EC7919194C04750049F1A3 /* Products */; 193 | projectDirPath = ""; 194 | projectRoot = ""; 195 | targets = ( 196 | 17EC7917194C04750049F1A3 /* Example */, 197 | 17EC7929194C04750049F1A3 /* ExampleTests */, 198 | ); 199 | }; 200 | /* End PBXProject section */ 201 | 202 | /* Begin PBXResourcesBuildPhase section */ 203 | 17EC7916194C04750049F1A3 /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 17EC7923194C04750049F1A3 /* Main.storyboard in Resources */, 208 | 17EC7925194C04750049F1A3 /* Images.xcassets in Resources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | 17EC7928194C04750049F1A3 /* Resources */ = { 213 | isa = PBXResourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXResourcesBuildPhase section */ 220 | 221 | /* Begin PBXSourcesBuildPhase section */ 222 | 17EC7914194C04750049F1A3 /* Sources */ = { 223 | isa = PBXSourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 17EC7920194C04750049F1A3 /* ViewController.swift in Sources */, 227 | 17EC791E194C04750049F1A3 /* AppDelegate.swift in Sources */, 228 | 17EC793C194C04B20049F1A3 /* SwiftWeiboKit.swift in Sources */, 229 | 17C966F41957EB4300853D20 /* SwiftyJSON.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | 17EC7926194C04750049F1A3 /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 17EC7931194C04750049F1A3 /* ExampleTests.swift in Sources */, 238 | 17EC793D194C04B20049F1A3 /* SwiftWeiboKit.swift in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXTargetDependency section */ 245 | 17EC792C194C04750049F1A3 /* PBXTargetDependency */ = { 246 | isa = PBXTargetDependency; 247 | target = 17EC7917194C04750049F1A3 /* Example */; 248 | targetProxy = 17EC792B194C04750049F1A3 /* PBXContainerItemProxy */; 249 | }; 250 | /* End PBXTargetDependency section */ 251 | 252 | /* Begin PBXVariantGroup section */ 253 | 17EC7921194C04750049F1A3 /* Main.storyboard */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | 17EC7922194C04750049F1A3 /* Base */, 257 | ); 258 | name = Main.storyboard; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 17EC7932194C04750049F1A3 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_UNREACHABLE_CODE = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 282 | COPY_PHASE_STRIP = NO; 283 | ENABLE_STRICT_OBJC_MSGSEND = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_OPTIMIZATION_LEVEL = 0; 287 | GCC_PREPROCESSOR_DEFINITIONS = ( 288 | "DEBUG=1", 289 | "$(inherited)", 290 | ); 291 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 299 | METAL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | }; 304 | name = Debug; 305 | }; 306 | 17EC7933194C04750049F1A3 /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BOOL_CONVERSION = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = YES; 325 | ENABLE_NS_ASSERTIONS = NO; 326 | ENABLE_STRICT_OBJC_MSGSEND = YES; 327 | GCC_C_LANGUAGE_STANDARD = gnu99; 328 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 329 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 330 | GCC_WARN_UNDECLARED_SELECTOR = YES; 331 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 332 | GCC_WARN_UNUSED_FUNCTION = YES; 333 | GCC_WARN_UNUSED_VARIABLE = YES; 334 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 335 | METAL_ENABLE_DEBUG_INFO = NO; 336 | SDKROOT = iphoneos; 337 | VALIDATE_PRODUCT = YES; 338 | }; 339 | name = Release; 340 | }; 341 | 17EC7935194C04750049F1A3 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 346 | INFOPLIST_FILE = Example/Info.plist; 347 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 348 | PRODUCT_NAME = "$(TARGET_NAME)"; 349 | }; 350 | name = Debug; 351 | }; 352 | 17EC7936194C04750049F1A3 /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 357 | INFOPLIST_FILE = Example/Info.plist; 358 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 359 | PRODUCT_NAME = "$(TARGET_NAME)"; 360 | }; 361 | name = Release; 362 | }; 363 | 17EC7938194C04750049F1A3 /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(SDKROOT)/Developer/Library/Frameworks", 369 | "$(inherited)", 370 | ); 371 | GCC_PREPROCESSOR_DEFINITIONS = ( 372 | "DEBUG=1", 373 | "$(inherited)", 374 | ); 375 | INFOPLIST_FILE = ExampleTests/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 377 | METAL_ENABLE_DEBUG_INFO = YES; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | TEST_HOST = "$(BUNDLE_LOADER)"; 380 | }; 381 | name = Debug; 382 | }; 383 | 17EC7939194C04750049F1A3 /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 387 | FRAMEWORK_SEARCH_PATHS = ( 388 | "$(SDKROOT)/Developer/Library/Frameworks", 389 | "$(inherited)", 390 | ); 391 | INFOPLIST_FILE = ExampleTests/Info.plist; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 393 | METAL_ENABLE_DEBUG_INFO = NO; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | TEST_HOST = "$(BUNDLE_LOADER)"; 396 | }; 397 | name = Release; 398 | }; 399 | /* End XCBuildConfiguration section */ 400 | 401 | /* Begin XCConfigurationList section */ 402 | 17EC7913194C04750049F1A3 /* Build configuration list for PBXProject "Example" */ = { 403 | isa = XCConfigurationList; 404 | buildConfigurations = ( 405 | 17EC7932194C04750049F1A3 /* Debug */, 406 | 17EC7933194C04750049F1A3 /* Release */, 407 | ); 408 | defaultConfigurationIsVisible = 0; 409 | defaultConfigurationName = Release; 410 | }; 411 | 17EC7934194C04750049F1A3 /* Build configuration list for PBXNativeTarget "Example" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | 17EC7935194C04750049F1A3 /* Debug */, 415 | 17EC7936194C04750049F1A3 /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | 17EC7937194C04750049F1A3 /* Build configuration list for PBXNativeTarget "ExampleTests" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | 17EC7938194C04750049F1A3 /* Debug */, 424 | 17EC7939194C04750049F1A3 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | /* End XCConfigurationList section */ 430 | }; 431 | rootObject = 17EC7910194C04750049F1A3 /* Project object */; 432 | } 433 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/aemaeth.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/aemaeth.xcuserdatad/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/aemaeth.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 17EC7917194C04750049F1A3 16 | 17 | primary 18 | 19 | 20 | 17EC7929194C04750049F1A3 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Ruoyu Fu on 14-6-14. 6 | // Copyright (c) 2014年 Ruoyu Fu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Example/Example/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" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Aemaeth.${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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Created by Ruoyu Fu on 14-6-14. 6 | // Copyright (c) 2014年 Ruoyu Fu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UITableViewController { 12 | //需要将这3个参数替换为你自己在新浪申请的参数 13 | let client = SWKClient(clientID:"1818152344", clientSecret:"dbeb5f4cfada69f679c1e39e8785df8d", redirectURI:"http://127.0.0.1/") 14 | var statuses:JSONValue = JSONValue.JInvalid 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | } 19 | 20 | @IBAction func login(sender : UIButton) { 21 | //点击登录后,首先打开授权页面,让用户授权 22 | client.presentAuthorizeView(fromViewController: self){ 23 | result in 24 | //用户授权的回调 25 | if result{ 26 | //若授权成功,就可以直接访问API了,这里是:statuses/home_timeline.json 27 | self.client.get("https://api.weibo.com/2/statuses/home_timeline.json"){ 28 | result in 29 | self.statuses = result.json 30 | self.tableView.reloadData() 31 | } 32 | } 33 | } 34 | } 35 | 36 | override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{ 37 | if statuses["statuses"].array{ 38 | return statuses["statuses"].array!.count 39 | } 40 | return 0 41 | } 42 | 43 | override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{ 44 | var cell:UITableViewCell 45 | if let dequeuedCell = tableView.dequeueReusableCellWithIdentifier("StatusCell", forIndexPath: indexPath) as? UITableViewCell{ 46 | cell = dequeuedCell 47 | }else{ 48 | cell = UITableViewCell() 49 | } 50 | 51 | cell.detailTextLabel.text = statuses["statuses"][indexPath.row]["text"].string 52 | cell.textLabel.text = statuses["statuses"][indexPath.row]["user"]["name"].string 53 | cell.setNeedsLayout() 54 | return cell 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Example/ExampleTests/ExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleTests.swift 3 | // ExampleTests 4 | // 5 | // Created by Ruoyu Fu on 14-6-14. 6 | // Copyright (c) 2014年 Ruoyu Fu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ExampleTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Example/ExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Aemaeth.${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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ruoyu Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftWeiboKit 2 | 3 | SwiftWeiboKit is An delightful Sina Weibo library written in Swift 4 | 5 | ## Update: 6 | Add [SwiftyJSON](https://github.com/lingoer/SwiftyJSON) for JSON handling 7 | 8 | ## Getting started 9 | 10 | Drag SwiftWeiboKit.swift to your project 11 | 12 | ```swift 13 | let client = SWKClient(clientID:"YOUR_ID", clientSecret:"YOUR_SECRET", redirectURI:"YOUR_REDIRECT_URI") 14 | client.presentAuthorizeView(fromViewController: self){ 15 | authResult in 16 | if authResult{ 17 | client.get("https://api.weibo.com/2/statuses/user_timeline.json"){ 18 | response in 19 | switch response{ 20 | case .success(let successResp): 21 | println(successResp.json) 22 | case .failure(let failureResp): 23 | println(failureResp) 24 | } 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /SwiftWeiboKit/SwiftWeiboKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SinaWeiboAuthView.swift 3 | // testSwift 4 | // 5 | // Created by Ruoyu Fu on 14-6-13. 6 | // Copyright (c) 2014 Ruoyu Fu 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | import Foundation 28 | import UIKit 29 | import Social 30 | import WebKit 31 | 32 | class SWKClient{ 33 | /* 34 | SWKClient是SwiftWeiboKit的主要工具类 35 | 它封装了整个OAuth2.0的授权流程,并提供了几个简便易用的请求方法: 36 | 使用方法: 37 | 38 | 1:构造Client,构造时你需要传入新浪开放平台的AppKey,AppSecret以及重定向地址 这3个参数。 39 | 关于这3个参数的解释请参阅新浪开放平台的文档 40 | 41 | 2:获取用户授权,你通过调用 - presentAuthorizeView 方法,弹出一个WebView,供用户输入用户名及密码, 42 | 并提供一个Closure作为回调 43 | 44 | 3:用户授权成功后,SWKClient会自动调用新浪API完成OAuth2认证的剩余流程,并调用第二步传入的Closure通知调用者 45 | 46 | 4:OAuth2完成后,就可以通过SWKClient的 - get - post - put - head 等方法直接调用新浪API了 47 | 48 | 使用示例: 49 | let client = SWKClient(clientID:YOUR_ID, clientSecret:YOUR_SECRET, redirectURI:YOUR_REDIRECT_URI) 50 | client.presentAuthorizeView(fromViewController: self){ 51 | (isSuccess : Bool) in 52 | if isSuccess{ 53 | client.get("https://api.weibo.com/2/statuses/user_timeline.json",parameters:nil){ 54 | (response) in 55 | switch response{ 56 | case .success(let successResp): 57 | println(successResp.json) 58 | case .failure(let failureResp): 59 | println(failureResp) 60 | } 61 | } 62 | } 63 | } 64 | */ 65 | 66 | 67 | /* 68 | 一些私有变量:Swift目前暂时还没有Access Controll的机制 69 | 但根据这里的说法,今后会很快添加进来 70 | https://devforums.apple.com/thread/227288 71 | 等Access Controll加入Swift以后再来Handle这堆东西 72 | */ 73 | let clientID:String 74 | let clientSecret:String 75 | let redirectURI:String 76 | var account:SWKAccount? 77 | 78 | init(clientID:String,clientSecret:String,redirectURI:String){ 79 | /* 80 | 构造方法:传入的三个参数参见新浪开放平台的文档 81 | */ 82 | self.clientID = clientID 83 | self.clientSecret = clientSecret 84 | self.redirectURI = redirectURI 85 | } 86 | 87 | func isAuthValid()->Bool{ 88 | /* 89 | 返回当前的认证是否有效 90 | */ 91 | if self.account?.accessToken&&self.account?.expireDate&&self.account?.uid{ 92 | return true 93 | } 94 | return false 95 | } 96 | 97 | func presentAuthorizeView(fromViewController viewController:UIViewController, authorizeHandler:(Bool)->()){ 98 | /* 99 | 调出用户授权页面给用户输入用户名及密码 100 | fromViewController: 表示从哪个ViewController将此页面Modal出来,必传。 101 | authorizeHandler: 表示OAuth完成后的结果,会回调一个布尔值表征成功与否 102 | */ 103 | let authController = SWKAuthController(clientID:self.clientID,clientSecret:self.clientSecret,redirectURI:self.redirectURI){ 104 | (result : SWKAuthorizationResult) in 105 | switch result{ 106 | case .Granted(let account): 107 | self.account = account 108 | authorizeHandler(true) 109 | dispatch_async(dispatch_get_main_queue()){ 110 | viewController.dismissViewControllerAnimated(true, completion: nil) 111 | } 112 | default: 113 | authorizeHandler(false) 114 | } 115 | } 116 | authController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "关闭", style: UIBarButtonItemStyle.Plain, target:viewController, action: "dismissModalViewControllerAnimated:") 117 | 118 | let navigation = UINavigationController(rootViewController:authController) 119 | 120 | dispatch_async(dispatch_get_main_queue()){ 121 | viewController.presentViewController(navigation, animated: true){} 122 | } 123 | } 124 | 125 | func get(url:String, parameters:Dictionary! = nil, completion:((SWKHTTPResponse)->())! = nil){ 126 | /* 127 | HTTP Get方法 128 | */ 129 | self.sendRequest(url, parameters: parameters, httpMethod: SLRequestMethod.GET, completion: completion) 130 | } 131 | 132 | func get(url:String, completion:((SWKHTTPResponse)->())! = nil){ 133 | self.sendRequest(url, parameters: nil, httpMethod: SLRequestMethod.GET, completion: completion) 134 | } 135 | 136 | func post(url:String, parameters:Dictionary! = nil, completion:((SWKHTTPResponse)->())! = nil){ 137 | /* 138 | HTTP POST方法 139 | */ 140 | self.sendRequest(url, parameters: parameters, httpMethod: SLRequestMethod.POST, completion: completion) 141 | } 142 | 143 | func sendRequest(url:String, parameters:Dictionary! = nil,httpMethod:SLRequestMethod , completion:((SWKHTTPResponse)->())! = nil){ 144 | /* 145 | 通用的发起新浪API HTTP请求的方法,需指明使用的是哪种方法 146 | */ 147 | var payload : Dictionary = [:] 148 | if let account=self.account{ 149 | payload["access_token"]=account.accessToken 150 | } 151 | if parameters{ 152 | for (key,value) in parameters{ 153 | payload[key]=value 154 | } 155 | } 156 | 157 | let sinaRequest = SLRequest(forServiceType : SLServiceTypeSinaWeibo, requestMethod : httpMethod, URL : NSURL(string:url), parameters: payload) 158 | sinaRequest.performRequestWithHandler{ 159 | (data : NSData!, httpResponse : NSHTTPURLResponse!, error : NSError!) in 160 | let response = SWKHTTPResponse(data: data,response: httpResponse,error: error) 161 | dispatch_async(dispatch_get_main_queue()){ 162 | completion(response) 163 | } 164 | } 165 | } 166 | 167 | 168 | struct SWKAccount{ 169 | /* 170 | 封装后的OAuth2信息 171 | */ 172 | var accessToken:String 173 | var expireDate:NSDate 174 | var uid:String 175 | } 176 | 177 | enum SWKAuthorizationResult{ 178 | /* 179 | Granted,表示用户通过输入用户名密码授权通过,其中的SWKAccount是授权后获取到的的OAuth2信息 180 | Rejected,表示用户手动取消或驳回授权 181 | Failed,表示非用户意愿的授权失败,例如,网络错误 182 | */ 183 | case Granted(SWKAccount) 184 | case Rejected 185 | case Failed 186 | } 187 | 188 | enum SWKHTTPResponse:LogicValue{ 189 | /* 190 | 封装后的 新浪API HTTP 响应,分为成功与失败两种不同的enum值: 191 | 两种值分别在其中封装了不同的数据类型及方法。 192 | 同时,由于实现了LogicValue协议,亦可以直接通过if等逻辑控制语句对相应结果进行判断 193 | 194 | 使用示例1,通过switch语句(推荐): 195 | { 196 | httpResponse:SWKHTTPResponse in 197 | 198 | switch httpResponse{ 199 | case .success(let successResp): 200 | println (success.json) //只有成功的值才拥有.json Property 201 | case .failure(let failedResp): 202 | println (failedResp) //成功与失败的值均遵守Printable协议,可以直接使用println输出文本 203 | } 204 | } 205 | 使用实例2,通过if语句: 206 | if httpResponse{ 207 | println(httpResponse.rawData!) 208 | } 209 | */ 210 | 211 | case Success(SuccessResp) 212 | case Failure(FailedResp) 213 | 214 | struct SuccessResp:Printable{ 215 | /* 216 | SWKHTTPResponse值为.success时封装的结构体: 217 | 其中: 218 | content: 返回的Raw Data,以NSData封装 219 | statusCode: 返回的HTTP Status Code,在200至299间 220 | headers: HTTP Header 221 | MIMEType: Content-Type,例如application/json 222 | encoding: 所采用的编码,例如UTF-8 223 | json: Computing Property,序列化后的JSON对象 224 | string: 纯文本 225 | */ 226 | let content : NSData 227 | let statusCode : Int 228 | let headers : NSDictionary 229 | let MIMEType : String 230 | let encoding : String 231 | var json : JSONValue{ 232 | return JSONValue(content) 233 | } 234 | var string : String{ 235 | return NSString(data:content,encoding:NSUTF8StringEncoding) 236 | } 237 | 238 | //Protocol Printable 239 | var description: String { 240 | return self.string 241 | } 242 | } 243 | 244 | struct FailedResp:Printable{ 245 | /* 246 | SWKHTTPResponse值为.failure时封装的结构体: 247 | 其中: 248 | error: NSError对象,一般是由底层的 NSURLSession 产生,一般表征网络错误 249 | message: 新浪服务器对此错误的描述,本来是一段JSON,懒得转了,直接放了文本,凑合着看吧…… 250 | */ 251 | let error : NSError! 252 | let message : String! 253 | var description: String { 254 | if message{ 255 | return message! 256 | } 257 | if error{ 258 | return error!.description 259 | } 260 | return "No Detail Description For This Error" 261 | } 262 | } 263 | 264 | init(data:NSData?, response:NSURLResponse?, error:NSError?) { 265 | /* 266 | init方法,必须使用此方法对本enum进行构造 267 | 其中: 268 | data:NSData!, 269 | response:NSURLResponse!, 270 | error:NSError! 271 | 这几个参数就是NSURLSession、NSURLConnection或SLRequest在网络请求时的标准回调参数 272 | */ 273 | 274 | if let httpResponse = response as? NSHTTPURLResponse { 275 | switch httpResponse.statusCode { 276 | case 200..299: 277 | var rawData:NSData 278 | if !data{ 279 | rawData = NSData() 280 | }else{ 281 | rawData = data! 282 | } 283 | let resp = SuccessResp( 284 | content : rawData, 285 | statusCode : httpResponse.statusCode, 286 | headers : httpResponse.allHeaderFields, 287 | MIMEType : httpResponse.MIMEType, 288 | encoding : httpResponse.textEncodingName) 289 | self = .Success(resp) 290 | default: 291 | var message:String! = nil 292 | if data{ 293 | message = NSString(data:data,encoding:NSUTF8StringEncoding) 294 | } 295 | let resp = FailedResp(error:error,message:message) 296 | self = .Failure(resp) 297 | } 298 | 299 | }else{ 300 | var message:String! = nil 301 | if data{ 302 | message = NSString(data:data,encoding:NSUTF8StringEncoding) 303 | } 304 | let resp = FailedResp(error:error,message:message) 305 | self = .Failure(resp) 306 | } 307 | } 308 | 309 | //Computing Property:成功时返回数据,失败时返回nil,参见本enum的使用示例2 310 | var rawData:NSData?{ 311 | switch self{ 312 | case .Success(let resp): 313 | return resp.content 314 | default: 315 | return nil 316 | } 317 | } 318 | 319 | var json:JSONValue{ 320 | switch self{ 321 | case .Success(let resp): 322 | return resp.json 323 | case .Failure(let Failure): 324 | return JSONValue.JInvalid 325 | } 326 | } 327 | 328 | //Protocol LogicValue 329 | func getLogicValue() -> Bool{ 330 | switch self{ 331 | case .Success: 332 | return true 333 | default: 334 | return false 335 | } 336 | } 337 | } 338 | 339 | 340 | class SWKAuthController: UIViewController,WKNavigationDelegate { 341 | /* 342 | 不稳定的授权Web页面,通过 - presentAuthorizeView 方法调出的Web页面就是由此ViewController生成。 343 | Web授权完毕后会自动dismiss,并Invoke回调 344 | 由于必须非常操蛋地访问Web,此部分代码与UIKit耦合了,有空可能会想点其他方式来解决这个问题 345 | */ 346 | let clientID:String 347 | let clientSecret:String 348 | let redirectURI:String 349 | let authorizeCallBack:(SWKAuthorizationResult)->() 350 | 351 | init(clientID:String,clientSecret:String,redirectURI:String,callBack:(SWKAuthorizationResult)->()){ 352 | self.clientID = clientID 353 | self.clientSecret = clientSecret 354 | self.redirectURI = redirectURI 355 | self.authorizeCallBack = callBack 356 | 357 | super.init(nibName:nil,bundle:nil) 358 | self.edgesForExtendedLayout=UIRectEdge.None 359 | self.modalTransitionStyle = UIModalTransitionStyle.CoverVertical 360 | } 361 | 362 | override func loadView(){ 363 | self.view = WKWebView() 364 | (self.view as WKWebView).navigationDelegate = self 365 | } 366 | 367 | override func viewDidLoad() { 368 | super.viewDidLoad() 369 | 370 | if let webView = self.view as? WKWebView{ 371 | let request = NSURLRequest(URL: NSURL(string:"https://api.weibo.com/oauth2/authorize?client_id=\(clientID)&redirect_uri=\(redirectURI)&display=mobile")) 372 | webView.loadRequest(request) 373 | } 374 | } 375 | 376 | func webView(webView: WKWebView!, decidePolicyForNavigationAction navigationAction: WKNavigationAction!, decisionHandler: ((WKNavigationActionPolicy) -> Void)!){ 377 | if let url = navigationAction?.request?.URL{ 378 | if url.absoluteString.hasPrefix(self.redirectURI) { 379 | if let array = url.query?.componentsSeparatedByString("&"){ 380 | for kvPairString in array { 381 | let kvArray = kvPairString.componentsSeparatedByString("=") 382 | if kvArray.count == 2{ 383 | if kvArray[0] == "code"{ 384 | let code = kvArray[1] 385 | self.requestAccessToken(code) 386 | } 387 | } 388 | } 389 | } 390 | decisionHandler(WKNavigationActionPolicy.Cancel) 391 | } 392 | } 393 | decisionHandler(WKNavigationActionPolicy.Allow) 394 | return 395 | } 396 | 397 | func requestAccessToken(code:String){ 398 | let url = NSURL(string:"https://api.weibo.com/oauth2/access_token") 399 | let client = SWKClient(clientID:clientID,clientSecret:clientSecret,redirectURI:redirectURI) 400 | let payload = [ 401 | "client_id":clientID, 402 | "client_secret":clientSecret, 403 | "grant_type":"authorization_code", 404 | "code":code, 405 | "redirect_uri":redirectURI] 406 | 407 | let sinaRequest = SLRequest(forServiceType : SLServiceTypeSinaWeibo, requestMethod : SLRequestMethod.POST, URL : url, parameters: payload) 408 | 409 | sinaRequest.performRequestWithHandler{ 410 | (data : NSData!, urlResponse : NSURLResponse!, error : NSError!) in 411 | if error{ 412 | self.authorizeCallBack(SWKAuthorizationResult.Failed) 413 | return 414 | } 415 | let json = JSONValue(data) 416 | let expire = json["expires_in"].number 417 | let uid = json["uid"].string 418 | let token = json["access_token"].string 419 | if expire&&uid&&token{ 420 | self.authorizeCallBack(SWKAuthorizationResult.Granted(SWKAccount(accessToken:token! 421 | ,expireDate:NSDate(timeIntervalSince1970:expire!), 422 | uid:uid!))) 423 | return 424 | }else{ 425 | self.authorizeCallBack(SWKAuthorizationResult.Failed) 426 | return 427 | } 428 | 429 | 430 | } 431 | } 432 | } 433 | } 434 | 435 | -------------------------------------------------------------------------------- /SwiftWeiboKit/SwiftyJSON.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftJSON.swift 3 | // SwiftJSON 4 | // 5 | // Created by Ruoyu Fu on 14-6-16. 6 | // Copyright (c) 2014年 Ruoyu Fu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func ==(lhs: JSONValue, rhs: JSONValue) -> Bool { 12 | switch lhs{ 13 | case .JNumber(let lvalue): 14 | switch rhs{ 15 | case .JNumber(let rvalue): 16 | return rvalue == lvalue 17 | default: 18 | return false 19 | } 20 | case .JString(let lvalue): 21 | switch rhs{ 22 | case .JString(let rvalue): 23 | return rvalue == lvalue 24 | default: 25 | return false 26 | } 27 | case .JBool(let lvalue): 28 | switch rhs{ 29 | case .JBool(let rvalue): 30 | return rvalue == lvalue 31 | default: 32 | return false 33 | } 34 | case .JNull: 35 | switch rhs{ 36 | case .JNull: 37 | return true 38 | default: 39 | return false 40 | } 41 | case .JArray(let lvalue): 42 | switch rhs{ 43 | case .JArray(let rvalue): 44 | return rvalue == lvalue 45 | default: 46 | return false 47 | } 48 | case .JObject(let lvalue): 49 | switch rhs{ 50 | case .JObject(let rvalue): 51 | return rvalue == lvalue 52 | default: 53 | return false 54 | } 55 | 56 | default: 57 | return false 58 | } 59 | } 60 | 61 | enum JSONValue:LogicValue, Equatable, Printable { 62 | 63 | case JNumber(Double) 64 | case JString(String) 65 | case JBool(Bool) 66 | case JNull 67 | case JArray(Array) 68 | case JObject(Dictionary) 69 | case JInvalid 70 | 71 | var string:String?{ 72 | switch self{ 73 | case .JString(let value): 74 | return value 75 | default: 76 | return nil 77 | } 78 | } 79 | var number:Double?{ 80 | switch self{ 81 | case .JNumber(let value): 82 | return value 83 | default: 84 | return nil 85 | } 86 | } 87 | var bool:Bool?{ 88 | switch self{ 89 | case .JBool(let value): 90 | return value 91 | default: 92 | return nil 93 | } 94 | } 95 | var array:Array?{ 96 | switch self{ 97 | case .JArray(let value): 98 | return value 99 | default: 100 | return nil 101 | } 102 | } 103 | var object:Dictionary?{ 104 | switch self{ 105 | case .JObject(let value): 106 | return value 107 | default: 108 | return nil 109 | } 110 | } 111 | 112 | // init(_ rawValue:Any){ 113 | // 114 | // switch rawValue{ 115 | // case let value as Int: 116 | // self = .JNumber(Double(value)) 117 | // case let value as Double: 118 | // self = .JNumber(value) 119 | // case let value as Bool: 120 | // self = .JBool(value) 121 | // case let value as String: 122 | // self = .JString(value) 123 | // case let value as Array: 124 | // self = .JArray(value) 125 | // case let value as Dictionary: 126 | // self = .JObject(value) 127 | // case let value as JSONValue: 128 | // self = value 129 | // default: 130 | // self = .JInvalid 131 | // } 132 | // } 133 | 134 | init (_ rawObject:AnyObject){ 135 | switch rawObject{ 136 | case let value as NSData: 137 | if let jsonObject : AnyObject = NSJSONSerialization.JSONObjectWithData(value, options: NSJSONReadingOptions.MutableContainers, error: nil){ 138 | self = JSONValue(jsonObject) 139 | }else{ 140 | self = JSONValue.JInvalid 141 | } 142 | case let value as NSNumber: 143 | if String.fromCString(value.objCType) == "c"{ 144 | self = .JBool(value.boolValue) 145 | return 146 | } 147 | self = .JNumber(value.doubleValue) 148 | case let value as NSString: 149 | self = .JString(value) 150 | case let value as NSNull: 151 | self = .JNull 152 | case let value as NSArray: 153 | var jsonValues = JSONValue[]() 154 | for possibleJsonValue : AnyObject in value{ 155 | let jsonValue = JSONValue(possibleJsonValue) 156 | if jsonValue{ 157 | jsonValues.append(jsonValue) 158 | } 159 | } 160 | self = .JArray(jsonValues) 161 | case let value as NSDictionary: 162 | var jsonObject = Dictionary() 163 | for (possibleJsonKey : AnyObject,possibleJsonValue : AnyObject) in value{ 164 | if let key = possibleJsonKey as? NSString{ 165 | let jsonValue = JSONValue(possibleJsonValue) 166 | if jsonValue{ 167 | jsonObject[key]=jsonValue 168 | } 169 | } 170 | } 171 | self = .JObject(jsonObject) 172 | default: 173 | self = .JInvalid 174 | } 175 | } 176 | 177 | subscript(index: Int) -> JSONValue { 178 | get { 179 | switch self{ 180 | case .JArray(let jsonArray) where jsonArray.count > index: 181 | return jsonArray[index] 182 | default: 183 | return JSONValue.JInvalid 184 | } 185 | } 186 | } 187 | 188 | subscript(key: String) -> JSONValue { 189 | get { 190 | switch self{ 191 | case .JObject(let jsonDictionary): 192 | if let value = jsonDictionary[key]{ 193 | return value 194 | }else{ 195 | return JSONValue.JInvalid 196 | } 197 | default: 198 | return JSONValue.JInvalid 199 | } 200 | } 201 | } 202 | 203 | func getLogicValue() -> Bool{ 204 | switch self{ 205 | case .JInvalid: 206 | return false 207 | default: 208 | return true 209 | } 210 | } 211 | 212 | var rawJSONString: String{ 213 | switch self{ 214 | case .JNumber(let value): 215 | return "\(value)" 216 | case .JBool(let value): 217 | return "\(value)" 218 | case .JString(let value): 219 | 220 | let jsonAbleString = value.stringByReplacingOccurrencesOfString("\"", withString: "\\\"", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil) 221 | return "\"\(jsonAbleString)\"" 222 | case .JNull: 223 | return "null" 224 | case .JArray(let array): 225 | var arrayString = "[" 226 | for (index, value) in enumerate(array) { 227 | if index != array.count - 1{ 228 | arrayString += "\(value.description)," 229 | }else{ 230 | arrayString += "\(value.description)" 231 | } 232 | } 233 | arrayString += "]" 234 | return arrayString 235 | case .JObject(let object): 236 | var objectString = "{" 237 | var (index, count) = (0, object.count) 238 | for (key, value) in object{ 239 | if index != count - 1{ 240 | objectString += "\"\(key)\":\(value.description)," 241 | }else{ 242 | objectString += "\"\(key)\":\(value.description)" 243 | } 244 | index += 1 245 | } 246 | objectString += "}" 247 | return objectString 248 | case .JInvalid: 249 | return "INVALID_JSON_VALUE" 250 | } 251 | } 252 | 253 | func printableString(indent:String)->String{ 254 | switch self{ 255 | case .JObject(let object): 256 | var objectString = "{\n" 257 | var (index, count) = (0, object.count) 258 | for (key, value) in object{ 259 | let valueString = value.printableString(indent + " ") 260 | if index != count - 1{ 261 | objectString += "\(indent) \"\(key)\":\(valueString),\n" 262 | }else{ 263 | objectString += "\(indent) \"\(key)\":\(valueString)\n" 264 | } 265 | index += 1 266 | } 267 | objectString += "\(indent)}" 268 | return objectString 269 | case .JArray(let array): 270 | var arrayString = "[\n" 271 | for (index, value) in enumerate(array) { 272 | let valueString = value.printableString(indent + " ") 273 | if index != array.count - 1{ 274 | arrayString += "\(indent) \(valueString),\n" 275 | }else{ 276 | arrayString += "\(indent) \(valueString)\n" 277 | } 278 | } 279 | arrayString += "\(indent)]" 280 | return arrayString 281 | default: 282 | return self.rawJSONString 283 | } 284 | } 285 | 286 | var description: String { 287 | return self.printableString("") 288 | } 289 | } 290 | --------------------------------------------------------------------------------