├── 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..