├── Tests ├── Resources │ ├── Images │ │ ├── rainbow.jpg │ │ └── unicorn.png │ └── Certificates │ │ ├── alamofire.org │ │ ├── expired.cer │ │ ├── valid-uri.cer │ │ ├── signed-by-ca1.cer │ │ ├── signed-by-ca2.cer │ │ ├── valid-dns-name.cer │ │ ├── alamofire-root-ca.cer │ │ ├── multiple-dns-names.cer │ │ ├── test.alamofire.org.cer │ │ ├── alamofire-signing-ca1.cer │ │ ├── alamofire-signing-ca2.cer │ │ ├── wildcard.alamofire.org.cer │ │ └── missing-dns-name-and-uri.cer │ │ ├── disig.sk │ │ ├── root-ca-disig.cer │ │ ├── intermediate-ca-disig.cer │ │ └── testssl-expire.disig.sk.cer │ │ └── selfSignedAndMalformedCerts │ │ ├── keyDER.der │ │ ├── certDER.cer │ │ ├── certDER.crt │ │ ├── certDER.der │ │ ├── randomGibberish.crt │ │ ├── certPEM.cer │ │ └── certPEM.crt ├── Info.plist ├── String+AlamofireTests.swift ├── BaseTestCase.swift ├── NSURLSessionConfiguration+AlamofireTests.swift ├── ResultTests.swift ├── URLProtocolTests.swift ├── NetworkReachabilityManagerTests.swift ├── AuthenticationTests.swift ├── ResponseTests.swift └── ManagerTests.swift ├── Example ├── Resources │ ├── Images.xcassets │ │ ├── Logo.imageset │ │ │ ├── Logo.png │ │ │ ├── Logo@2x.png │ │ │ └── Contents.json │ │ ├── LaunchImage.launchimage │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist ├── iOS Example.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── iOS Example.xcscheme └── Source │ ├── AppDelegate.swift │ ├── MasterViewController.swift │ └── DetailViewController.swift ├── Alamofire.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Alamofire watchOS.xcscheme │ ├── Alamofire OSX.xcscheme │ ├── Alamofire iOS.xcscheme │ └── Alamofire tvOS.xcscheme ├── Alamofire.xcworkspace └── contents.xcworkspacedata ├── .gitignore ├── Alamofire.podspec ├── Source ├── Info.plist ├── Info-tvOS.plist ├── Alamofire.h ├── Notifications.swift ├── Result.swift ├── Response.swift ├── Error.swift ├── Timeline.swift ├── Stream.swift ├── Validation.swift ├── NetworkReachabilityManager.swift ├── Download.swift └── ParameterEncoding.swift ├── LICENSE ├── Package.swift ├── .travis.yml ├── CONTRIBUTING.md └── Documentation ├── Alamofire 2.0 Migration Guide.md └── Alamofire 3.0 Migration Guide.md /Tests/Resources/Images/rainbow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Images/rainbow.jpg -------------------------------------------------------------------------------- /Tests/Resources/Images/unicorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Images/unicorn.png -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/expired.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/expired.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/disig.sk/root-ca-disig.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/disig.sk/root-ca-disig.cer -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/Logo.imageset/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Example/Resources/Images.xcassets/Logo.imageset/Logo.png -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/valid-uri.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/valid-uri.cer -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/Logo.imageset/Logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Example/Resources/Images.xcassets/Logo.imageset/Logo@2x.png -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/signed-by-ca1.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/signed-by-ca1.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/signed-by-ca2.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/signed-by-ca2.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/valid-dns-name.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/valid-dns-name.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/alamofire-root-ca.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/alamofire-root-ca.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/multiple-dns-names.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/multiple-dns-names.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/test.alamofire.org.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/test.alamofire.org.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/disig.sk/intermediate-ca-disig.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/disig.sk/intermediate-ca-disig.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/disig.sk/testssl-expire.disig.sk.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/disig.sk/testssl-expire.disig.sk.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/keyDER.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/selfSignedAndMalformedCerts/keyDER.der -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/alamofire-signing-ca1.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/alamofire-signing-ca1.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/alamofire-signing-ca2.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/alamofire-signing-ca2.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/wildcard.alamofire.org.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/wildcard.alamofire.org.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.crt -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/selfSignedAndMalformedCerts/certDER.der -------------------------------------------------------------------------------- /Tests/Resources/Certificates/alamofire.org/missing-dns-name-and-uri.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/alamofire.org/missing-dns-name-and-uri.cer -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/randomGibberish.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielPeart/Alamofire/HEAD/Tests/Resources/Certificates/selfSignedAndMalformedCerts/randomGibberish.crt -------------------------------------------------------------------------------- /Alamofire.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Alamofire.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | -------------------------------------------------------------------------------- /Alamofire.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Alamofire' 3 | s.version = '3.4.0' 4 | s.license = 'MIT' 5 | s.summary = 'Elegant HTTP Networking in Swift' 6 | s.homepage = 'https://github.com/Alamofire/Alamofire' 7 | s.social_media_url = 'http://twitter.com/AlamofireSF' 8 | s.authors = { 'Alamofire Software Foundation' => 'info@alamofire.org' } 9 | s.source = { :git => 'https://github.com/Alamofire/Alamofire.git', :tag => s.version } 10 | 11 | s.ios.deployment_target = '8.0' 12 | s.osx.deployment_target = '10.9' 13 | s.tvos.deployment_target = '9.0' 14 | s.watchos.deployment_target = '2.0' 15 | 16 | s.source_files = 'Source/*.swift' 17 | end 18 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.4.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.4.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | UIRequiredDeviceCapabilities 26 | 27 | arm64 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // Package.swift 2 | // 3 | // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) 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 | 23 | import PackageDescription 24 | 25 | let package = Package( 26 | name: "Alamofire" 27 | ) 28 | -------------------------------------------------------------------------------- /Example/Resources/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 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /Source/Alamofire.h: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.h 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | @import Foundation; 26 | 27 | FOUNDATION_EXPORT double AlamofireVersionNumber; 28 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 29 | -------------------------------------------------------------------------------- /Example/Resources/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 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/certPEM.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID9DCCAtygAwIBAgIJAIqBVOBRW4qMMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UEBxMIQmVya2VsZXkxKjAoBgNVBAoT 4 | IVRlc3RpbmcgQ2VydGlmaWNhdGVzIGluIEFsYW1vRmlyZTAeFw0xNTEyMTEwMjA5 5 | MDlaFw0xNjEyMTAwMjA5MDlaMFkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTER 6 | MA8GA1UEBxMIQmVya2VsZXkxKjAoBgNVBAoTIVRlc3RpbmcgQ2VydGlmaWNhdGVz 7 | IGluIEFsYW1vRmlyZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJy9 8 | FTR4QzhYyo2v2yv8SwlBQ32MHQF8Ez1J+YBsyZjcTVOGJtyPxrbJxGuRhyDzKUqz 9 | X/zTsT+JPvZQXXBmyq0l0DhCcK84cHyVLcdEAzukam85EpJRWzSg8kDKzuTx2oLk 10 | X8Zdcj7EEtYWV/5+/YahM4tXYhg+Lqm6koJEVHMld6zfedC7HN+jsTb73IrAY0dd 11 | BPl7WPgDIPEl0kcGI6F7NS0+CKsgX412E5+TGUkvA8VI+e9+cn/EXBdklVQQGSOu 12 | rHpcoOi1VqnQI0hGXlFi4MpamwMG2yArIUU0TXZ7G+/AbUYiGdB6ogvg5UTCfyZy 13 | UXVljSJyzYmLs7hXQK8CAwEAAaOBvjCBuzAdBgNVHQ4EFgQU9EaWHrJGYvpCEW5f 14 | CUEMRk9DlN8wgYsGA1UdIwSBgzCBgIAU9EaWHrJGYvpCEW5fCUEMRk9DlN+hXaRb 15 | MFkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UEBxMIQmVya2VsZXkx 16 | KjAoBgNVBAoTIVRlc3RpbmcgQ2VydGlmaWNhdGVzIGluIEFsYW1vRmlyZYIJAIqB 17 | VOBRW4qMMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEeHeXNZGHJW 18 | VImbOHrYmSsZ5jFnzjGw8ynkOrcoJzaxg3OHoo/pNCQ7KcrIa5YPNFiNoaSa/Lzn 19 | LBt6HkM1Vi1rMaERHLWp/W5ruInCu4CuVtQshdNcOEofJ03wdrQylOBZq8MZkTVn 20 | NcqUFg/sBANM/9WhafVi7XaUjWl+V7ZnzdbKP/ywvsiJ+wyKMA7Wt19HMrV2dTBz 21 | CD4vxpwOBev0oTp2NvAHdgNkeK52skHoz+MY8uivVJQr4hqLYJPXUyAcVZCaqeK/ 22 | hxDkbRo6eZsYcjTRqMKtGMVjHHd8alXcVJwcuWkhUYxy8jRf0kFj/9mMie9jRokJ 23 | ovKLbNJfEbI= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /Tests/Resources/Certificates/selfSignedAndMalformedCerts/certPEM.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID9DCCAtygAwIBAgIJAIqBVOBRW4qMMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UEBxMIQmVya2VsZXkxKjAoBgNVBAoT 4 | IVRlc3RpbmcgQ2VydGlmaWNhdGVzIGluIEFsYW1vRmlyZTAeFw0xNTEyMTEwMjA5 5 | MDlaFw0xNjEyMTAwMjA5MDlaMFkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTER 6 | MA8GA1UEBxMIQmVya2VsZXkxKjAoBgNVBAoTIVRlc3RpbmcgQ2VydGlmaWNhdGVz 7 | IGluIEFsYW1vRmlyZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJy9 8 | FTR4QzhYyo2v2yv8SwlBQ32MHQF8Ez1J+YBsyZjcTVOGJtyPxrbJxGuRhyDzKUqz 9 | X/zTsT+JPvZQXXBmyq0l0DhCcK84cHyVLcdEAzukam85EpJRWzSg8kDKzuTx2oLk 10 | X8Zdcj7EEtYWV/5+/YahM4tXYhg+Lqm6koJEVHMld6zfedC7HN+jsTb73IrAY0dd 11 | BPl7WPgDIPEl0kcGI6F7NS0+CKsgX412E5+TGUkvA8VI+e9+cn/EXBdklVQQGSOu 12 | rHpcoOi1VqnQI0hGXlFi4MpamwMG2yArIUU0TXZ7G+/AbUYiGdB6ogvg5UTCfyZy 13 | UXVljSJyzYmLs7hXQK8CAwEAAaOBvjCBuzAdBgNVHQ4EFgQU9EaWHrJGYvpCEW5f 14 | CUEMRk9DlN8wgYsGA1UdIwSBgzCBgIAU9EaWHrJGYvpCEW5fCUEMRk9DlN+hXaRb 15 | MFkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UEBxMIQmVya2VsZXkx 16 | KjAoBgNVBAoTIVRlc3RpbmcgQ2VydGlmaWNhdGVzIGluIEFsYW1vRmlyZYIJAIqB 17 | VOBRW4qMMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEeHeXNZGHJW 18 | VImbOHrYmSsZ5jFnzjGw8ynkOrcoJzaxg3OHoo/pNCQ7KcrIa5YPNFiNoaSa/Lzn 19 | LBt6HkM1Vi1rMaERHLWp/W5ruInCu4CuVtQshdNcOEofJ03wdrQylOBZq8MZkTVn 20 | NcqUFg/sBANM/9WhafVi7XaUjWl+V7ZnzdbKP/ywvsiJ+wyKMA7Wt19HMrV2dTBz 21 | CD4vxpwOBev0oTp2NvAHdgNkeK52skHoz+MY8uivVJQr4hqLYJPXUyAcVZCaqeK/ 22 | hxDkbRo6eZsYcjTRqMKtGMVjHHd8alXcVJwcuWkhUYxy8jRf0kFj/9mMie9jRokJ 23 | ovKLbNJfEbI= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /Tests/String+AlamofireTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSessionConfiguration+AlamofireTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension String { 28 | init(count: Int, repeatedString: String) { 29 | var value = "" 30 | for _ in 0.. NSURL { 33 | let bundle = NSBundle(forClass: BaseTestCase.self) 34 | return bundle.URLForResource(fileName, withExtension: withExtension)! 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Example/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Alamofire 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Tests/NSURLSessionConfiguration+AlamofireTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSessionConfiguration+AlamofireTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension NSURLSessionConfiguration { 28 | static func backgroundSessionConfigurationForAllPlatformsWithIdentifier(identifier: String) -> NSURLSessionConfiguration { 29 | let configuration: NSURLSessionConfiguration 30 | 31 | if #available(OSX 10.10, *) { 32 | configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(identifier) 33 | } else { 34 | configuration = NSURLSessionConfiguration.backgroundSessionConfiguration(identifier) 35 | } 36 | 37 | return configuration 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Contains all the `NSNotification` names posted by Alamofire with descriptions of each notification's payload. 28 | public struct Notifications { 29 | /// Used as a namespace for all `NSURLSessionTask` related notifications. 30 | public struct Task { 31 | /// Notification posted when an `NSURLSessionTask` is resumed. The notification `object` contains the resumed 32 | /// `NSURLSessionTask`. 33 | public static let DidResume = "com.alamofire.notifications.task.didResume" 34 | 35 | /// Notification posted when an `NSURLSessionTask` is suspended. The notification `object` contains the 36 | /// suspended `NSURLSessionTask`. 37 | public static let DidSuspend = "com.alamofire.notifications.task.didSuspend" 38 | 39 | /// Notification posted when an `NSURLSessionTask` is cancelled. The notification `object` contains the 40 | /// cancelled `NSURLSessionTask`. 41 | public static let DidCancel = "com.alamofire.notifications.task.didCancel" 42 | 43 | /// Notification posted when an `NSURLSessionTask` is completed. The notification `object` contains the 44 | /// completed `NSURLSessionTask`. 45 | public static let DidComplete = "com.alamofire.notifications.task.didComplete" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/Source/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import UIKit 26 | 27 | @UIApplicationMain 28 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 29 | 30 | var window: UIWindow? 31 | 32 | // MARK: - UIApplicationDelegate 33 | 34 | func application( 35 | application: UIApplication, 36 | didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) 37 | -> Bool 38 | { 39 | let splitViewController = window!.rootViewController as! UISplitViewController 40 | let navigationController = splitViewController.viewControllers.last as! UINavigationController 41 | navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() 42 | splitViewController.delegate = self 43 | 44 | return true 45 | } 46 | 47 | // MARK: - UISplitViewControllerDelegate 48 | 49 | func splitViewController( 50 | splitViewController: UISplitViewController, 51 | collapseSecondaryViewController secondaryViewController: UIViewController, 52 | ontoPrimaryViewController primaryViewController: UIViewController) 53 | -> Bool 54 | { 55 | if let secondaryAsNavController = secondaryViewController as? UINavigationController { 56 | if let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController { 57 | return topAsDetailController.request == nil 58 | } 59 | } 60 | 61 | return false 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | - WORKSPACE=Alamofire.xcworkspace 8 | - IOS_FRAMEWORK_SCHEME="Alamofire iOS" 9 | - OSX_FRAMEWORK_SCHEME="Alamofire OSX" 10 | - TVOS_FRAMEWORK_SCHEME="Alamofire tvOS" 11 | - WATCHOS_FRAMEWORK_SCHEME="Alamofire watchOS" 12 | - IOS_SDK=iphonesimulator9.3 13 | - OSX_SDK=macosx10.11 14 | - TVOS_SDK=appletvsimulator9.2 15 | - WATCHOS_SDK=watchsimulator2.2 16 | - EXAMPLE_SCHEME="iOS Example" 17 | matrix: 18 | - DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="YES" 19 | - DESTINATION="OS=8.2,name=iPhone 5" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 20 | - DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 21 | - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 22 | - DESTINATION="OS=9.0,name=iPhone 6" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 23 | - DESTINATION="OS=9.1,name=iPhone 6 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 24 | - DESTINATION="OS=9.2,name=iPhone 6S" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 25 | - DESTINATION="OS=9.3,name=iPhone 6S Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="NO" 26 | - DESTINATION="arch=x86_64" SCHEME="$OSX_FRAMEWORK_SCHEME" SDK="$OSX_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO" 27 | - DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO" 28 | - DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" RUN_TESTS="NO" BUILD_EXAMPLE="NO" POD_LINT="NO" 29 | script: 30 | - set -o pipefail 31 | - xcodebuild -version 32 | - xcodebuild -showsdks 33 | 34 | # Build Framework in Debug and Run Tests if specified 35 | - if [ $RUN_TESTS == "YES" ]; then 36 | xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c; 37 | else 38 | xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 39 | fi 40 | 41 | # Build Framework in ReleaseTest and Run Tests if specified 42 | - if [ $RUN_TESTS == "YES" ]; then 43 | xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration ReleaseTest ONLY_ACTIVE_ARCH=NO test | xcpretty -c; 44 | else 45 | xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration ReleaseTest ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 46 | fi 47 | 48 | # Build Example in Debug if specified 49 | - if [ $BUILD_EXAMPLE == "YES" ]; then 50 | xcodebuild -workspace "$WORKSPACE" -scheme "$EXAMPLE_SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 51 | fi 52 | 53 | # Run `pod lib lint` if specified 54 | - if [ $POD_LINT == "YES" ]; then 55 | pod lib lint; 56 | fi 57 | -------------------------------------------------------------------------------- /Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /** 28 | Used to represent whether a request was successful or encountered an error. 29 | 30 | - Success: The request and all post processing operations were successful resulting in the serialization of the 31 | provided associated value. 32 | - Failure: The request encountered an error resulting in a failure. The associated values are the original data 33 | provided by the server as well as the error that caused the failure. 34 | */ 35 | public enum Result { 36 | case Success(Value) 37 | case Failure(Error) 38 | 39 | /// Returns `true` if the result is a success, `false` otherwise. 40 | public var isSuccess: Bool { 41 | switch self { 42 | case .Success: 43 | return true 44 | case .Failure: 45 | return false 46 | } 47 | } 48 | 49 | /// Returns `true` if the result is a failure, `false` otherwise. 50 | public var isFailure: Bool { 51 | return !isSuccess 52 | } 53 | 54 | /// Returns the associated value if the result is a success, `nil` otherwise. 55 | public var value: Value? { 56 | switch self { 57 | case .Success(let value): 58 | return value 59 | case .Failure: 60 | return nil 61 | } 62 | } 63 | 64 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 65 | public var error: Error? { 66 | switch self { 67 | case .Success: 68 | return nil 69 | case .Failure(let error): 70 | return error 71 | } 72 | } 73 | } 74 | 75 | // MARK: - CustomStringConvertible 76 | 77 | extension Result: CustomStringConvertible { 78 | /// The textual representation used when written to an output stream, which includes whether the result was a 79 | /// success or failure. 80 | public var description: String { 81 | switch self { 82 | case .Success: 83 | return "SUCCESS" 84 | case .Failure: 85 | return "FAILURE" 86 | } 87 | } 88 | } 89 | 90 | // MARK: - CustomDebugStringConvertible 91 | 92 | extension Result: CustomDebugStringConvertible { 93 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 94 | /// success or failure in addition to the value or error. 95 | public var debugDescription: String { 96 | switch self { 97 | case .Success(let value): 98 | return "SUCCESS: \(value)" 99 | case .Failure(let error): 100 | return "FAILURE: \(error)" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Used to store all response data returned from a completed `Request`. 28 | public struct Response { 29 | /// The URL request sent to the server. 30 | public let request: NSURLRequest? 31 | 32 | /// The server's response to the URL request. 33 | public let response: NSHTTPURLResponse? 34 | 35 | /// The data returned by the server. 36 | public let data: NSData? 37 | 38 | /// The result of response serialization. 39 | public let result: Result 40 | 41 | /// The timeline of the complete lifecycle of the `Request`. 42 | public let timeline: Timeline 43 | 44 | /** 45 | Initializes the `Response` instance with the specified URL request, URL response, server data and response 46 | serialization result. 47 | 48 | - parameter request: The URL request sent to the server. 49 | - parameter response: The server's response to the URL request. 50 | - parameter data: The data returned by the server. 51 | - parameter result: The result of response serialization. 52 | - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. 53 | 54 | - returns: the new `Response` instance. 55 | */ 56 | public init( 57 | request: NSURLRequest?, 58 | response: NSHTTPURLResponse?, 59 | data: NSData?, 60 | result: Result, 61 | timeline: Timeline = Timeline()) 62 | { 63 | self.request = request 64 | self.response = response 65 | self.data = data 66 | self.result = result 67 | self.timeline = timeline 68 | } 69 | } 70 | 71 | // MARK: - CustomStringConvertible 72 | 73 | extension Response: CustomStringConvertible { 74 | /// The textual representation used when written to an output stream, which includes whether the result was a 75 | /// success or failure. 76 | public var description: String { 77 | return result.debugDescription 78 | } 79 | } 80 | 81 | // MARK: - CustomDebugStringConvertible 82 | 83 | extension Response: CustomDebugStringConvertible { 84 | /// The debug textual representation used when written to an output stream, which includes the URL request, the URL 85 | /// response, the server data and the response serialization result. 86 | public var debugDescription: String { 87 | var output: [String] = [] 88 | 89 | output.append(request != nil ? "[Request]: \(request!)" : "[Request]: nil") 90 | output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") 91 | output.append("[Data]: \(data?.length ?? 0) bytes") 92 | output.append("[Result]: \(result.debugDescription)") 93 | output.append("[Timeline]: \(timeline.debugDescription)") 94 | 95 | return output.joinWithSeparator("\n") 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Source/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// The `Error` struct provides a convenience for creating custom Alamofire NSErrors. 28 | public struct Error { 29 | /// The domain used for creating all Alamofire errors. 30 | public static let Domain = "com.alamofire.error" 31 | 32 | /// The custom error codes generated by Alamofire. 33 | public enum Code: Int { 34 | case InputStreamReadFailed = -6000 35 | case OutputStreamWriteFailed = -6001 36 | case ContentTypeValidationFailed = -6002 37 | case StatusCodeValidationFailed = -6003 38 | case DataSerializationFailed = -6004 39 | case StringSerializationFailed = -6005 40 | case JSONSerializationFailed = -6006 41 | case PropertyListSerializationFailed = -6007 42 | } 43 | 44 | /// Custom keys contained within certain NSError `userInfo` dictionaries generated by Alamofire. 45 | public struct UserInfoKeys { 46 | /// The content type user info key for a `.ContentTypeValidationFailed` error stored as a `String` value. 47 | public static let ContentType = "ContentType" 48 | 49 | /// The status code user info key for a `.StatusCodeValidationFailed` error stored as an `Int` value. 50 | public static let StatusCode = "StatusCode" 51 | } 52 | 53 | /** 54 | Creates an `NSError` with the given error code and failure reason. 55 | 56 | - parameter code: The error code. 57 | - parameter failureReason: The failure reason. 58 | 59 | - returns: An `NSError` with the given error code and failure reason. 60 | */ 61 | @available(*, deprecated=3.4.0) 62 | public static func errorWithCode(code: Code, failureReason: String) -> NSError { 63 | return errorWithCode(code.rawValue, failureReason: failureReason) 64 | } 65 | 66 | /** 67 | Creates an `NSError` with the given error code and failure reason. 68 | 69 | - parameter code: The error code. 70 | - parameter failureReason: The failure reason. 71 | 72 | - returns: An `NSError` with the given error code and failure reason. 73 | */ 74 | @available(*, deprecated=3.4.0) 75 | public static func errorWithCode(code: Int, failureReason: String) -> NSError { 76 | let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason] 77 | return NSError(domain: Domain, code: code, userInfo: userInfo) 78 | } 79 | 80 | static func error(domain domain: String = Error.Domain, code: Code, failureReason: String) -> NSError { 81 | return error(domain: domain, code: code.rawValue, failureReason: failureReason) 82 | } 83 | 84 | static func error(domain domain: String = Error.Domain, code: Int, failureReason: String) -> NSError { 85 | let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason] 86 | return NSError(domain: domain, code: code, userInfo: userInfo) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This document contains information and guidelines about contributing to this project. 4 | Please read it before you start participating. 5 | 6 | **Topics** 7 | 8 | * [Asking Questions](#asking-questions) 9 | * [Reporting Security Issues](#reporting-security-issues) 10 | * [Reporting Issues](#reporting-other-issues) 11 | * [Developers Certificate of Origin](#developers-certificate-of-origin) 12 | * [Code of Conduct](#code-of-conduct) 13 | 14 | ## Asking Questions 15 | 16 | We don't use GitHub as a support forum. 17 | For any usage questions that are not specific to the project itself, 18 | please ask on [Stack Overflow](https://stackoverflow.com) instead. 19 | By doing so, you'll be more likely to quickly solve your problem, 20 | and you'll allow anyone else with the same question to find the answer. 21 | This also allows maintainers to focus on improving the project for others. 22 | 23 | ## Reporting Security Issues 24 | 25 | The Alamofire Software Foundation takes security seriously. 26 | If you discover a security issue, please bring it to our attention right away! 27 | 28 | Please **DO NOT** file a public issue, 29 | instead send your report privately to . 30 | This will help ensure that any vulnerabilities that _are_ found 31 | can be [disclosed responsibly](http://en.wikipedia.org/wiki/Responsible_disclosure) 32 | to any affected parties. 33 | 34 | ## Reporting Other Issues 35 | 36 | A great way to contribute to the project 37 | is to send a detailed issue when you encounter an problem. 38 | We always appreciate a well-written, thorough bug report. 39 | 40 | Check that the project issues database 41 | doesn't already include that problem or suggestion before submitting an issue. 42 | If you find a match, add a quick "+1" or "I have this problem too." 43 | Doing this helps prioritize the most common problems and requests. 44 | 45 | When reporting issues, please include the following: 46 | 47 | * The version of Xcode you're using 48 | * The version of iOS or OS X you're targeting 49 | * The full output of any stack trace or compiler error 50 | * A code snippet that reproduces the described behavior, if applicable 51 | * Any other details that would be useful in understanding the problem 52 | 53 | This information will help us review and fix your issue faster. 54 | 55 | ## Developer's Certificate of Origin 1.1 56 | 57 | By making a contribution to this project, I certify that: 58 | 59 | - (a) The contribution was created in whole or in part by me and I 60 | have the right to submit it under the open source license 61 | indicated in the file; or 62 | 63 | - (b) The contribution is based upon previous work that, to the best 64 | of my knowledge, is covered under an appropriate open source 65 | license and I have the right under that license to submit that 66 | work with modifications, whether created in whole or in part 67 | by me, under the same open source license (unless I am 68 | permitted to submit under a different license), as indicated 69 | in the file; or 70 | 71 | - (c) The contribution was provided directly to me by some other 72 | person who certified (a), (b) or (c) and I have not modified 73 | it. 74 | 75 | - (d) I understand and agree that this project and the contribution 76 | are public and that a record of the contribution (including all 77 | personal information I submit with it, including my sign-off) is 78 | maintained indefinitely and may be redistributed consistent with 79 | this project or the open source license(s) involved. 80 | 81 | ## Code of Conduct 82 | 83 | The Code of Conduct governs how we behave in public or in private 84 | whenever the project will be judged by our actions. 85 | We expect it to be honored by everyone who contributes to this project. 86 | 87 | See [CONDUCT.md](https://github.com/Alamofire/Foundation/blob/master/CONDUCT.md) for details. 88 | 89 | --- 90 | 91 | *Some of the ideas and wording for the statements above were based on work by the [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) and [Linux](http://elinux.org/Developer_Certificate_Of_Origin) communities. We commend them for their efforts to facilitate collaboration in their projects.* 92 | -------------------------------------------------------------------------------- /Example/Source/MasterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MasterViewController.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Alamofire 26 | import UIKit 27 | 28 | class MasterViewController: UITableViewController { 29 | 30 | @IBOutlet weak var titleImageView: UIImageView! 31 | 32 | var detailViewController: DetailViewController? = nil 33 | var objects = NSMutableArray() 34 | 35 | // MARK: - View Lifecycle 36 | 37 | override func awakeFromNib() { 38 | super.awakeFromNib() 39 | 40 | navigationItem.titleView = titleImageView 41 | } 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | if let split = splitViewController { 47 | let controllers = split.viewControllers 48 | 49 | if let 50 | navigationController = controllers.last as? UINavigationController, 51 | topViewController = navigationController.topViewController as? DetailViewController 52 | { 53 | detailViewController = topViewController 54 | } 55 | } 56 | } 57 | 58 | // MARK: - UIStoryboardSegue 59 | 60 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 61 | if let 62 | navigationController = segue.destinationViewController as? UINavigationController, 63 | detailViewController = navigationController.topViewController as? DetailViewController 64 | { 65 | func requestForSegue(segue: UIStoryboardSegue) -> Request? { 66 | switch segue.identifier! { 67 | case "GET": 68 | detailViewController.segueIdentifier = "GET" 69 | return Alamofire.request(.GET, "https://httpbin.org/get") 70 | case "POST": 71 | detailViewController.segueIdentifier = "POST" 72 | return Alamofire.request(.POST, "https://httpbin.org/post") 73 | case "PUT": 74 | detailViewController.segueIdentifier = "PUT" 75 | return Alamofire.request(.PUT, "https://httpbin.org/put") 76 | case "DELETE": 77 | detailViewController.segueIdentifier = "DELETE" 78 | return Alamofire.request(.DELETE, "https://httpbin.org/delete") 79 | case "DOWNLOAD": 80 | detailViewController.segueIdentifier = "DOWNLOAD" 81 | let destination = Alamofire.Request.suggestedDownloadDestination( 82 | directory: .CachesDirectory, 83 | domain: .UserDomainMask 84 | ) 85 | return Alamofire.download(.GET, "https://httpbin.org/stream/1", destination: destination) 86 | default: 87 | return nil 88 | } 89 | } 90 | 91 | if let request = requestForSegue(segue) { 92 | detailViewController.request = request 93 | } 94 | } 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Tests/ResultTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResultTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | @testable import Alamofire 26 | import Foundation 27 | import XCTest 28 | 29 | class ResultTestCase: BaseTestCase { 30 | let error = Error.error(code: .StatusCodeValidationFailed, failureReason: "Status code validation failed") 31 | 32 | // MARK: - Is Success Tests 33 | 34 | func testThatIsSuccessPropertyReturnsTrueForSuccessCase() { 35 | // Given, When 36 | let result = Result.Success("success") 37 | 38 | // Then 39 | XCTAssertTrue(result.isSuccess, "result is success should be true for success case") 40 | } 41 | 42 | func testThatIsSuccessPropertyReturnsFalseForFailureCase() { 43 | // Given, When 44 | let result = Result.Failure(error) 45 | 46 | // Then 47 | XCTAssertFalse(result.isSuccess, "result is success should be true for failure case") 48 | } 49 | 50 | // MARK: - Is Failure Tests 51 | 52 | func testThatIsFailurePropertyReturnsFalseForSuccessCase() { 53 | // Given, When 54 | let result = Result.Success("success") 55 | 56 | // Then 57 | XCTAssertFalse(result.isFailure, "result is failure should be false for success case") 58 | } 59 | 60 | func testThatIsFailurePropertyReturnsTrueForFailureCase() { 61 | // Given, When 62 | let result = Result.Failure(error) 63 | 64 | // Then 65 | XCTAssertTrue(result.isFailure, "result is failure should be true for failure case") 66 | } 67 | 68 | // MARK: - Value Tests 69 | 70 | func testThatValuePropertyReturnsValueForSuccessCase() { 71 | // Given, When 72 | let result = Result.Success("success") 73 | 74 | // Then 75 | XCTAssertEqual(result.value ?? "", "success", "result value should match expected value") 76 | } 77 | 78 | func testThatValuePropertyReturnsNilForFailureCase() { 79 | // Given, When 80 | let result = Result.Failure(error) 81 | 82 | // Then 83 | XCTAssertNil(result.value, "result value should be nil for failure case") 84 | } 85 | 86 | // MARK: - Error Tests 87 | 88 | func testThatErrorPropertyReturnsNilForSuccessCase() { 89 | // Given, When 90 | let result = Result.Success("success") 91 | 92 | // Then 93 | XCTAssertTrue(result.error == nil, "result error should be nil for success case") 94 | } 95 | 96 | func testThatErrorPropertyReturnsErrorForFailureCase() { 97 | // Given, When 98 | let result = Result.Failure(error) 99 | 100 | // Then 101 | XCTAssertTrue(result.error != nil, "result error should not be nil for failure case") 102 | } 103 | 104 | // MARK: - Description Tests 105 | 106 | func testThatDescriptionStringMatchesExpectedValueForSuccessCase() { 107 | // Given, When 108 | let result = Result.Success("success") 109 | 110 | // Then 111 | XCTAssertEqual(result.description, "SUCCESS", "result description should match expected value for success case") 112 | } 113 | 114 | func testThatDescriptionStringMatchesExpectedValueForFailureCase() { 115 | // Given, When 116 | let result = Result.Failure(error) 117 | 118 | // Then 119 | XCTAssertEqual(result.description, "FAILURE", "result description should match expected value for failure case") 120 | } 121 | 122 | // MARK: - Debug Description Tests 123 | 124 | func testThatDebugDescriptionStringMatchesExpectedValueForSuccessCase() { 125 | // Given, When 126 | let result = Result.Success("success value") 127 | 128 | // Then 129 | XCTAssertEqual( 130 | result.debugDescription, 131 | "SUCCESS: success value", 132 | "result debug description should match expected value for success case" 133 | ) 134 | } 135 | 136 | func testThatDebugDescriptionStringMatchesExpectedValueForFailureCase() { 137 | // Given, When 138 | let result = Result.Failure(error) 139 | 140 | // Then 141 | XCTAssertEqual( 142 | result.debugDescription, 143 | "FAILURE: \(error)", 144 | "result debug description should match expected value for failure case" 145 | ) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. 28 | public struct Timeline { 29 | /// The time the request was initialized. 30 | public let requestStartTime: CFAbsoluteTime 31 | 32 | /// The time the first bytes were received from or sent to the server. 33 | public let initialResponseTime: CFAbsoluteTime 34 | 35 | /// The time when the request was completed. 36 | public let requestCompletedTime: CFAbsoluteTime 37 | 38 | /// The time when the response serialization was completed. 39 | public let serializationCompletedTime: CFAbsoluteTime 40 | 41 | /// The time interval in seconds from the time the request started to the initial response from the server. 42 | public let latency: NSTimeInterval 43 | 44 | /// The time interval in seconds from the time the request started to the time the request completed. 45 | public let requestDuration: NSTimeInterval 46 | 47 | /// The time interval in seconds from the time the request completed to the time response serialization completed. 48 | public let serializationDuration: NSTimeInterval 49 | 50 | /// The time interval in seconds from the time the request started to the time response serialization completed. 51 | public let totalDuration: NSTimeInterval 52 | 53 | /** 54 | Creates a new `Timeline` instance with the specified request times. 55 | 56 | - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. 57 | - parameter initialResponseTime: The time the first bytes were received from or sent to the server. 58 | Defaults to `0.0`. 59 | - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. 60 | - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults 61 | to `0.0`. 62 | 63 | - returns: The new `Timeline` instance. 64 | */ 65 | public init( 66 | requestStartTime: CFAbsoluteTime = 0.0, 67 | initialResponseTime: CFAbsoluteTime = 0.0, 68 | requestCompletedTime: CFAbsoluteTime = 0.0, 69 | serializationCompletedTime: CFAbsoluteTime = 0.0) 70 | { 71 | self.requestStartTime = requestStartTime 72 | self.initialResponseTime = initialResponseTime 73 | self.requestCompletedTime = requestCompletedTime 74 | self.serializationCompletedTime = serializationCompletedTime 75 | 76 | self.latency = initialResponseTime - requestStartTime 77 | self.requestDuration = requestCompletedTime - requestStartTime 78 | self.serializationDuration = serializationCompletedTime - requestCompletedTime 79 | self.totalDuration = serializationCompletedTime - requestStartTime 80 | } 81 | } 82 | 83 | // MARK: - CustomStringConvertible 84 | 85 | extension Timeline: CustomStringConvertible { 86 | /// The textual representation used when written to an output stream, which includes the latency, the request 87 | /// duration and the total duration. 88 | public var description: String { 89 | let latency = String(format: "%.3f", self.latency) 90 | let requestDuration = String(format: "%.3f", self.requestDuration) 91 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 92 | let totalDuration = String(format: "%.3f", self.totalDuration) 93 | 94 | let timings = [ 95 | "\"Latency\": \(latency) secs", 96 | "\"Request Duration\": \(requestDuration) secs", 97 | "\"Serialization Duration\": \(serializationDuration) secs", 98 | "\"Total Duration\": \(totalDuration) secs" 99 | ] 100 | 101 | return "Timeline: { \(timings.joinWithSeparator(", ")) }" 102 | } 103 | } 104 | 105 | // MARK: - CustomDebugStringConvertible 106 | 107 | extension Timeline: CustomDebugStringConvertible { 108 | /// The textual representation used when written to an output stream, which includes the request start time, the 109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request 110 | /// duration and the total duration. 111 | public var debugDescription: String { 112 | let timings = [ 113 | "\"Request Start Time\": \(requestStartTime)", 114 | "\"Initial Response Time\": \(initialResponseTime)", 115 | "\"Request Completed Time\": \(requestCompletedTime)", 116 | "\"Serialization Completed Time\": \(serializationCompletedTime)", 117 | "\"Latency\": \(latency) secs", 118 | "\"Request Duration\": \(requestDuration) secs", 119 | "\"Serialization Duration\": \(serializationDuration) secs", 120 | "\"Total Duration\": \(totalDuration) secs" 121 | ] 122 | 123 | return "Timeline: { \(timings.joinWithSeparator(", ")) }" 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Tests/URLProtocolTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLProtocolTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Alamofire 26 | import Foundation 27 | import XCTest 28 | 29 | class ProxyURLProtocol: NSURLProtocol { 30 | 31 | // MARK: Properties 32 | 33 | struct PropertyKeys { 34 | static let HandledByForwarderURLProtocol = "HandledByProxyURLProtocol" 35 | } 36 | 37 | lazy var session: NSURLSession = { 38 | let configuration: NSURLSessionConfiguration = { 39 | let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration() 40 | configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders 41 | 42 | return configuration 43 | }() 44 | 45 | let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil) 46 | 47 | return session 48 | }() 49 | 50 | var activeTask: NSURLSessionTask? 51 | 52 | // MARK: Class Request Methods 53 | 54 | override class func canInitWithRequest(request: NSURLRequest) -> Bool { 55 | if NSURLProtocol.propertyForKey(PropertyKeys.HandledByForwarderURLProtocol, inRequest: request) != nil { 56 | return false 57 | } 58 | 59 | return true 60 | } 61 | 62 | override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest { 63 | if let headers = request.allHTTPHeaderFields { 64 | return ParameterEncoding.URL.encode(request, parameters: headers).0 65 | } 66 | 67 | return request 68 | } 69 | 70 | override class func requestIsCacheEquivalent(a: NSURLRequest, toRequest b: NSURLRequest) -> Bool { 71 | return false 72 | } 73 | 74 | // MARK: Loading Methods 75 | 76 | override func startLoading() { 77 | let mutableRequest = request.URLRequest 78 | NSURLProtocol.setProperty(true, forKey: PropertyKeys.HandledByForwarderURLProtocol, inRequest: mutableRequest) 79 | 80 | activeTask = session.dataTaskWithRequest(mutableRequest) 81 | activeTask?.resume() 82 | } 83 | 84 | override func stopLoading() { 85 | activeTask?.cancel() 86 | } 87 | } 88 | 89 | // MARK: - 90 | 91 | extension ProxyURLProtocol: NSURLSessionDelegate { 92 | 93 | // MARK: NSURLSessionDelegate 94 | 95 | func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { 96 | client?.URLProtocol(self, didLoadData: data) 97 | } 98 | 99 | func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { 100 | if let response = task.response { 101 | client?.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed) 102 | } 103 | 104 | client?.URLProtocolDidFinishLoading(self) 105 | } 106 | } 107 | 108 | // MARK: - 109 | 110 | class URLProtocolTestCase: BaseTestCase { 111 | var manager: Manager! 112 | 113 | // MARK: Setup and Teardown 114 | 115 | override func setUp() { 116 | super.setUp() 117 | 118 | manager = { 119 | let configuration: NSURLSessionConfiguration = { 120 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 121 | configuration.protocolClasses = [ProxyURLProtocol.self] 122 | configuration.HTTPAdditionalHeaders = ["session-configuration-header": "foo"] 123 | 124 | return configuration 125 | }() 126 | 127 | return Manager(configuration: configuration) 128 | }() 129 | } 130 | 131 | // MARK: Tests 132 | 133 | func testThatURLProtocolReceivesRequestHeadersAndSessionConfigurationHeaders() { 134 | // Given 135 | let URLString = "https://httpbin.org/response-headers" 136 | let URL = NSURL(string: URLString)! 137 | 138 | let URLRequest = NSMutableURLRequest(URL: URL) 139 | URLRequest.HTTPMethod = Method.GET.rawValue 140 | URLRequest.setValue("foobar", forHTTPHeaderField: "request-header") 141 | 142 | let expectation = expectationWithDescription("GET request should succeed") 143 | 144 | var request: NSURLRequest? 145 | var response: NSHTTPURLResponse? 146 | var data: NSData? 147 | var error: NSError? 148 | 149 | // When 150 | manager.request(URLRequest) 151 | .response { responseRequest, responseResponse, responseData, responseError in 152 | request = responseRequest 153 | response = responseResponse 154 | data = responseData 155 | error = responseError 156 | 157 | expectation.fulfill() 158 | } 159 | 160 | waitForExpectationsWithTimeout(timeout, handler: nil) 161 | 162 | // Then 163 | XCTAssertNotNil(request, "request should not be nil") 164 | XCTAssertNotNil(response, "response should not be nil") 165 | XCTAssertNotNil(data, "data should not be nil") 166 | XCTAssertNil(error, "error should be nil") 167 | 168 | if let headers = response?.allHeaderFields as? [String: String] { 169 | XCTAssertEqual(headers["request-header"], "foobar") 170 | 171 | // Configuration headers are only passed in on iOS 9.0+ 172 | if #available(iOS 9.0, *) { 173 | XCTAssertEqual(headers["session-configuration-header"], "foo") 174 | } else { 175 | XCTAssertNil(headers["session-configuration-header"]) 176 | } 177 | } else { 178 | XCTFail("headers should not be nil") 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /Source/Stream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stream.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | #if !os(watchOS) 28 | 29 | @available(iOS 9.0, OSX 10.11, tvOS 9.0, *) 30 | extension Manager { 31 | private enum Streamable { 32 | case Stream(String, Int) 33 | case NetService(NSNetService) 34 | } 35 | 36 | private func stream(streamable: Streamable) -> Request { 37 | var streamTask: NSURLSessionStreamTask! 38 | 39 | switch streamable { 40 | case .Stream(let hostName, let port): 41 | dispatch_sync(queue) { 42 | streamTask = self.session.streamTaskWithHostName(hostName, port: port) 43 | } 44 | case .NetService(let netService): 45 | dispatch_sync(queue) { 46 | streamTask = self.session.streamTaskWithNetService(netService) 47 | } 48 | } 49 | 50 | let request = Request(session: session, task: streamTask) 51 | 52 | delegate[request.delegate.task] = request.delegate 53 | 54 | if startRequestsImmediately { 55 | request.resume() 56 | } 57 | 58 | return request 59 | } 60 | 61 | /** 62 | Creates a request for bidirectional streaming with the given hostname and port. 63 | 64 | - parameter hostName: The hostname of the server to connect to. 65 | - parameter port: The port of the server to connect to. 66 | 67 | :returns: The created stream request. 68 | */ 69 | public func stream(hostName hostName: String, port: Int) -> Request { 70 | return stream(.Stream(hostName, port)) 71 | } 72 | 73 | /** 74 | Creates a request for bidirectional streaming with the given `NSNetService`. 75 | 76 | - parameter netService: The net service used to identify the endpoint. 77 | 78 | - returns: The created stream request. 79 | */ 80 | public func stream(netService netService: NSNetService) -> Request { 81 | return stream(.NetService(netService)) 82 | } 83 | } 84 | 85 | // MARK: - 86 | 87 | @available(iOS 9.0, OSX 10.11, tvOS 9.0, *) 88 | extension Manager.SessionDelegate: NSURLSessionStreamDelegate { 89 | 90 | // MARK: Override Closures 91 | 92 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:readClosedForStreamTask:`. 93 | public var streamTaskReadClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 94 | get { 95 | return _streamTaskReadClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 96 | } 97 | set { 98 | _streamTaskReadClosed = newValue 99 | } 100 | } 101 | 102 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:writeClosedForStreamTask:`. 103 | public var streamTaskWriteClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 104 | get { 105 | return _streamTaskWriteClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 106 | } 107 | set { 108 | _streamTaskWriteClosed = newValue 109 | } 110 | } 111 | 112 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:betterRouteDiscoveredForStreamTask:`. 113 | public var streamTaskBetterRouteDiscovered: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 114 | get { 115 | return _streamTaskBetterRouteDiscovered as? (NSURLSession, NSURLSessionStreamTask) -> Void 116 | } 117 | set { 118 | _streamTaskBetterRouteDiscovered = newValue 119 | } 120 | } 121 | 122 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:streamTask:didBecomeInputStream:outputStream:`. 123 | public var streamTaskDidBecomeInputStream: ((NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void)? { 124 | get { 125 | return _streamTaskDidBecomeInputStream as? (NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void 126 | } 127 | set { 128 | _streamTaskDidBecomeInputStream = newValue 129 | } 130 | } 131 | 132 | // MARK: Delegate Methods 133 | 134 | /** 135 | Tells the delegate that the read side of the connection has been closed. 136 | 137 | - parameter session: The session. 138 | - parameter streamTask: The stream task. 139 | */ 140 | public func URLSession(session: NSURLSession, readClosedForStreamTask streamTask: NSURLSessionStreamTask) { 141 | streamTaskReadClosed?(session, streamTask) 142 | } 143 | 144 | /** 145 | Tells the delegate that the write side of the connection has been closed. 146 | 147 | - parameter session: The session. 148 | - parameter streamTask: The stream task. 149 | */ 150 | public func URLSession(session: NSURLSession, writeClosedForStreamTask streamTask: NSURLSessionStreamTask) { 151 | streamTaskWriteClosed?(session, streamTask) 152 | } 153 | 154 | /** 155 | Tells the delegate that the system has determined that a better route to the host is available. 156 | 157 | - parameter session: The session. 158 | - parameter streamTask: The stream task. 159 | */ 160 | public func URLSession(session: NSURLSession, betterRouteDiscoveredForStreamTask streamTask: NSURLSessionStreamTask) { 161 | streamTaskBetterRouteDiscovered?(session, streamTask) 162 | } 163 | 164 | /** 165 | Tells the delegate that the stream task has been completed and provides the unopened stream objects. 166 | 167 | - parameter session: The session. 168 | - parameter streamTask: The stream task. 169 | - parameter inputStream: The new input stream. 170 | - parameter outputStream: The new output stream. 171 | */ 172 | public func URLSession( 173 | session: NSURLSession, 174 | streamTask: NSURLSessionStreamTask, 175 | didBecomeInputStream inputStream: NSInputStream, 176 | outputStream: NSOutputStream) 177 | { 178 | streamTaskDidBecomeInputStream?(session, streamTask, inputStream, outputStream) 179 | } 180 | } 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /Example/Source/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Alamofire 26 | import UIKit 27 | 28 | class DetailViewController: UITableViewController { 29 | enum Sections: Int { 30 | case Headers, Body 31 | } 32 | 33 | var request: Alamofire.Request? { 34 | didSet { 35 | oldValue?.cancel() 36 | 37 | title = request?.description 38 | refreshControl?.endRefreshing() 39 | headers.removeAll() 40 | body = nil 41 | elapsedTime = nil 42 | } 43 | } 44 | 45 | var headers: [String: String] = [:] 46 | var body: String? 47 | var elapsedTime: NSTimeInterval? 48 | var segueIdentifier: String? 49 | 50 | static let numberFormatter: NSNumberFormatter = { 51 | let formatter = NSNumberFormatter() 52 | formatter.numberStyle = .DecimalStyle 53 | return formatter 54 | }() 55 | 56 | // MARK: View Lifecycle 57 | 58 | override func awakeFromNib() { 59 | super.awakeFromNib() 60 | refreshControl?.addTarget(self, action: #selector(DetailViewController.refresh), forControlEvents: .ValueChanged) 61 | } 62 | 63 | override func viewDidAppear(animated: Bool) { 64 | super.viewDidAppear(animated) 65 | refresh() 66 | } 67 | 68 | // MARK: IBActions 69 | 70 | @IBAction func refresh() { 71 | guard let request = request else { 72 | return 73 | } 74 | 75 | refreshControl?.beginRefreshing() 76 | 77 | let start = CACurrentMediaTime() 78 | request.responseString { response in 79 | let end = CACurrentMediaTime() 80 | self.elapsedTime = end - start 81 | 82 | if let response = response.response { 83 | for (field, value) in response.allHeaderFields { 84 | self.headers["\(field)"] = "\(value)" 85 | } 86 | } 87 | 88 | if let segueIdentifier = self.segueIdentifier { 89 | switch segueIdentifier { 90 | case "GET", "POST", "PUT", "DELETE": 91 | self.body = response.result.value 92 | case "DOWNLOAD": 93 | self.body = self.downloadedBodyString() 94 | default: 95 | break 96 | } 97 | } 98 | 99 | self.tableView.reloadData() 100 | self.refreshControl?.endRefreshing() 101 | } 102 | } 103 | 104 | private func downloadedBodyString() -> String { 105 | let fileManager = NSFileManager.defaultManager() 106 | let cachesDirectory = fileManager.URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask)[0] 107 | 108 | do { 109 | let contents = try fileManager.contentsOfDirectoryAtURL( 110 | cachesDirectory, 111 | includingPropertiesForKeys: nil, 112 | options: .SkipsHiddenFiles 113 | ) 114 | 115 | if let 116 | fileURL = contents.first, 117 | data = NSData(contentsOfURL: fileURL) 118 | { 119 | let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) 120 | let prettyData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted) 121 | 122 | if let prettyString = NSString(data: prettyData, encoding: NSUTF8StringEncoding) as? String { 123 | try fileManager.removeItemAtURL(fileURL) 124 | return prettyString 125 | } 126 | } 127 | } catch { 128 | // No-op 129 | } 130 | 131 | return "" 132 | } 133 | } 134 | 135 | // MARK: - UITableViewDataSource 136 | 137 | extension DetailViewController { 138 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 139 | switch Sections(rawValue: section)! { 140 | case .Headers: 141 | return headers.count 142 | case .Body: 143 | return body == nil ? 0 : 1 144 | } 145 | } 146 | 147 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 148 | switch Sections(rawValue: indexPath.section)! { 149 | case .Headers: 150 | let cell = tableView.dequeueReusableCellWithIdentifier("Header")! 151 | let field = headers.keys.sort(<)[indexPath.row] 152 | let value = headers[field] 153 | 154 | cell.textLabel?.text = field 155 | cell.detailTextLabel?.text = value 156 | 157 | return cell 158 | case .Body: 159 | let cell = tableView.dequeueReusableCellWithIdentifier("Body")! 160 | cell.textLabel?.text = body 161 | 162 | return cell 163 | } 164 | } 165 | } 166 | 167 | // MARK: - UITableViewDelegate 168 | 169 | extension DetailViewController { 170 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 171 | return 2 172 | } 173 | 174 | override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 175 | if self.tableView(tableView, numberOfRowsInSection: section) == 0 { 176 | return "" 177 | } 178 | 179 | switch Sections(rawValue: section)! { 180 | case .Headers: 181 | return "Headers" 182 | case .Body: 183 | return "Body" 184 | } 185 | } 186 | 187 | override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 188 | switch Sections(rawValue: indexPath.section)! { 189 | case .Body: 190 | return 300 191 | default: 192 | return tableView.rowHeight 193 | } 194 | } 195 | 196 | override func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { 197 | if Sections(rawValue: section) == .Body, let elapsedTime = elapsedTime { 198 | let elapsedTimeText = DetailViewController.numberFormatter.stringFromNumber(elapsedTime) ?? "???" 199 | return "Elapsed Time: \(elapsedTimeText) sec" 200 | } 201 | 202 | return "" 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /Tests/NetworkReachabilityManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManagerTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | @testable import Alamofire 26 | import Foundation 27 | import SystemConfiguration 28 | import XCTest 29 | 30 | class NetworkReachabilityManagerTestCase: BaseTestCase { 31 | 32 | // MARK: - Tests - Initialization 33 | 34 | func testThatManagerCanBeInitializedFromHost() { 35 | // Given, When 36 | let manager = NetworkReachabilityManager(host: "localhost") 37 | 38 | // Then 39 | XCTAssertNotNil(manager) 40 | } 41 | 42 | func testThatManagerCanBeInitializedFromAddress() { 43 | // Given, When 44 | let manager = NetworkReachabilityManager() 45 | 46 | // Then 47 | XCTAssertNotNil(manager) 48 | } 49 | 50 | func testThatHostManagerIsReachableOnWiFi() { 51 | // Given, When 52 | let manager = NetworkReachabilityManager(host: "localhost") 53 | 54 | // Then 55 | XCTAssertEqual(manager?.networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 56 | XCTAssertEqual(manager?.isReachable, true) 57 | XCTAssertEqual(manager?.isReachableOnWWAN, false) 58 | XCTAssertEqual(manager?.isReachableOnEthernetOrWiFi, true) 59 | } 60 | 61 | func testThatHostManagerStartsWithReachableStatus() { 62 | // Given, When 63 | let manager = NetworkReachabilityManager(host: "localhost") 64 | 65 | // Then 66 | XCTAssertEqual(manager?.networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 67 | XCTAssertEqual(manager?.isReachable, true) 68 | XCTAssertEqual(manager?.isReachableOnWWAN, false) 69 | XCTAssertEqual(manager?.isReachableOnEthernetOrWiFi, true) 70 | } 71 | 72 | func testThatAddressManagerStartsWithReachableStatus() { 73 | // Given, When 74 | let manager = NetworkReachabilityManager() 75 | 76 | // Then 77 | XCTAssertEqual(manager?.networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 78 | XCTAssertEqual(manager?.isReachable, true) 79 | XCTAssertEqual(manager?.isReachableOnWWAN, false) 80 | XCTAssertEqual(manager?.isReachableOnEthernetOrWiFi, true) 81 | } 82 | 83 | func testThatHostManagerCanBeDeinitialized() { 84 | // Given 85 | var manager: NetworkReachabilityManager? = NetworkReachabilityManager(host: "localhost") 86 | 87 | // When 88 | manager = nil 89 | 90 | // Then 91 | XCTAssertNil(manager) 92 | } 93 | 94 | func testThatAddressManagerCanBeDeinitialized() { 95 | // Given 96 | var manager: NetworkReachabilityManager? = NetworkReachabilityManager() 97 | 98 | // When 99 | manager = nil 100 | 101 | // Then 102 | XCTAssertNil(manager) 103 | } 104 | 105 | // MARK: - Tests - Listener 106 | 107 | func testThatHostManagerIsNotifiedWhenStartListeningIsCalled() { 108 | // Given 109 | let manager = NetworkReachabilityManager(host: "localhost") 110 | let expectation = expectationWithDescription("listener closure should be executed") 111 | 112 | var networkReachabilityStatus: NetworkReachabilityManager.NetworkReachabilityStatus? 113 | 114 | manager?.listener = { status in 115 | networkReachabilityStatus = status 116 | expectation.fulfill() 117 | } 118 | 119 | // When 120 | manager?.startListening() 121 | waitForExpectationsWithTimeout(timeout, handler: nil) 122 | 123 | // Then 124 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 125 | } 126 | 127 | func testThatAddressManagerIsNotifiedWhenStartListeningIsCalled() { 128 | // Given 129 | let manager = NetworkReachabilityManager() 130 | let expectation = expectationWithDescription("listener closure should be executed") 131 | 132 | var networkReachabilityStatus: NetworkReachabilityManager.NetworkReachabilityStatus? 133 | 134 | manager?.listener = { status in 135 | networkReachabilityStatus = status 136 | expectation.fulfill() 137 | } 138 | 139 | // When 140 | manager?.startListening() 141 | waitForExpectationsWithTimeout(timeout, handler: nil) 142 | 143 | // Then 144 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 145 | } 146 | 147 | // MARK: - Tests - Network Reachability Status 148 | 149 | func testThatManagerReturnsNotReachableStatusWhenReachableFlagIsAbsent() { 150 | // Given 151 | let manager = NetworkReachabilityManager() 152 | let flags: SCNetworkReachabilityFlags = [.ConnectionOnDemand] 153 | 154 | // When 155 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 156 | 157 | // Then 158 | XCTAssertEqual(networkReachabilityStatus, .NotReachable) 159 | } 160 | 161 | func testThatManagerReturnsNotReachableStatusWhenInterventionIsRequired() { 162 | // Given 163 | let manager = NetworkReachabilityManager() 164 | let flags: SCNetworkReachabilityFlags = [.Reachable, .ConnectionRequired, .ConnectionOnDemand, .InterventionRequired] 165 | 166 | // When 167 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 168 | 169 | // Then 170 | XCTAssertEqual(networkReachabilityStatus, .NotReachable) 171 | } 172 | 173 | func testThatManagerReturnsReachableOnWiFiStatusWhenConnectionIsNotRequired() { 174 | // Given 175 | let manager = NetworkReachabilityManager() 176 | let flags: SCNetworkReachabilityFlags = [.Reachable] 177 | 178 | // When 179 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 180 | 181 | // Then 182 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 183 | } 184 | 185 | func testThatManagerReturnsReachableOnWiFiStatusWhenConnectionIsOnDemand() { 186 | // Given 187 | let manager = NetworkReachabilityManager() 188 | let flags: SCNetworkReachabilityFlags = [.Reachable, .ConnectionRequired, .ConnectionOnDemand] 189 | 190 | // When 191 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 192 | 193 | // Then 194 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 195 | } 196 | 197 | func testThatManagerReturnsReachableOnWiFiStatusWhenConnectionIsOnTraffic() { 198 | // Given 199 | let manager = NetworkReachabilityManager() 200 | let flags: SCNetworkReachabilityFlags = [.Reachable, .ConnectionRequired, .ConnectionOnTraffic] 201 | 202 | // When 203 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 204 | 205 | // Then 206 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.EthernetOrWiFi)) 207 | } 208 | 209 | #if os(iOS) 210 | func testThatManagerReturnsReachableOnWWANStatusWhenIsWWAN() { 211 | // Given 212 | let manager = NetworkReachabilityManager() 213 | let flags: SCNetworkReachabilityFlags = [.Reachable, .IsWWAN] 214 | 215 | // When 216 | let networkReachabilityStatus = manager?.networkReachabilityStatusForFlags(flags) 217 | 218 | // Then 219 | XCTAssertEqual(networkReachabilityStatus, .Reachable(.WWAN)) 220 | } 221 | #endif 222 | } 223 | -------------------------------------------------------------------------------- /Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validation.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Request { 28 | 29 | /** 30 | Used to represent whether validation was successful or encountered an error resulting in a failure. 31 | 32 | - Success: The validation was successful. 33 | - Failure: The validation failed encountering the provided error. 34 | */ 35 | public enum ValidationResult { 36 | case Success 37 | case Failure(NSError) 38 | } 39 | 40 | /** 41 | A closure used to validate a request that takes a URL request and URL response, and returns whether the 42 | request was valid. 43 | */ 44 | public typealias Validation = (NSURLRequest?, NSHTTPURLResponse) -> ValidationResult 45 | 46 | /** 47 | Validates the request, using the specified closure. 48 | 49 | If validation fails, subsequent calls to response handlers will have an associated error. 50 | 51 | - parameter validation: A closure to validate the request. 52 | 53 | - returns: The request. 54 | */ 55 | public func validate(validation: Validation) -> Self { 56 | delegate.queue.addOperationWithBlock { 57 | if let 58 | response = self.response where self.delegate.error == nil, 59 | case let .Failure(error) = validation(self.request, response) 60 | { 61 | self.delegate.error = error 62 | } 63 | } 64 | 65 | return self 66 | } 67 | 68 | // MARK: - Status Code 69 | 70 | /** 71 | Validates that the response has a status code in the specified range. 72 | 73 | If validation fails, subsequent calls to response handlers will have an associated error. 74 | 75 | - parameter range: The range of acceptable status codes. 76 | 77 | - returns: The request. 78 | */ 79 | public func validate(statusCode acceptableStatusCode: S) -> Self { 80 | return validate { _, response in 81 | if acceptableStatusCode.contains(response.statusCode) { 82 | return .Success 83 | } else { 84 | let failureReason = "Response status code was unacceptable: \(response.statusCode)" 85 | 86 | let error = NSError( 87 | domain: Error.Domain, 88 | code: Error.Code.StatusCodeValidationFailed.rawValue, 89 | userInfo: [ 90 | NSLocalizedFailureReasonErrorKey: failureReason, 91 | Error.UserInfoKeys.StatusCode: response.statusCode 92 | ] 93 | ) 94 | 95 | return .Failure(error) 96 | } 97 | } 98 | } 99 | 100 | // MARK: - Content-Type 101 | 102 | private struct MIMEType { 103 | let type: String 104 | let subtype: String 105 | 106 | init?(_ string: String) { 107 | let components: [String] = { 108 | let stripped = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 109 | let split = stripped.substringToIndex(stripped.rangeOfString(";")?.startIndex ?? stripped.endIndex) 110 | return split.componentsSeparatedByString("/") 111 | }() 112 | 113 | if let 114 | type = components.first, 115 | subtype = components.last 116 | { 117 | self.type = type 118 | self.subtype = subtype 119 | } else { 120 | return nil 121 | } 122 | } 123 | 124 | func matches(MIME: MIMEType) -> Bool { 125 | switch (type, subtype) { 126 | case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"): 127 | return true 128 | default: 129 | return false 130 | } 131 | } 132 | } 133 | 134 | /** 135 | Validates that the response has a content type in the specified array. 136 | 137 | If validation fails, subsequent calls to response handlers will have an associated error. 138 | 139 | - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 140 | 141 | - returns: The request. 142 | */ 143 | public func validate(contentType acceptableContentTypes: S) -> Self { 144 | return validate { _, response in 145 | guard let validData = self.delegate.data where validData.length > 0 else { return .Success } 146 | 147 | if let 148 | responseContentType = response.MIMEType, 149 | responseMIMEType = MIMEType(responseContentType) 150 | { 151 | for contentType in acceptableContentTypes { 152 | if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) { 153 | return .Success 154 | } 155 | } 156 | } else { 157 | for contentType in acceptableContentTypes { 158 | if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" { 159 | return .Success 160 | } 161 | } 162 | } 163 | 164 | let contentType: String 165 | let failureReason: String 166 | 167 | if let responseContentType = response.MIMEType { 168 | contentType = responseContentType 169 | 170 | failureReason = ( 171 | "Response content type \"\(responseContentType)\" does not match any acceptable " + 172 | "content types: \(acceptableContentTypes)" 173 | ) 174 | } else { 175 | contentType = "" 176 | failureReason = "Response content type was missing and acceptable content type does not match \"*/*\"" 177 | } 178 | 179 | let error = NSError( 180 | domain: Error.Domain, 181 | code: Error.Code.ContentTypeValidationFailed.rawValue, 182 | userInfo: [ 183 | NSLocalizedFailureReasonErrorKey: failureReason, 184 | Error.UserInfoKeys.ContentType: contentType 185 | ] 186 | ) 187 | 188 | return .Failure(error) 189 | } 190 | } 191 | 192 | // MARK: - Automatic 193 | 194 | /** 195 | Validates that the response has a status code in the default acceptable range of 200...299, and that the content 196 | type matches any specified in the Accept HTTP header field. 197 | 198 | If validation fails, subsequent calls to response handlers will have an associated error. 199 | 200 | - returns: The request. 201 | */ 202 | public func validate() -> Self { 203 | let acceptableStatusCodes: Range = 200..<300 204 | let acceptableContentTypes: [String] = { 205 | if let accept = request?.valueForHTTPHeaderField("Accept") { 206 | return accept.componentsSeparatedByString(",") 207 | } 208 | 209 | return ["*/*"] 210 | }() 211 | 212 | return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /Tests/AuthenticationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Alamofire 26 | import Foundation 27 | import XCTest 28 | 29 | class AuthenticationTestCase: BaseTestCase { 30 | let user = "user" 31 | let password = "password" 32 | var URLString = "" 33 | 34 | var manager: Manager! 35 | 36 | override func setUp() { 37 | super.setUp() 38 | 39 | manager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) 40 | 41 | // Clear out credentials 42 | let credentialStorage = NSURLCredentialStorage.sharedCredentialStorage() 43 | 44 | for (protectionSpace, credentials) in credentialStorage.allCredentials { 45 | for (_, credential) in credentials { 46 | credentialStorage.removeCredential(credential, forProtectionSpace: protectionSpace) 47 | } 48 | } 49 | 50 | // Clear out cookies 51 | let cookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage() 52 | cookieStorage.cookies?.forEach { cookieStorage.deleteCookie($0) } 53 | } 54 | } 55 | 56 | // MARK: - 57 | 58 | class BasicAuthenticationTestCase: AuthenticationTestCase { 59 | override func setUp() { 60 | super.setUp() 61 | URLString = "https://httpbin.org/basic-auth/\(user)/\(password)" 62 | } 63 | 64 | func testHTTPBasicAuthenticationWithInvalidCredentials() { 65 | // Given 66 | let expectation = expectationWithDescription("\(URLString) 401") 67 | 68 | var request: NSURLRequest? 69 | var response: NSHTTPURLResponse? 70 | var data: NSData? 71 | var error: NSError? 72 | 73 | // When 74 | manager.request(.GET, URLString) 75 | .authenticate(user: "invalid", password: "credentials") 76 | .response { responseRequest, responseResponse, responseData, responseError in 77 | request = responseRequest 78 | response = responseResponse 79 | data = responseData 80 | error = responseError 81 | 82 | expectation.fulfill() 83 | } 84 | 85 | waitForExpectationsWithTimeout(timeout, handler: nil) 86 | 87 | // Then 88 | XCTAssertNotNil(request, "request should not be nil") 89 | XCTAssertNotNil(response, "response should not be nil") 90 | XCTAssertEqual(response?.statusCode ?? 0, 401, "response status code should be 401") 91 | XCTAssertNotNil(data, "data should not be nil") 92 | XCTAssertNil(error, "error should be nil") 93 | } 94 | 95 | func testHTTPBasicAuthenticationWithValidCredentials() { 96 | // Given 97 | let expectation = expectationWithDescription("\(URLString) 200") 98 | 99 | var request: NSURLRequest? 100 | var response: NSHTTPURLResponse? 101 | var data: NSData? 102 | var error: NSError? 103 | 104 | // When 105 | manager.request(.GET, URLString) 106 | .authenticate(user: user, password: password) 107 | .response { responseRequest, responseResponse, responseData, responseError in 108 | request = responseRequest 109 | response = responseResponse 110 | data = responseData 111 | error = responseError 112 | 113 | expectation.fulfill() 114 | } 115 | 116 | waitForExpectationsWithTimeout(timeout, handler: nil) 117 | 118 | // Then 119 | XCTAssertNotNil(request, "request should not be nil") 120 | XCTAssertNotNil(response, "response should not be nil") 121 | XCTAssertEqual(response?.statusCode ?? 0, 200, "response status code should be 200") 122 | XCTAssertNotNil(data, "data should not be nil") 123 | XCTAssertNil(error, "error should be nil") 124 | } 125 | 126 | func testHiddenHTTPBasicAuthentication() { 127 | // Given 128 | let authorizationHeader = Request.authorizationHeader(user: user, password: password) 129 | let expectation = expectationWithDescription("\(URLString) 200") 130 | 131 | var request: NSURLRequest? 132 | var response: NSHTTPURLResponse? 133 | var data: NSData? 134 | var error: NSError? 135 | 136 | // When 137 | manager.request(.GET, "http://httpbin.org/hidden-basic-auth/\(user)/\(password)", headers: authorizationHeader) 138 | .response { responseRequest, responseResponse, responseData, responseError in 139 | request = responseRequest 140 | response = responseResponse 141 | data = responseData 142 | error = responseError 143 | 144 | expectation.fulfill() 145 | } 146 | 147 | waitForExpectationsWithTimeout(timeout, handler: nil) 148 | 149 | // Then 150 | XCTAssertNotNil(request, "request should not be nil") 151 | XCTAssertNotNil(response, "response should not be nil") 152 | XCTAssertEqual(response?.statusCode ?? 0, 200, "response status code should be 200") 153 | XCTAssertNotNil(data, "data should not be nil") 154 | XCTAssertNil(error, "error should be nil") 155 | } 156 | } 157 | 158 | // MARK: - 159 | 160 | class HTTPDigestAuthenticationTestCase: AuthenticationTestCase { 161 | let qop = "auth" 162 | 163 | override func setUp() { 164 | super.setUp() 165 | URLString = "https://httpbin.org/digest-auth/\(qop)/\(user)/\(password)" 166 | } 167 | 168 | func testHTTPDigestAuthenticationWithInvalidCredentials() { 169 | // Given 170 | let expectation = expectationWithDescription("\(URLString) 401") 171 | 172 | var request: NSURLRequest? 173 | var response: NSHTTPURLResponse? 174 | var data: NSData? 175 | var error: NSError? 176 | 177 | // When 178 | manager.request(.GET, URLString) 179 | .authenticate(user: "invalid", password: "credentials") 180 | .response { responseRequest, responseResponse, responseData, responseError in 181 | request = responseRequest 182 | response = responseResponse 183 | data = responseData 184 | error = responseError 185 | 186 | expectation.fulfill() 187 | } 188 | 189 | waitForExpectationsWithTimeout(timeout, handler: nil) 190 | 191 | // Then 192 | XCTAssertNotNil(request, "request should not be nil") 193 | XCTAssertNotNil(response, "response should not be nil") 194 | XCTAssertEqual(response?.statusCode ?? 0, 401, "response status code should be 401") 195 | XCTAssertNotNil(data, "data should not be nil") 196 | XCTAssertNil(error, "error should be nil") 197 | } 198 | 199 | func testHTTPDigestAuthenticationWithValidCredentials() { 200 | // Given 201 | let expectation = expectationWithDescription("\(URLString) 200") 202 | 203 | var request: NSURLRequest? 204 | var response: NSHTTPURLResponse? 205 | var data: NSData? 206 | var error: NSError? 207 | 208 | // When 209 | manager.request(.GET, URLString) 210 | .authenticate(user: user, password: password) 211 | .response { responseRequest, responseResponse, responseData, responseError in 212 | request = responseRequest 213 | response = responseResponse 214 | data = responseData 215 | error = responseError 216 | 217 | expectation.fulfill() 218 | } 219 | 220 | waitForExpectationsWithTimeout(timeout, handler: nil) 221 | 222 | // Then 223 | XCTAssertNotNil(request, "request should not be nil") 224 | XCTAssertNotNil(response, "response should not be nil") 225 | XCTAssertEqual(response?.statusCode ?? 0, 200, "response status code should be 200") 226 | XCTAssertNotNil(data, "data should not be nil") 227 | XCTAssertNil(error, "error should be nil") 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Documentation/Alamofire 2.0 Migration Guide.md: -------------------------------------------------------------------------------- 1 | # Alamofire 2.0 Migration Guide 2 | 3 | Alamofire 2.0 is the latest major release of Alamofire, an HTTP networking library for iOS, Mac OS X and watchOS written in Swift. As a major release, following Semantic Versioning conventions, 2.0 introduces several API-breaking changes that one should be aware of. 4 | 5 | This guide is provided in order to ease the transition of existing applications using Alamofire 1.x to the latest APIs, as well as explain the design and structure of new and changed functionality. 6 | 7 | ## New Requirements 8 | 9 | Alamofire 2.0 officially supports iOS 8+, Mac OS X 10.9+, Xcode 7 and Swift 2.0. If you'd like to use Alamofire in a project targeting iOS 7 and Swift 1.x, use the latest tagged 1.x release. 10 | 11 | --- 12 | 13 | ## Breaking API Changes 14 | 15 | ### Swift 2.0 16 | 17 | The biggest change between Alamofire 1.x and Alamofire 2.0 is Swift 2.0. Swift 2 brought many new features to take advantage of such as error handling, protocol extensions and availability checking. Other new features such as `guard` and `defer` do not affect the public APIs, but allowed us to create much cleaner implementations of the same logic. All of the source files, test logic and example code has been updated to reflect the latest Swift 2.0 paradigms. 18 | 19 | > It is not possible to use Alamofire 2.0 without Swift 2.0. 20 | 21 | ### Response Serializers 22 | 23 | The most significant logic change made to Alamofire 2.0 is its new response serialization system leveraging `Result` types. Previously in Alamofire 1.x, each response serializer used the same completion handler signature: 24 | 25 | ```swift 26 | public func response(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self { 27 | return response(serializer: Request.responseDataSerializer(), completionHandler: completionHandler) 28 | } 29 | ``` 30 | 31 | Alamofire 2.0 has redesigned the entire response serialization process to make it much easier to access the original server data without serialization, or serialize the response into a non-optional `Result` type defining whether the `Request` was successful. 32 | 33 | #### No Response Serialization 34 | 35 | The first `response` serializer is non-generic and does not process the server data in any way. It merely forwards on the accumulated information from the `NSURLSessionDelegate` callbacks. 36 | 37 | ```swift 38 | public func response( 39 | queue queue: dispatch_queue_t? = nil, 40 | completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, ErrorType?) -> Void) 41 | -> Self 42 | { 43 | delegate.queue.addOperationWithBlock { 44 | dispatch_async(queue ?? dispatch_get_main_queue()) { 45 | completionHandler(self.request, self.response, self.delegate.data, self.delegate.error) 46 | } 47 | } 48 | 49 | return self 50 | } 51 | ``` 52 | 53 | Another important note of this change is the return type of `data` is now an `NSData` type. You no longer need to cast the `data` parameter from an `AnyObject?` to an `NSData?`. 54 | 55 | #### Generic Response Serializers 56 | 57 | The second, more powerful response serializer leverages generics along with a `Result` type to eliminate the case of the dreaded double optional. 58 | 59 | ```swift 60 | public func response( 61 | queue queue: dispatch_queue_t? = nil, 62 | responseSerializer: T, 63 | completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result) -> Void) 64 | -> Self 65 | { 66 | delegate.queue.addOperationWithBlock { 67 | let result: Result = { 68 | if let error = self.delegate.error { 69 | return .Failure(self.delegate.data, error) 70 | } else { 71 | return responseSerializer.serializeResponse(self.request, self.response, self.delegate.data) 72 | } 73 | }() 74 | 75 | dispatch_async(queue ?? dispatch_get_main_queue()) { 76 | completionHandler(self.request, self.response, result) 77 | } 78 | } 79 | 80 | return self 81 | } 82 | ``` 83 | 84 | ##### Response Data 85 | 86 | ```swift 87 | Alamofire.request(.GET, "http://httpbin.org/get") 88 | .responseData { _, _, result in 89 | print("Success: \(result.isSuccess)") 90 | print("Response: \(result)") 91 | } 92 | ``` 93 | 94 | ##### Response String 95 | 96 | ```swift 97 | Alamofire.request(.GET, "http://httpbin.org/get") 98 | .responseString { _, _, result in 99 | print("Success: \(result.isSuccess)") 100 | print("Response String: \(result.value)") 101 | } 102 | ``` 103 | 104 | ##### Response JSON 105 | 106 | ```swift 107 | Alamofire.request(.GET, "http://httpbin.org/get") 108 | .responseJSON { _, _, result in 109 | print(result) 110 | debugPrint(result) 111 | } 112 | ``` 113 | 114 | #### Result Types 115 | 116 | The `Result` enumeration was added to handle the case of the double optional return type. Previously, the return value and error were both optionals. Checking if one was `nil` did not ensure the other was also not `nil`. This case has been blogged about many times and can be solved by a `Result` type. Alamofire 2.0 brings a `Result` type to the response serializers to make it much easier to handle success and failure cases. 117 | 118 | ```swift 119 | public enum Result { 120 | case Success(Value) 121 | case Failure(NSData?, ErrorType) 122 | } 123 | ``` 124 | 125 | There are also many other convenience computed properties to make accessing the data inside easy. The `Result` type also conforms to the `CustomStringConvertible` and `CustomDebugStringConvertible` protocols to make it easier to debug. 126 | 127 | #### Error Types 128 | 129 | While Alamofire still only generates `NSError` objects, all `Result` types have been converted to store `ErrorType` objects to allow custom response serializer implementations to use any `ErrorType` they wish. This also includes the `ValidationResult` and `MultipartFormDataEncodingResult` types as well. 130 | 131 | ### URLRequestConvertible 132 | 133 | In order to make it easier to deal with non-common scenarios, the `URLRequestConvertible` protocol now returns an `NSMutableURLRequest`. Alamofire 2.0 makes it much easier to customize the URL request after is has been encoded. This should only affect a small amount of users. 134 | 135 | ```swift 136 | public protocol URLRequestConvertible { 137 | var URLRequest: NSMutableURLRequest { get } 138 | } 139 | ``` 140 | 141 | ### Multipart Form Data 142 | 143 | Encoding `MultipartFormData` previous returned an `EncodingResult` to encapsulate any possible errors that occurred during encoding. Alamofire 2.0 uses the new Swift 2.0 error handling instead making it easier to use. This change is mostly encapsulated internally and should only affect a very small subset of users. 144 | 145 | --- 146 | 147 | ## Updated ACLs and New Features 148 | 149 | ### Parameter Encoding 150 | 151 | #### ACL Updates 152 | 153 | The `ParameterEncoding` enumeration implementation was previously hidden behind `internal` and `private` ACLs. Alamofire 2.0 opens up the `queryComponents` and `escape` methods to make it much easier to implement `.Custom` cases. 154 | 155 | #### Encoding in the URL 156 | 157 | In the previous versions of Alamofire, `.URL` encoding would automatically append the query string to either the URL or HTTP body depending on which HTTP method was set in the `NSURLRequest`. While this satisfies the majority of common use cases, it made it quite difficult to append query string parameter to a URL for HTTP methods such as `PUT` and `POST`. In Alamofire 2.0, we've added a second URL encoding case, `.URLEncodedInURL`, that always appends the query string to the URL regardless of HTTP method. 158 | 159 | ### Server Trust Policies 160 | 161 | In Alamofire 1.x, the `ServerTrustPolicyManager` methods were internal making it impossible to implement any custom domain matching behavior. Alamofire 2.0 opens up the internals with a `public` ACL allowing more flexible server trust policy matching behavior (i.e. wildcarded domains) through subclassing. 162 | 163 | ```swift 164 | class CustomServerTrustPolicyManager: ServerTrustPolicyManager { 165 | override func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? { 166 | var policy: ServerTrustPolicy? 167 | 168 | // Implement your custom domain matching behavior... 169 | 170 | return policy 171 | } 172 | } 173 | ``` 174 | 175 | ### Download Requests 176 | 177 | The global and `Manager` download APIs now support `parameters` and `encoding` parameters to better support dynamic payloads used in background sessions. Constructing a `download` request is now the same as constructing a `data` request with the addition of a `destination` parameter. 178 | 179 | ```swift 180 | public func download( 181 | method: Method, 182 | _ URLString: URLStringConvertible, 183 | parameters: [String: AnyObject]? = nil, 184 | encoding: ParameterEncoding = .URL, 185 | headers: [String: String]? = nil, 186 | destination: Request.DownloadFileDestination) 187 | -> Request 188 | { 189 | return Manager.sharedInstance.download( 190 | method, 191 | URLString, 192 | parameters: parameters, 193 | encoding: encoding, 194 | headers: headers, 195 | destination: destination 196 | ) 197 | } 198 | ``` 199 | 200 | ### Stream Tasks 201 | 202 | Alamofire 2.0 adds support for creating `NSURLSessionStreamTask` tasks for iOS 9 and OS X 10.11. It also extends the `SessionDelegate` to support all the new `NSURLSessionStreamDelegate` APIs. 203 | -------------------------------------------------------------------------------- /Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #if !os(watchOS) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /** 31 | The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and 32 | WiFi network interfaces. 33 | 34 | Reachability can be used to determine background information about why a network operation failed, or to retry 35 | network requests when a connection is established. It should not be used to prevent a user from initiating a network 36 | request, as it's possible that an initial request may be required to establish reachability. 37 | */ 38 | public class NetworkReachabilityManager { 39 | /** 40 | Defines the various states of network reachability. 41 | 42 | - Unknown: It is unknown whether the network is reachable. 43 | - NotReachable: The network is not reachable. 44 | - ReachableOnWWAN: The network is reachable over the WWAN connection. 45 | - ReachableOnWiFi: The network is reachable over the WiFi connection. 46 | */ 47 | public enum NetworkReachabilityStatus { 48 | case Unknown 49 | case NotReachable 50 | case Reachable(ConnectionType) 51 | } 52 | 53 | /** 54 | Defines the various connection types detected by reachability flags. 55 | 56 | - EthernetOrWiFi: The connection type is either over Ethernet or WiFi. 57 | - WWAN: The connection type is a WWAN connection. 58 | */ 59 | public enum ConnectionType { 60 | case EthernetOrWiFi 61 | case WWAN 62 | } 63 | 64 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 65 | /// network reachability status. 66 | public typealias Listener = NetworkReachabilityStatus -> Void 67 | 68 | // MARK: - Properties 69 | 70 | /// Whether the network is currently reachable. 71 | public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } 72 | 73 | /// Whether the network is currently reachable over the WWAN interface. 74 | public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .Reachable(.WWAN) } 75 | 76 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 77 | public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .Reachable(.EthernetOrWiFi) } 78 | 79 | /// The current network reachability status. 80 | public var networkReachabilityStatus: NetworkReachabilityStatus { 81 | guard let flags = self.flags else { return .Unknown } 82 | return networkReachabilityStatusForFlags(flags) 83 | } 84 | 85 | /// The dispatch queue to execute the `listener` closure on. 86 | public var listenerQueue: dispatch_queue_t = dispatch_get_main_queue() 87 | 88 | /// A closure executed when the network reachability status changes. 89 | public var listener: Listener? 90 | 91 | private var flags: SCNetworkReachabilityFlags? { 92 | var flags = SCNetworkReachabilityFlags() 93 | 94 | if SCNetworkReachabilityGetFlags(reachability, &flags) { 95 | return flags 96 | } 97 | 98 | return nil 99 | } 100 | 101 | private let reachability: SCNetworkReachability 102 | private var previousFlags: SCNetworkReachabilityFlags 103 | 104 | // MARK: - Initialization 105 | 106 | /** 107 | Creates a `NetworkReachabilityManager` instance with the specified host. 108 | 109 | - parameter host: The host used to evaluate network reachability. 110 | 111 | - returns: The new `NetworkReachabilityManager` instance. 112 | */ 113 | public convenience init?(host: String) { 114 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 115 | self.init(reachability: reachability) 116 | } 117 | 118 | /** 119 | Creates a `NetworkReachabilityManager` instance with the default socket IPv4 or IPv6 address. 120 | 121 | - returns: The new `NetworkReachabilityManager` instance. 122 | */ 123 | public convenience init?() { 124 | if #available(iOS 9.0, OSX 10.10, *) { 125 | var address = sockaddr_in6() 126 | address.sin6_len = UInt8(sizeofValue(address)) 127 | address.sin6_family = sa_family_t(AF_INET6) 128 | 129 | guard let reachability = withUnsafePointer(&address, { 130 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) 131 | }) else { return nil } 132 | 133 | self.init(reachability: reachability) 134 | } else { 135 | var address = sockaddr_in() 136 | address.sin_len = UInt8(sizeofValue(address)) 137 | address.sin_family = sa_family_t(AF_INET) 138 | 139 | guard let reachability = withUnsafePointer(&address, { 140 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) 141 | }) else { return nil } 142 | 143 | self.init(reachability: reachability) 144 | } 145 | } 146 | 147 | private init(reachability: SCNetworkReachability) { 148 | self.reachability = reachability 149 | self.previousFlags = SCNetworkReachabilityFlags() 150 | } 151 | 152 | deinit { 153 | stopListening() 154 | } 155 | 156 | // MARK: - Listening 157 | 158 | /** 159 | Starts listening for changes in network reachability status. 160 | 161 | - returns: `true` if listening was started successfully, `false` otherwise. 162 | */ 163 | public func startListening() -> Bool { 164 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 165 | context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()) 166 | 167 | let callbackEnabled = SCNetworkReachabilitySetCallback( 168 | reachability, 169 | { (_, flags, info) in 170 | let reachability = Unmanaged.fromOpaque(COpaquePointer(info)).takeUnretainedValue() 171 | reachability.notifyListener(flags) 172 | }, 173 | &context 174 | ) 175 | 176 | let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) 177 | 178 | dispatch_async(listenerQueue) { 179 | self.previousFlags = SCNetworkReachabilityFlags() 180 | self.notifyListener(self.flags ?? SCNetworkReachabilityFlags()) 181 | } 182 | 183 | return callbackEnabled && queueEnabled 184 | } 185 | 186 | /** 187 | Stops listening for changes in network reachability status. 188 | */ 189 | public func stopListening() { 190 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 191 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 192 | } 193 | 194 | // MARK: - Internal - Listener Notification 195 | 196 | func notifyListener(flags: SCNetworkReachabilityFlags) { 197 | guard previousFlags != flags else { return } 198 | previousFlags = flags 199 | 200 | listener?(networkReachabilityStatusForFlags(flags)) 201 | } 202 | 203 | // MARK: - Internal - Network Reachability Status 204 | 205 | func networkReachabilityStatusForFlags(flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { 206 | guard flags.contains(.Reachable) else { return .NotReachable } 207 | 208 | var networkStatus: NetworkReachabilityStatus = .NotReachable 209 | 210 | if !flags.contains(.ConnectionRequired) { networkStatus = .Reachable(.EthernetOrWiFi) } 211 | 212 | if flags.contains(.ConnectionOnDemand) || flags.contains(.ConnectionOnTraffic) { 213 | if !flags.contains(.InterventionRequired) { networkStatus = .Reachable(.EthernetOrWiFi) } 214 | } 215 | 216 | #if os(iOS) 217 | if flags.contains(.IsWWAN) { networkStatus = .Reachable(.WWAN) } 218 | #endif 219 | 220 | return networkStatus 221 | } 222 | } 223 | 224 | // MARK: - 225 | 226 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 227 | 228 | /** 229 | Returns whether the two network reachability status values are equal. 230 | 231 | - parameter lhs: The left-hand side value to compare. 232 | - parameter rhs: The right-hand side value to compare. 233 | 234 | - returns: `true` if the two values are equal, `false` otherwise. 235 | */ 236 | public func ==( 237 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 238 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 239 | -> Bool 240 | { 241 | switch (lhs, rhs) { 242 | case (.Unknown, .Unknown): 243 | return true 244 | case (.NotReachable, .NotReachable): 245 | return true 246 | case let (.Reachable(lhsConnectionType), .Reachable(rhsConnectionType)): 247 | return lhsConnectionType == rhsConnectionType 248 | default: 249 | return false 250 | } 251 | } 252 | 253 | #endif 254 | -------------------------------------------------------------------------------- /Source/Download.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Download.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Manager { 28 | private enum Downloadable { 29 | case Request(NSURLRequest) 30 | case ResumeData(NSData) 31 | } 32 | 33 | private func download(downloadable: Downloadable, destination: Request.DownloadFileDestination) -> Request { 34 | var downloadTask: NSURLSessionDownloadTask! 35 | 36 | switch downloadable { 37 | case .Request(let request): 38 | dispatch_sync(queue) { 39 | downloadTask = self.session.downloadTaskWithRequest(request) 40 | } 41 | case .ResumeData(let resumeData): 42 | dispatch_sync(queue) { 43 | downloadTask = self.session.downloadTaskWithResumeData(resumeData) 44 | } 45 | } 46 | 47 | let request = Request(session: session, task: downloadTask) 48 | 49 | if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate { 50 | downloadDelegate.downloadTaskDidFinishDownloadingToURL = { session, downloadTask, URL in 51 | return destination(URL, downloadTask.response as! NSHTTPURLResponse) 52 | } 53 | } 54 | 55 | delegate[request.delegate.task] = request.delegate 56 | 57 | if startRequestsImmediately { 58 | request.resume() 59 | } 60 | 61 | return request 62 | } 63 | 64 | // MARK: Request 65 | 66 | /** 67 | Creates a download request for the specified method, URL string, parameters, parameter encoding, headers 68 | and destination. 69 | 70 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 71 | 72 | - parameter method: The HTTP method. 73 | - parameter URLString: The URL string. 74 | - parameter parameters: The parameters. `nil` by default. 75 | - parameter encoding: The parameter encoding. `.URL` by default. 76 | - parameter headers: The HTTP headers. `nil` by default. 77 | - parameter destination: The closure used to determine the destination of the downloaded file. 78 | 79 | - returns: The created download request. 80 | */ 81 | public func download( 82 | method: Method, 83 | _ URLString: URLStringConvertible, 84 | parameters: [String: AnyObject]? = nil, 85 | encoding: ParameterEncoding = .URL, 86 | headers: [String: String]? = nil, 87 | destination: Request.DownloadFileDestination) 88 | -> Request 89 | { 90 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 91 | let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 92 | 93 | return download(encodedURLRequest, destination: destination) 94 | } 95 | 96 | /** 97 | Creates a request for downloading from the specified URL request. 98 | 99 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 100 | 101 | - parameter URLRequest: The URL request 102 | - parameter destination: The closure used to determine the destination of the downloaded file. 103 | 104 | - returns: The created download request. 105 | */ 106 | public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { 107 | return download(.Request(URLRequest.URLRequest), destination: destination) 108 | } 109 | 110 | // MARK: Resume Data 111 | 112 | /** 113 | Creates a request for downloading from the resume data produced from a previous request cancellation. 114 | 115 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 116 | 117 | - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` 118 | when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for 119 | additional information. 120 | - parameter destination: The closure used to determine the destination of the downloaded file. 121 | 122 | - returns: The created download request. 123 | */ 124 | public func download(resumeData: NSData, destination: Request.DownloadFileDestination) -> Request { 125 | return download(.ResumeData(resumeData), destination: destination) 126 | } 127 | } 128 | 129 | // MARK: - 130 | 131 | extension Request { 132 | /** 133 | A closure executed once a request has successfully completed in order to determine where to move the temporary 134 | file written to during the download process. The closure takes two arguments: the temporary file URL and the URL 135 | response, and returns a single argument: the file URL where the temporary file should be moved. 136 | */ 137 | public typealias DownloadFileDestination = (NSURL, NSHTTPURLResponse) -> NSURL 138 | 139 | /** 140 | Creates a download file destination closure which uses the default file manager to move the temporary file to a 141 | file URL in the first available directory with the specified search path directory and search path domain mask. 142 | 143 | - parameter directory: The search path directory. `.DocumentDirectory` by default. 144 | - parameter domain: The search path domain mask. `.UserDomainMask` by default. 145 | 146 | - returns: A download file destination closure. 147 | */ 148 | public class func suggestedDownloadDestination( 149 | directory directory: NSSearchPathDirectory = .DocumentDirectory, 150 | domain: NSSearchPathDomainMask = .UserDomainMask) 151 | -> DownloadFileDestination 152 | { 153 | return { temporaryURL, response -> NSURL in 154 | let directoryURLs = NSFileManager.defaultManager().URLsForDirectory(directory, inDomains: domain) 155 | 156 | if !directoryURLs.isEmpty { 157 | return directoryURLs[0].URLByAppendingPathComponent(response.suggestedFilename!) 158 | } 159 | 160 | return temporaryURL 161 | } 162 | } 163 | 164 | /// The resume data of the underlying download task if available after a failure. 165 | public var resumeData: NSData? { 166 | var data: NSData? 167 | 168 | if let delegate = delegate as? DownloadTaskDelegate { 169 | data = delegate.resumeData 170 | } 171 | 172 | return data 173 | } 174 | 175 | // MARK: - DownloadTaskDelegate 176 | 177 | class DownloadTaskDelegate: TaskDelegate, NSURLSessionDownloadDelegate { 178 | var downloadTask: NSURLSessionDownloadTask? { return task as? NSURLSessionDownloadTask } 179 | var downloadProgress: ((Int64, Int64, Int64) -> Void)? 180 | 181 | var resumeData: NSData? 182 | override var data: NSData? { return resumeData } 183 | 184 | // MARK: - NSURLSessionDownloadDelegate 185 | 186 | // MARK: Override Closures 187 | 188 | var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> NSURL)? 189 | var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 190 | var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)? 191 | 192 | // MARK: Delegate Methods 193 | 194 | func URLSession( 195 | session: NSURLSession, 196 | downloadTask: NSURLSessionDownloadTask, 197 | didFinishDownloadingToURL location: NSURL) 198 | { 199 | if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { 200 | do { 201 | let destination = downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) 202 | try NSFileManager.defaultManager().moveItemAtURL(location, toURL: destination) 203 | } catch { 204 | self.error = error as NSError 205 | } 206 | } 207 | } 208 | 209 | func URLSession( 210 | session: NSURLSession, 211 | downloadTask: NSURLSessionDownloadTask, 212 | didWriteData bytesWritten: Int64, 213 | totalBytesWritten: Int64, 214 | totalBytesExpectedToWrite: Int64) 215 | { 216 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 217 | 218 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 219 | downloadTaskDidWriteData( 220 | session, 221 | downloadTask, 222 | bytesWritten, 223 | totalBytesWritten, 224 | totalBytesExpectedToWrite 225 | ) 226 | } else { 227 | progress.totalUnitCount = totalBytesExpectedToWrite 228 | progress.completedUnitCount = totalBytesWritten 229 | 230 | downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) 231 | } 232 | } 233 | 234 | func URLSession( 235 | session: NSURLSession, 236 | downloadTask: NSURLSessionDownloadTask, 237 | didResumeAtOffset fileOffset: Int64, 238 | expectedTotalBytes: Int64) 239 | { 240 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 241 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 242 | } else { 243 | progress.totalUnitCount = expectedTotalBytes 244 | progress.completedUnitCount = fileOffset 245 | } 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /Documentation/Alamofire 3.0 Migration Guide.md: -------------------------------------------------------------------------------- 1 | # Alamofire 3.0 Migration Guide 2 | 3 | Alamofire 3.0 is the latest major release of Alamofire, an HTTP networking library for iOS, Mac OS X and watchOS written in Swift. As a major release, following Semantic Versioning conventions, 3.0 introduces several API-breaking changes that one should be aware of. 4 | 5 | This guide is provided in order to ease the transition of existing applications using Alamofire 2.x to the latest APIs, as well as explain the design and structure of new and changed functionality. 6 | 7 | ## Requirements 8 | 9 | Alamofire 3.0 officially supports iOS 8+, Mac OS X 10.9+, watchOS 2.0, Xcode 7 and Swift 2.0. If you'd like to use Alamofire in a project targeting iOS 7 and Swift 1.x, use the latest tagged 1.x release. 10 | 11 | ## Reasons for Bumping to 3.0 12 | 13 | The [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) (ASF) tries to do everything possible to avoid MAJOR version bumps. We realize the challenges involved with migrating large projects from one MAJOR version to another. With that said, we also want to make sure we're always producing the highest quality APIs and features possible. 14 | 15 | After releasing Alamofire 2.0, it became clear that the response serialization system still had some room for improvement. After much debate, we decided to strictly follow semver and move forward with all the core logic changes becoming Alamofire 3.0. We've also made some fairly significant changes that should give us more flexibility moving forward to help avoid the need for MAJOR version bumps to maintain backwards compatibility. 16 | 17 | ## Benefits of Upgrading 18 | 19 | The benefits of upgrading can be summarized as follows: 20 | 21 | * No more casting a response serializer `error` from an `ErrorType` to an `NSError`. 22 | * Original server data is now ALWAYS returned in all response serializers regardless of whether the result was a `.Success` or `.Failure`. 23 | * Custom response serializers are now ALWAYS called regardless of whether an `error` occurred. 24 | * Custom response serializers are now passed in the `error` allowing you to switch between different parsing schemes if necessary. 25 | * Custom response serializers can now wrap up any Alamofire `NSError` into a `CustomError` type of your choosing. 26 | * `Manager` initialization can now accept custom `NSURLSession` or `SessionDelegate` objects using dependency injection. 27 | 28 | --- 29 | 30 | ## Breaking API Changes 31 | 32 | Alamofire 3.0 contains some breaking API changes to the foundational classes supporting the response serialization system. It is important to understand how these changes affect the common usage patterns. 33 | 34 | ### Result Type 35 | 36 | The `Result` type was introduced in Alamofire 2.0 as a single generic parameter with the following signature: 37 | 38 | ```swift 39 | public enum Result { 40 | case Success(Value) 41 | case Failure(NSData?, ErrorType) 42 | } 43 | ``` 44 | 45 | While this was a significant improvement on the behavior of Alamofire 1.0, there was still room for improvement. By defining the `.Failure` case to take an `ErrorType`, all consumers needed to cast the `ErrorType` to some concrete object such as an `NSError` before being able to interact with it. This was certainly not ideal. Additionally, by only allowing the `NSData?` from the server to be appended in a `.Failure` case, it was not possible to access the original server data in a `.Success` case. 46 | 47 | In Alamofire 3.0, the `Result` type has been redesigned to be a double generic type that does not store the `NSData?` in the `.Failure` case. 48 | 49 | ```swift 50 | public enum Result { 51 | case Success(Value) 52 | case Failure(Error) 53 | } 54 | ``` 55 | 56 | These changes allow Alamofire to return the original server data in both cases. It also removes the requirement of having to cast the `ErrorType` when working with the `.Failure` case error object. 57 | 58 | ### Response 59 | 60 | In order to avoid constantly having to change the response serializer completion closure signatures, Alamofire 3.0 introduces a `Response` struct. All response serializers (with the exception of `response`) return a generic `Response` struct. 61 | 62 | ```swift 63 | public struct Response { 64 | /// The URL request sent to the server. 65 | public let request: NSURLRequest? 66 | 67 | /// The server's response to the URL request. 68 | public let response: NSHTTPURLResponse? 69 | 70 | /// The data returned by the server. 71 | public let data: NSData? 72 | 73 | /// The result of response serialization. 74 | public let result: Result 75 | } 76 | ``` 77 | 78 | This unifies the signature of all response serializer completion closures by only needing to specify a single parameter rather than three or four. If another major release of Alamofire needs to modify the signature, thankfully the number of parameters in all response serializers will NOT need to change. Given the fact that the Swift compiler can present some fairly misleading compiler errors when the arguments are not correct, this should help alleviate some painful updates between MAJOR version bumps of Alamofire. 79 | 80 | ### Response Serializers 81 | 82 | The biggest change in Alamofire 3.0 are the response serializers. They are now powered by the new `Response` struct and updated `Result` type. These two generic classes make it VERY easy to interact with the response serializers in a consistent, type-safe manner. 83 | 84 | ```swift 85 | Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) 86 | .responseJSON { response in 87 | debugPrint(response) // prints detailed description of all response properties 88 | 89 | print(response.request) // original URL request 90 | print(response.response) // URL response 91 | print(response.data) // server data 92 | print(response.result) // result of response serialization 93 | 94 | if let JSON = response.result.value { 95 | print("JSON: \(JSON)") 96 | } 97 | } 98 | ``` 99 | 100 | Besides the single response parameter in the completion closure, the other major callouts are that the original server data is always available whether the `Result` was a `.Success` or `.Failure`. Additionally, both the `value` and `error` of the `Result` type are strongly typed objects thanks to the power of generics. All default response serializer errors will be an `NSError` type. Custom response serializers can specify any custom `ErrorType`. 101 | 102 | #### Response Serializer Type 103 | 104 | For those wishing to create custom response serializer types, you'll need to familiarize yourself with the new `ResponseSerializerType` protocol and generic `ResponseSerializer` struct. 105 | 106 | ```swift 107 | public protocol ResponseSerializerType { 108 | /// The type of serialized object to be created by this `ResponseSerializerType`. 109 | typealias SerializedObject 110 | 111 | /// The type of error to be created by this `ResponseSerializer` if serialization fails. 112 | typealias ErrorObject: ErrorType 113 | 114 | /** 115 | A closure used by response handlers that takes a request, response, data and error and returns a result. 116 | */ 117 | var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result { get } 118 | } 119 | ``` 120 | 121 | All the possible information about the `Request` is now passed into the `serializeResponse` closure. In Alamofire 3.0, the `serializeResponse` closure is ALWAYS called whether an error occurred or not. This is for several reasons. 122 | 123 | 1. Passing the error into the response serializer allows the implementation to switch parsing schemes based on what error occurred. For example, some APIs will return different payload schemas when certain errors occur. The new design allows you to switch on the error type and use different parsing logic. 124 | 2. Any error produced by Alamofire will always be an `NSError`. If your custom response serializer returns `CustomError` types, then the `NSError` returned by Alamofire must be converted into a `CustomError` type. This makes it MUCH easier to wrap Alamofire errors in your own `CustomError` type objects. 125 | > This is also required for all the generics logic to work properly. 126 | 127 | ### Validation Result 128 | 129 | The `ValidationResult` enumeration in Alamofire 3.0 has been updated to take an `NSError` in the `.Failure` case. The reasoning for this change is that all Alamofire errors generated need to be `NSError` types. If not, it introduces the need to cast all error objects coming from Alamofire at the response serializer level. 130 | 131 | ```swift 132 | public enum ValidationResult { 133 | case Success 134 | case Failure(NSError) 135 | } 136 | ``` 137 | 138 | > If you are extending the `Request` type in any way that can produce an error, that error always needs to be of type `NSError`. If you'd like to wrap the error into a `CustomError` type, it should be wrapped in a custom response serializer implementation. 139 | 140 | --- 141 | 142 | ## New Features 143 | 144 | ### Dependency Injection 145 | 146 | Alamofire 3.0 leverages [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) to allow some powerful new customizations to take place for the URL session and delegate. 147 | 148 | #### Session Delegate 149 | 150 | In previous versions of Alamofire, the `SessionDelegate` was automatically created by the `Manager` instance. While this is convenient, it can be problematic for background sessions. One may need to hook up the task override closures before instantiating the URL session. Otherwise the URL session delegate could be called before the task override closures are able to be set. 151 | 152 | In Alamofire 3.0, the `Manager` initializer adds the ability to provide a custom `SessionDelegate` object with the task override closures already set using dependency injection. This greatly increases the flexibility of Alamofire in regards to background sessions. 153 | 154 | ```swift 155 | public init( 156 | configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(), 157 | delegate: SessionDelegate = SessionDelegate(), 158 | serverTrustPolicyManager: ServerTrustPolicyManager? = nil) 159 | { 160 | self.delegate = delegate 161 | self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) 162 | 163 | commonInit(serverTrustPolicyManager: serverTrustPolicyManager) 164 | } 165 | ``` 166 | 167 | #### URL Session 168 | 169 | Alamofire 3.0 also adds the ability to use dependency injection to provide a custom `NSURLSession` to the `Manager` instance. This provides complete control over the URL session initialization if you need it allowing `NSURLSession` subclasses for various kinds of testing and DVR implementations. 170 | 171 | ```swift 172 | public init?( 173 | session: NSURLSession, 174 | delegate: SessionDelegate, 175 | serverTrustPolicyManager: ServerTrustPolicyManager? = nil) 176 | { 177 | self.delegate = delegate 178 | self.session = session 179 | 180 | guard delegate === session.delegate else { return nil } 181 | 182 | commonInit(serverTrustPolicyManager: serverTrustPolicyManager) 183 | } 184 | ``` 185 | 186 | > We're very excited to see what the community comes up with given these new possibilities with Alamofire 3.0. 187 | -------------------------------------------------------------------------------- /Tests/ResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Alamofire 26 | import Foundation 27 | import XCTest 28 | 29 | class ResponseDataTestCase: BaseTestCase { 30 | func testThatResponseDataReturnsSuccessResultWithValidData() { 31 | // Given 32 | let URLString = "https://httpbin.org/get" 33 | let expectation = expectationWithDescription("request should succeed") 34 | 35 | var response: Response? 36 | 37 | // When 38 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 39 | .responseData { closureResponse in 40 | response = closureResponse 41 | expectation.fulfill() 42 | } 43 | 44 | waitForExpectationsWithTimeout(timeout, handler: nil) 45 | 46 | // Then 47 | if let response = response { 48 | XCTAssertNotNil(response.request, "request should not be nil") 49 | XCTAssertNotNil(response.response, "response should not be nil") 50 | XCTAssertNotNil(response.data, "data should not be nil") 51 | XCTAssertTrue(response.result.isSuccess, "result should be success") 52 | } else { 53 | XCTFail("response should not be nil") 54 | } 55 | } 56 | 57 | func testThatResponseDataReturnsFailureResultWithOptionalDataAndError() { 58 | // Given 59 | let URLString = "https://invalid-url-here.org/this/does/not/exist" 60 | let expectation = expectationWithDescription("request should fail with 404") 61 | 62 | var response: Response? 63 | 64 | // When 65 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 66 | .responseData { closureResponse in 67 | response = closureResponse 68 | expectation.fulfill() 69 | } 70 | 71 | waitForExpectationsWithTimeout(timeout, handler: nil) 72 | 73 | // Then 74 | if let response = response { 75 | XCTAssertNotNil(response.request, "request should not be nil") 76 | XCTAssertNil(response.response, "response should be nil") 77 | XCTAssertNotNil(response.data, "data should not be nil") 78 | XCTAssertTrue(response.result.isFailure, "result should be failure") 79 | } else { 80 | XCTFail("response should not be nil") 81 | } 82 | } 83 | } 84 | 85 | // MARK: - 86 | 87 | class ResponseStringTestCase: BaseTestCase { 88 | func testThatResponseStringReturnsSuccessResultWithValidString() { 89 | // Given 90 | let URLString = "https://httpbin.org/get" 91 | let expectation = expectationWithDescription("request should succeed") 92 | 93 | var response: Response? 94 | 95 | // When 96 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 97 | .responseString { closureResponse in 98 | response = closureResponse 99 | expectation.fulfill() 100 | } 101 | 102 | waitForExpectationsWithTimeout(timeout, handler: nil) 103 | 104 | // Then 105 | if let response = response { 106 | XCTAssertNotNil(response.request, "request should not be nil") 107 | XCTAssertNotNil(response.response, "response should not be nil") 108 | XCTAssertNotNil(response.data, "data should not be nil") 109 | XCTAssertTrue(response.result.isSuccess, "result should be success") 110 | } else { 111 | XCTFail("response should not be nil") 112 | } 113 | } 114 | 115 | func testThatResponseStringReturnsFailureResultWithOptionalDataAndError() { 116 | // Given 117 | let URLString = "https://invalid-url-here.org/this/does/not/exist" 118 | let expectation = expectationWithDescription("request should fail with 404") 119 | 120 | var response: Response? 121 | 122 | // When 123 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 124 | .responseString { closureResponse in 125 | response = closureResponse 126 | expectation.fulfill() 127 | } 128 | 129 | waitForExpectationsWithTimeout(timeout, handler: nil) 130 | 131 | // Then 132 | if let response = response { 133 | XCTAssertNotNil(response.request, "request should not be nil") 134 | XCTAssertNil(response.response, "response should be nil") 135 | XCTAssertNotNil(response.data, "data should not be nil") 136 | XCTAssertTrue(response.result.isFailure, "result should be failure") 137 | } else { 138 | XCTFail("response should not be nil") 139 | } 140 | } 141 | } 142 | 143 | // MARK: - 144 | 145 | class ResponseJSONTestCase: BaseTestCase { 146 | func testThatResponseJSONReturnsSuccessResultWithValidJSON() { 147 | // Given 148 | let URLString = "https://httpbin.org/get" 149 | let expectation = expectationWithDescription("request should succeed") 150 | 151 | var response: Response? 152 | 153 | // When 154 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 155 | .responseJSON { closureResponse in 156 | response = closureResponse 157 | expectation.fulfill() 158 | } 159 | 160 | waitForExpectationsWithTimeout(timeout, handler: nil) 161 | 162 | // Then 163 | if let response = response { 164 | XCTAssertNotNil(response.request, "request should not be nil") 165 | XCTAssertNotNil(response.response, "response should not be nil") 166 | XCTAssertNotNil(response.data, "data should not be nil") 167 | XCTAssertTrue(response.result.isSuccess, "result should be success") 168 | } else { 169 | XCTFail("response should not be nil") 170 | } 171 | } 172 | 173 | func testThatResponseStringReturnsFailureResultWithOptionalDataAndError() { 174 | // Given 175 | let URLString = "https://invalid-url-here.org/this/does/not/exist" 176 | let expectation = expectationWithDescription("request should fail with 404") 177 | 178 | var response: Response? 179 | 180 | // When 181 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 182 | .responseJSON { closureResponse in 183 | response = closureResponse 184 | expectation.fulfill() 185 | } 186 | 187 | waitForExpectationsWithTimeout(timeout, handler: nil) 188 | 189 | // Then 190 | if let response = response { 191 | XCTAssertNotNil(response.request, "request should not be nil") 192 | XCTAssertNil(response.response, "response should be nil") 193 | XCTAssertNotNil(response.data, "data should not be nil") 194 | XCTAssertTrue(response.result.isFailure, "result should be failure") 195 | } else { 196 | XCTFail("response should not be nil") 197 | } 198 | } 199 | 200 | func testThatResponseJSONReturnsSuccessResultForGETRequest() { 201 | // Given 202 | let URLString = "https://httpbin.org/get" 203 | let expectation = expectationWithDescription("request should succeed") 204 | 205 | var response: Response? 206 | 207 | // When 208 | Alamofire.request(.GET, URLString, parameters: ["foo": "bar"]) 209 | .responseJSON { closureResponse in 210 | response = closureResponse 211 | expectation.fulfill() 212 | } 213 | 214 | waitForExpectationsWithTimeout(timeout, handler: nil) 215 | 216 | // Then 217 | if let response = response { 218 | XCTAssertNotNil(response.request, "request should not be nil") 219 | XCTAssertNotNil(response.response, "response should not be nil") 220 | XCTAssertNotNil(response.data, "data should not be nil") 221 | XCTAssertTrue(response.result.isSuccess, "result should be success") 222 | 223 | // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info. 224 | // - https://openradar.appspot.com/radar?id=5517037090635776 225 | if let args = response.result.value?["args" as NSString] as? [String: String] { 226 | XCTAssertEqual(args, ["foo": "bar"], "args should match parameters") 227 | } else { 228 | XCTFail("args should not be nil") 229 | } 230 | } else { 231 | XCTFail("response should not be nil") 232 | } 233 | } 234 | 235 | func testThatResponseJSONReturnsSuccessResultForPOSTRequest() { 236 | // Given 237 | let URLString = "https://httpbin.org/post" 238 | let expectation = expectationWithDescription("request should succeed") 239 | 240 | var response: Response? 241 | 242 | // When 243 | Alamofire.request(.POST, URLString, parameters: ["foo": "bar"]) 244 | .responseJSON { closureResponse in 245 | response = closureResponse 246 | expectation.fulfill() 247 | } 248 | 249 | waitForExpectationsWithTimeout(timeout, handler: nil) 250 | 251 | // Then 252 | if let response = response { 253 | XCTAssertNotNil(response.request, "request should not be nil") 254 | XCTAssertNotNil(response.response, "response should not be nil") 255 | XCTAssertNotNil(response.data, "data should not be nil") 256 | XCTAssertTrue(response.result.isSuccess, "result should be success") 257 | 258 | // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info. 259 | // - https://openradar.appspot.com/radar?id=5517037090635776 260 | if let form = response.result.value?["form" as NSString] as? [String: String] { 261 | XCTAssertEqual(form, ["foo": "bar"], "form should match parameters") 262 | } else { 263 | XCTFail("form should not be nil") 264 | } 265 | } else { 266 | XCTFail("response should not be nil") 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /Tests/ManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManagerTests.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | @testable import Alamofire 26 | import Foundation 27 | import XCTest 28 | 29 | class ManagerTestCase: BaseTestCase { 30 | 31 | // MARK: Initialization Tests 32 | 33 | func testInitializerWithDefaultArguments() { 34 | // Given, When 35 | let manager = Manager() 36 | 37 | // Then 38 | XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil") 39 | XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate") 40 | XCTAssertNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should be nil") 41 | } 42 | 43 | func testInitializerWithSpecifiedArguments() { 44 | // Given 45 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 46 | let delegate = Manager.SessionDelegate() 47 | let serverTrustPolicyManager = ServerTrustPolicyManager(policies: [:]) 48 | 49 | // When 50 | let manager = Manager( 51 | configuration: configuration, 52 | delegate: delegate, 53 | serverTrustPolicyManager: serverTrustPolicyManager 54 | ) 55 | 56 | // Then 57 | XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil") 58 | XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate") 59 | XCTAssertNotNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should not be nil") 60 | } 61 | 62 | func testThatFailableInitializerSucceedsWithDefaultArguments() { 63 | // Given 64 | let delegate = Manager.SessionDelegate() 65 | let session: NSURLSession = { 66 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 67 | return NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) 68 | }() 69 | 70 | // When 71 | let manager = Manager(session: session, delegate: delegate) 72 | 73 | // Then 74 | if let manager = manager { 75 | XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate") 76 | XCTAssertNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should be nil") 77 | } else { 78 | XCTFail("manager should not be nil") 79 | } 80 | } 81 | 82 | func testThatFailableInitializerSucceedsWithSpecifiedArguments() { 83 | // Given 84 | let delegate = Manager.SessionDelegate() 85 | let session: NSURLSession = { 86 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 87 | return NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) 88 | }() 89 | 90 | let serverTrustPolicyManager = ServerTrustPolicyManager(policies: [:]) 91 | 92 | // When 93 | let manager = Manager(session: session, delegate: delegate, serverTrustPolicyManager: serverTrustPolicyManager) 94 | 95 | // Then 96 | if let manager = manager { 97 | XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate") 98 | XCTAssertNotNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should not be nil") 99 | } else { 100 | XCTFail("manager should not be nil") 101 | } 102 | } 103 | 104 | func testThatFailableInitializerFailsWithWhenDelegateDoesNotEqualSessionDelegate() { 105 | // Given 106 | let delegate = Manager.SessionDelegate() 107 | let session: NSURLSession = { 108 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 109 | return NSURLSession(configuration: configuration, delegate: Manager.SessionDelegate(), delegateQueue: nil) 110 | }() 111 | 112 | // When 113 | let manager = Manager(session: session, delegate: delegate) 114 | 115 | // Then 116 | XCTAssertNil(manager, "manager should be nil") 117 | } 118 | 119 | func testThatFailableInitializerFailsWhenSessionDelegateIsNil() { 120 | // Given 121 | let delegate = Manager.SessionDelegate() 122 | let session: NSURLSession = { 123 | let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 124 | return NSURLSession(configuration: configuration, delegate: nil, delegateQueue: nil) 125 | }() 126 | 127 | // When 128 | let manager = Manager(session: session, delegate: delegate) 129 | 130 | // Then 131 | XCTAssertNil(manager, "manager should be nil") 132 | } 133 | 134 | // MARK: Start Requests Immediately Tests 135 | 136 | func testSetStartRequestsImmediatelyToFalseAndResumeRequest() { 137 | // Given 138 | let manager = Alamofire.Manager() 139 | manager.startRequestsImmediately = false 140 | 141 | let URL = NSURL(string: "https://httpbin.org/get")! 142 | let URLRequest = NSURLRequest(URL: URL) 143 | 144 | let expectation = expectationWithDescription("\(URL)") 145 | 146 | var response: NSHTTPURLResponse? 147 | 148 | // When 149 | manager.request(URLRequest) 150 | .response { _, responseResponse, _, _ in 151 | response = responseResponse 152 | expectation.fulfill() 153 | } 154 | .resume() 155 | 156 | waitForExpectationsWithTimeout(timeout, handler: nil) 157 | 158 | // Then 159 | XCTAssertNotNil(response, "response should not be nil") 160 | XCTAssertTrue(response?.statusCode == 200, "response status code should be 200") 161 | } 162 | 163 | // MARK: Deinitialization Tests 164 | 165 | func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() { 166 | // Given 167 | var manager: Manager? = Alamofire.Manager() 168 | manager?.startRequestsImmediately = false 169 | 170 | let URL = NSURL(string: "https://httpbin.org/get")! 171 | let URLRequest = NSURLRequest(URL: URL) 172 | 173 | // When 174 | let request = manager?.request(URLRequest) 175 | manager = nil 176 | 177 | // Then 178 | XCTAssertTrue(request?.task.state == .Suspended, "request task state should be '.Suspended'") 179 | XCTAssertNil(manager, "manager should be nil") 180 | } 181 | 182 | func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() { 183 | // Given 184 | var manager: Manager? = Alamofire.Manager() 185 | manager!.startRequestsImmediately = false 186 | 187 | let URL = NSURL(string: "https://httpbin.org/get")! 188 | let URLRequest = NSURLRequest(URL: URL) 189 | 190 | // When 191 | let request = manager!.request(URLRequest) 192 | request.cancel() 193 | manager = nil 194 | 195 | // Then 196 | let state = request.task.state 197 | XCTAssertTrue(state == .Canceling || state == .Completed, "state should be .Canceling or .Completed") 198 | XCTAssertNil(manager, "manager should be nil") 199 | } 200 | } 201 | 202 | // MARK: - 203 | 204 | class ManagerConfigurationHeadersTestCase: BaseTestCase { 205 | enum ConfigurationType { 206 | case Default, Ephemeral, Background 207 | } 208 | 209 | func testThatDefaultConfigurationHeadersAreSentWithRequest() { 210 | // Given, When, Then 211 | executeAuthorizationHeaderTestForConfigurationType(.Default) 212 | } 213 | 214 | func testThatEphemeralConfigurationHeadersAreSentWithRequest() { 215 | // Given, When, Then 216 | executeAuthorizationHeaderTestForConfigurationType(.Ephemeral) 217 | } 218 | 219 | func testThatBackgroundConfigurationHeadersAreSentWithRequest() { 220 | // Given, When, Then 221 | executeAuthorizationHeaderTestForConfigurationType(.Background) 222 | } 223 | 224 | private func executeAuthorizationHeaderTestForConfigurationType(type: ConfigurationType) { 225 | // Given 226 | let manager: Manager = { 227 | let configuration: NSURLSessionConfiguration = { 228 | let configuration: NSURLSessionConfiguration 229 | 230 | switch type { 231 | case .Default: 232 | configuration = NSURLSessionConfiguration.defaultSessionConfiguration() 233 | case .Ephemeral: 234 | configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration() 235 | case .Background: 236 | let identifier = "com.alamofire.test.manager-configuration-tests" 237 | configuration = NSURLSessionConfiguration.backgroundSessionConfigurationForAllPlatformsWithIdentifier(identifier) 238 | } 239 | 240 | var headers = Alamofire.Manager.defaultHTTPHeaders 241 | headers["Authorization"] = "Bearer 123456" 242 | configuration.HTTPAdditionalHeaders = headers 243 | 244 | return configuration 245 | }() 246 | 247 | return Manager(configuration: configuration) 248 | }() 249 | 250 | let expectation = expectationWithDescription("request should complete successfully") 251 | 252 | var response: Response? 253 | 254 | // When 255 | manager.request(.GET, "https://httpbin.org/headers") 256 | .responseJSON { closureResponse in 257 | response = closureResponse 258 | expectation.fulfill() 259 | } 260 | 261 | waitForExpectationsWithTimeout(timeout, handler: nil) 262 | 263 | // Then 264 | if let response = response { 265 | XCTAssertNotNil(response.request, "request should not be nil") 266 | XCTAssertNotNil(response.response, "response should not be nil") 267 | XCTAssertNotNil(response.data, "data should not be nil") 268 | XCTAssertTrue(response.result.isSuccess, "result should be a success") 269 | 270 | if let 271 | headers = response.result.value?["headers" as NSString] as? [String: String], 272 | authorization = headers["Authorization"] 273 | { 274 | XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match") 275 | } else { 276 | XCTFail("failed to extract authorization header value") 277 | } 278 | } else { 279 | XCTFail("response should not be nil") 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Source/ParameterEncoding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterEncoding.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /** 28 | HTTP method definitions. 29 | 30 | See https://tools.ietf.org/html/rfc7231#section-4.3 31 | */ 32 | public enum Method: String { 33 | case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT 34 | } 35 | 36 | // MARK: ParameterEncoding 37 | 38 | /** 39 | Used to specify the way in which a set of parameters are applied to a URL request. 40 | 41 | - `URL`: Creates a query string to be set as or appended to any existing URL query for `GET`, `HEAD`, 42 | and `DELETE` requests, or set as the body for requests with any other HTTP method. The 43 | `Content-Type` HTTP header field of an encoded request with HTTP body is set to 44 | `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification 45 | for how to encode collection types, the convention of appending `[]` to the key for array 46 | values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested 47 | dictionary values (`foo[bar]=baz`). 48 | 49 | - `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same 50 | implementation as the `.URL` case, but always applies the encoded result to the URL. 51 | 52 | - `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is 53 | set as the body of the request. The `Content-Type` HTTP header field of an encoded request is 54 | set to `application/json`. 55 | 56 | - `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, 57 | according to the associated format and write options values, which is set as the body of the 58 | request. The `Content-Type` HTTP header field of an encoded request is set to 59 | `application/x-plist`. 60 | 61 | - `Custom`: Uses the associated closure value to construct a new request given an existing request and 62 | parameters. 63 | */ 64 | public enum ParameterEncoding { 65 | case URL 66 | case URLEncodedInURL 67 | case JSON 68 | case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions) 69 | case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) 70 | 71 | /** 72 | Creates a URL request by encoding parameters and applying them onto an existing request. 73 | 74 | - parameter URLRequest: The request to have parameters applied. 75 | - parameter parameters: The parameters to apply. 76 | 77 | - returns: A tuple containing the constructed request and the error that occurred during parameter encoding, 78 | if any. 79 | */ 80 | public func encode( 81 | URLRequest: URLRequestConvertible, 82 | parameters: [String: AnyObject]?) 83 | -> (NSMutableURLRequest, NSError?) 84 | { 85 | var mutableURLRequest = URLRequest.URLRequest 86 | 87 | guard let parameters = parameters else { return (mutableURLRequest, nil) } 88 | 89 | var encodingError: NSError? = nil 90 | 91 | switch self { 92 | case .URL, .URLEncodedInURL: 93 | func query(parameters: [String: AnyObject]) -> String { 94 | var components: [(String, String)] = [] 95 | 96 | for key in parameters.keys.sort(<) { 97 | let value = parameters[key]! 98 | components += queryComponents(key, value) 99 | } 100 | 101 | return (components.map { "\($0)=\($1)" } as [String]).joinWithSeparator("&") 102 | } 103 | 104 | func encodesParametersInURL(method: Method) -> Bool { 105 | switch self { 106 | case .URLEncodedInURL: 107 | return true 108 | default: 109 | break 110 | } 111 | 112 | switch method { 113 | case .GET, .HEAD, .DELETE: 114 | return true 115 | default: 116 | return false 117 | } 118 | } 119 | 120 | if let method = Method(rawValue: mutableURLRequest.HTTPMethod) where encodesParametersInURL(method) { 121 | if let 122 | URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) 123 | where !parameters.isEmpty 124 | { 125 | let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 126 | URLComponents.percentEncodedQuery = percentEncodedQuery 127 | mutableURLRequest.URL = URLComponents.URL 128 | } 129 | } else { 130 | if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 131 | mutableURLRequest.setValue( 132 | "application/x-www-form-urlencoded; charset=utf-8", 133 | forHTTPHeaderField: "Content-Type" 134 | ) 135 | } 136 | 137 | mutableURLRequest.HTTPBody = query(parameters).dataUsingEncoding( 138 | NSUTF8StringEncoding, 139 | allowLossyConversion: false 140 | ) 141 | } 142 | case .JSON: 143 | do { 144 | let options = NSJSONWritingOptions() 145 | let data = try NSJSONSerialization.dataWithJSONObject(parameters, options: options) 146 | 147 | if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 148 | mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 149 | } 150 | 151 | mutableURLRequest.HTTPBody = data 152 | } catch { 153 | encodingError = error as NSError 154 | } 155 | case .PropertyList(let format, let options): 156 | do { 157 | let data = try NSPropertyListSerialization.dataWithPropertyList( 158 | parameters, 159 | format: format, 160 | options: options 161 | ) 162 | 163 | if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 164 | mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 165 | } 166 | 167 | mutableURLRequest.HTTPBody = data 168 | } catch { 169 | encodingError = error as NSError 170 | } 171 | case .Custom(let closure): 172 | (mutableURLRequest, encodingError) = closure(mutableURLRequest, parameters) 173 | } 174 | 175 | return (mutableURLRequest, encodingError) 176 | } 177 | 178 | /** 179 | Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 180 | 181 | - parameter key: The key of the query component. 182 | - parameter value: The value of the query component. 183 | 184 | - returns: The percent-escaped, URL encoded query string components. 185 | */ 186 | public func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] { 187 | var components: [(String, String)] = [] 188 | 189 | if let dictionary = value as? [String: AnyObject] { 190 | for (nestedKey, value) in dictionary { 191 | components += queryComponents("\(key)[\(nestedKey)]", value) 192 | } 193 | } else if let array = value as? [AnyObject] { 194 | for value in array { 195 | components += queryComponents("\(key)[]", value) 196 | } 197 | } else { 198 | components.append((escape(key), escape("\(value)"))) 199 | } 200 | 201 | return components 202 | } 203 | 204 | /** 205 | Returns a percent-escaped string following RFC 3986 for a query string key or value. 206 | 207 | RFC 3986 states that the following characters are "reserved" characters. 208 | 209 | - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 210 | - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 211 | 212 | In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 213 | query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 214 | should be percent-escaped in the query string. 215 | 216 | - parameter string: The string to be percent-escaped. 217 | 218 | - returns: The percent-escaped string. 219 | */ 220 | public func escape(string: String) -> String { 221 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 222 | let subDelimitersToEncode = "!$&'()*+,;=" 223 | 224 | let allowedCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet 225 | allowedCharacterSet.removeCharactersInString(generalDelimitersToEncode + subDelimitersToEncode) 226 | 227 | var escaped = "" 228 | 229 | //========================================================================================================== 230 | // 231 | // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few 232 | // hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no 233 | // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more 234 | // info, please refer to: 235 | // 236 | // - https://github.com/Alamofire/Alamofire/issues/206 237 | // 238 | //========================================================================================================== 239 | 240 | if #available(iOS 8.3, OSX 10.10, *) { 241 | escaped = string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? string 242 | } else { 243 | let batchSize = 50 244 | var index = string.startIndex 245 | 246 | while index != string.endIndex { 247 | let startIndex = index 248 | let endIndex = index.advancedBy(batchSize, limit: string.endIndex) 249 | let range = startIndex..