├── .swift-version
├── Cartfile
├── Cartfile.resolved
├── SLNetwork.png
├── SolarNetworkExample
├── SolarNetworkExample
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Cer
│ │ ├── github.cer
│ │ └── github.p12
│ ├── HTTPBin
│ │ ├── HTTPBin.swift
│ │ ├── Target
│ │ │ └── HTTPBinTarget.swift
│ │ ├── Request
│ │ │ ├── HTTPBinDownLoadRequest.swift
│ │ │ ├── HTTPBinUploadRequest.swift
│ │ │ ├── HTTPBinGETRequest.swift
│ │ │ ├── HTTPBinPUTRequest.swift
│ │ │ ├── HTTPBinPATCHRequest.swift
│ │ │ ├── HTTPBinDELETERequest.swift
│ │ │ ├── HTTPBinStringParametersRequest.swift
│ │ │ ├── HTTPBinArrayParametersRequest.swift
│ │ │ └── HTTPBinPOSTRequest.swift
│ │ └── HTTPBinVC.swift
│ ├── GitHub
│ │ ├── GitHub.swift
│ │ ├── Request
│ │ │ ├── GitHubMyInfoRequest.swift
│ │ │ ├── GitHubUserInfoRequest.swift
│ │ │ ├── GitHubAPIRequest.swift
│ │ │ ├── GitHubSigninRequest.swift
│ │ │ ├── GitHubSignoutRequest.swift
│ │ │ └── GitHubDownloadRequest.swift
│ │ ├── Plugin
│ │ │ └── GitHubPlugin.swift
│ │ ├── Model
│ │ │ └── GitHubAuthenticationModel.swift
│ │ ├── Target
│ │ │ └── GitHubTarget.swift
│ │ └── GitHubVC.swift
│ ├── Info.plist
│ ├── AppDelegate.swift
│ └── Base.lproj
│ │ └── LaunchScreen.storyboard
├── Podfile
├── SolarNetworkExample.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── SolarNetworkExample.xcworkspace
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── contents.xcworkspacedata
└── Podfile.lock
├── SolarNetwork.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── SolarNetwork.xcscheme
└── project.pbxproj
├── SolarNetwork.podspec
├── SolarNetwork
├── SolarNetwork.h
└── Info.plist
├── Source
├── Extension
│ ├── String+SolarNetwork.swift
│ ├── FileManager+SolarNetwork.swift
│ ├── URLSessionTask+SolarNetwork.swift
│ └── SLNamespace.swift
├── Utility
│ ├── SLSessionDelegate.swift
│ ├── SLReflection.swift
│ ├── SLParameterValueEncoding.swift
│ └── SLResumeData.swift
├── SLPlugin.swift
├── SLNetwork+Alamofire.swift
├── SLProgress.swift
├── SLResponse.swift
├── SLTarget.swift
├── SLRequest.swift
└── SLNetwork.swift
├── Documentation
├── UpdateLog.md
├── Progress.md
├── Plugin.md
├── Response.md
├── Download.md
├── Upload.md
├── Request.md
└── Target.md
├── LICENSE
├── PodRelease.sh
├── .gitignore
├── ChineseREADME.md
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire"
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" "4.6.0"
2 |
--------------------------------------------------------------------------------
/SLNetwork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/HEAD/SLNetwork.png
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Cer/github.cer:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/HEAD/SolarNetworkExample/SolarNetworkExample/Cer/github.cer
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Cer/github.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/HEAD/SolarNetworkExample/SolarNetworkExample/Cer/github.p12
--------------------------------------------------------------------------------
/SolarNetwork.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SolarNetworkExample/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '10.0'
3 | use_frameworks!
4 |
5 | target 'SolarNetworkExample' do
6 |
7 | pod 'SolarNetwork', :path => '../'
8 | pod 'Alamofire', '~> 5.0.0-rc.2'
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SolarNetwork.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/HTTPBin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBin.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | let HTTPBinNetwork = SLNetwork(HTTPBinTarget())
13 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Target/HTTPBinTarget.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinTarget.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | struct HTTPBinTarget: SLTarget {
13 | var baseURLString: String = "https://httpbin.org"
14 | }
15 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/GitHub.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHub.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | let GitHubNetwork = SLNetwork(GitHubTarget())
13 |
14 | let GitHubAppClientID: String = "e02a05e02e13bc1d1e51"
15 | let GitHubAppClientSecret: String = "48c3229b79302d818fe7c4fd2310d58996297aca"
16 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubMyInfoRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubMyInfoRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubMyInfoRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | path = "/user"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubUserInfoRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubUserInfoRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubUserInfoRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | path = "/users/wyhazq"
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinDownLoadRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinDownLoadRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinDownLoadRequest: SLDownloadRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | path = "/image/png"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinUploadRequest.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // HTTPBinUploadRequest.swift
4 | // SolarKit-SwiftExample
5 | //
6 | // Created by wyhazq on 2018/1/30.
7 | // Copyright © 2018年 SolarKit. All rights reserved.
8 | //
9 |
10 | import Foundation
11 | import SolarNetwork
12 |
13 | class HTTPBinUploadRequest: SLUploadRequest {
14 |
15 | override func loadRequest() {
16 | super.loadRequest()
17 |
18 | path = "/post"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinGETRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinGETRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinGETRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | path = "/get"
18 | }
19 |
20 | let testGETProperty = "testGETProperty"
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SolarNetwork.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "SolarNetwork"
4 | s.version = "5.0.0"
5 | s.summary = "Elegant network abstraction layer in Swift."
6 | s.homepage = "https://github.com/ThreeGayHub/SolarNetwork"
7 | s.license = 'MIT'
8 | s.author = { "wyhazq" => "wyhazq@foxmail.com" }
9 | s.source = { :git => "https://github.com/ThreeGayHub/SolarNetwork.git", :tag => "#{s.version}" }
10 |
11 | s.ios.deployment_target = '10.0'
12 |
13 | s.source_files = 'Source/**/*.swift'
14 |
15 | s.dependency "Alamofire"
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinPUTRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinPUTRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinPUTRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .put
18 | path = "/put"
19 | }
20 |
21 | let testPUTProperty = "testPUTProperty"
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinPATCHRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinPATCHRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinPATCHRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .patch
18 | path = "/patch"
19 | }
20 |
21 | let testPATCHProperty = "testPATCHProperty"
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/SolarNetworkExample/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (5.0.5)
3 | - SolarNetwork (5.0.0):
4 | - Alamofire
5 |
6 | DEPENDENCIES:
7 | - Alamofire (~> 5.0.0-rc.2)
8 | - SolarNetwork (from `../`)
9 |
10 | SPEC REPOS:
11 | https://github.com/cocoapods/specs.git:
12 | - Alamofire
13 |
14 | EXTERNAL SOURCES:
15 | SolarNetwork:
16 | :path: "../"
17 |
18 | SPEC CHECKSUMS:
19 | Alamofire: df2f8f826963b08b9a870791ad48e07a10090b2e
20 | SolarNetwork: c78e9369106e6e46ca350ec7c174d99f09fc2fdc
21 |
22 | PODFILE CHECKSUM: 3216a036c044dde4e6bd1e1a58928e6f0e7e1efa
23 |
24 | COCOAPODS: 1.7.2
25 |
--------------------------------------------------------------------------------
/SolarNetwork/SolarNetwork.h:
--------------------------------------------------------------------------------
1 | //
2 | // SolarNetwork.h
3 | // SolarNetwork
4 | //
5 | // Created by wyh on 2018/2/5.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SolarNetwork.
12 | FOUNDATION_EXPORT double SolarNetworkVersionNumber;
13 |
14 | //! Project version string for SolarNetwork.
15 | FOUNDATION_EXPORT const unsigned char SolarNetworkVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinDELETERequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinDELETERequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinDELETERequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | self.method = .delete
18 | self.path = "/delete"
19 | }
20 |
21 | let testDELETEProperty = "testDELETEProperty"
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Source/Extension/String+SolarNetwork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+SolarNetwork.swift
3 | // Alamofire
4 | //
5 | // Created by 温一鸿 on 2019/9/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension String: SLNamespaceProtocol {}
11 |
12 | extension SLNamespace where Base == String {
13 |
14 | var isIP: Bool {
15 | if let char = base.first {
16 | let zero: Character = "0"
17 | let nine: Character = "9"
18 | if char >= zero && char <= nine {
19 | return true
20 | }
21 | }
22 |
23 | return false;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubAPIRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubAPIRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubAPIRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | parameterEncoding = URLEncoding.default
18 | }
19 |
20 | let client_id: String = GitHubAppClientID
21 | let client_secret: String = GitHubAppClientSecret
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Documentation/UpdateLog.md:
--------------------------------------------------------------------------------
1 | ## Release Log
2 |
3 | #### 1.1.0
4 |
5 | 1. SLTatget: change baseURLString to optional.
6 | 2. SLRequest: add userInfo, requestLog; change originalRequest to public.
7 | 3. SLResponse: add statusCode, header, originData, dataString.
8 |
9 | #### 1.2.0
10 |
11 | 1. SLReflection: change api:
12 | a. func toJSONObject() to var jsonObject
13 | b. func blackList() to var blackList
14 | 2. fix blackList not work issue
15 |
16 |
17 |
18 | #### 4.9.0
19 |
20 | adapt to Alamofire 4.9.0
21 |
22 | 1. Change Api: secPKCS12Name to secPKCS12Path
23 | 2. Change file structure
24 | 3. Fix Crash: resume download bug >= iOS12
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubSigninRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubSigninRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubSigninRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .put
18 | path = "/authorizations/clients/\(GitHubAppClientID)"
19 | }
20 |
21 | var client_secret = GitHubAppClientSecret
22 |
23 | var scopes = ["repo", "user"]
24 | }
25 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinStringParametersRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinStringParametersRequest.swift
3 | // SolarNetworkExample
4 | //
5 | // Created by wyhazq on 2018/9/4.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinStringParametersRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .post
18 | path = "/post"
19 | parameterEncoding = SLParameterValueJSONEncoding.default
20 |
21 | }
22 |
23 | let anyKey = "anyString"
24 | }
25 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinArrayParametersRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinArrayParametersRequest.swift
3 | // SolarNetworkExample
4 | //
5 | // Created by wyhazq on 2018/9/4.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinArrayParametersRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .post
18 | path = "/post"
19 | parameterEncoding = SLParameterValueJSONEncoding.default
20 |
21 | }
22 |
23 | let anyKey: [Any] = ["anyObj0", 1]
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Documentation/Progress.md:
--------------------------------------------------------------------------------
1 | ## Progress
2 |
3 | Return progress when download or upload.
4 |
5 | #### Download Progress
6 |
7 | ```swift
8 | HTTPBinNetwork.download(HTTPBinDownLoadRequest(), progressClosure: { (progress) in
9 | debugPrint(progress.currentProgress) //0~1
10 | debugPrint(progress.currentProgressString) //0%~100%
11 | }) { (resposne) in
12 |
13 | }
14 | ```
15 |
16 | #### Upload Progress
17 |
18 | ```swift
19 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
20 | debugPrint(progress.currentProgress) //0~1
21 | debugPrint(progress.currentProgressString) //0%~100%
22 | }) { (response) in
23 |
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Plugin/GitHubPlugin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubPlugin.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubPlugin: SLPlugin {
13 |
14 | func willSend(request: SLRequest) {
15 | debugPrint("willSend request: \(request.URLString)")
16 |
17 | //Do whatever you want before request.
18 | }
19 |
20 | func didReceive(response: SLResponse) {
21 | debugPrint("didReceive response: \(response.request?.URLString ?? "")")
22 |
23 | //Do whatever you want after response.
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubSignoutRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubSignoutRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubSignoutRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .delete
18 | path = "/applications" + "/\(GitHubAppClientID)" + "/tokens/"
19 |
20 | let authorizationHeader = HTTPHeader.authorization(username: GitHubAppClientID, password: GitHubAppClientSecret)
21 | headers = HTTPHeaders(arrayLiteral: authorizationHeader)
22 |
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SolarNetwork/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/Request/HTTPBinPOSTRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinPOSTRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class HTTPBinPOSTRequest: SLRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | method = .post
18 | path = "/post"
19 | // parameterEncoding = JSONEncoding.default
20 |
21 | headers = ["test": "testValue"]
22 | }
23 |
24 | let testPOSTProperty = "testPOSTProperty"
25 |
26 | var name: String?
27 |
28 | let testBlackListProperty = "testBlackListProperty"
29 |
30 | override var blackList: [String] {
31 | return ["testBlackListProperty"]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Documentation/Plugin.md:
--------------------------------------------------------------------------------
1 | ## Plugin
2 |
3 | Modify SLRequest in `willSend` and modify SLResponse in `didReceive`.
4 |
5 | #### Modify SLRequest
6 |
7 | ```swift
8 | func willSend(request: SLRequest) {
9 | //Do whatever you want before request.
10 | //Such as Log,Modify SLRequest,Encryption,Signature.
11 | }
12 | ```
13 |
14 | #### Modify SLResponse
15 |
16 | ```swift
17 | func didReceive(response: SLResponse) {
18 | //Do whatever you want after response.
19 | //Such as Log,Modify SLResponse,Decryption.
20 | }
21 |
22 | ```
23 |
24 | #### Usage
25 |
26 | ```swift
27 | import SolarNetwork
28 |
29 | class GitHubPlugin: SLPlugin {
30 |
31 | func willSend(request: SLRequest) {}
32 |
33 | func didReceive(response: SLResponse) {}
34 | }
35 |
36 | struct GitHubTarget: SLTarget {
37 |
38 | var plugins: [SLPlugin]? {
39 |
40 | return [GitHubPlugin(), OtherPligin()]
41 | }
42 | }
43 | ```
44 |
45 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Request/GitHubDownloadRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubDownloadRequest.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/31.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | class GitHubDownloadRequest: SLDownloadRequest {
13 |
14 | override func loadRequest() {
15 | super.loadRequest()
16 |
17 | URLString = "https://video.cdnvue.com/uploads/1746405174696532785/video/asBF81t"
18 | }
19 |
20 | // "https://video.cdnvue.com/uploads/1746405174696532785/video/asBF81t"
21 | // "https://video.cdnvue.com/uploads/812734077666566393/video/ar9RkFL"
22 | // "http://cdnvue.com/video/rzGHzRA19L/64tBZo"
23 | // "https://video.cdnvue.com/uploads/-3518274263869595162/video/asBqjiJ"
24 | // "https://video.cdnvue.com/uploads/6921208716048356378/video/asCjvhf"
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 ThreeGayHub Organization (https://github.com/ThreeGayHub)
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 |
--------------------------------------------------------------------------------
/PodRelease.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #pod trunk register email@email.com 'username' --verbose
3 |
4 | podspecName=`find *.podspec -maxdepth 0`
5 |
6 | read -p "is vertify native pod?(1: yes,anyotherkey: no): " isNativeVertify
7 |
8 | #pod repo update master
9 |
10 | if [ ${isNativeVertify} = 1 ]; then
11 | echo -e "\n"
12 |
13 | pod lib lint ${podspecName} --verbose --allow-warnings --use-libraries --no-clean
14 | fi
15 | echo -e "\n"
16 |
17 | read -p "create git tag?(1: yes,anyotherkey: no): " isTag
18 | if [ ${isTag} = 1 ]; then
19 | releaseTagVersion=$(grep -E "s.version.+=" ${podspecName} | awk '{print $3}')
20 | releaseTagVersionCount=${#releaseTagVersion}
21 | releaseTagVersion=${releaseTagVersion:1:${releaseTagVersionCount}-2}
22 | echo -e "\n"
23 | echo "create git tag:${releaseTagVersion}"
24 |
25 | if [ $(git tag -l "${releaseTagVersion}") ]; then
26 | echo "Tag:${releaseTagVersion} Already exists! Please modify the tag version."
27 |
28 | exit
29 |
30 | else
31 |
32 | git tag "${releaseTagVersion}"
33 | git push --tags
34 |
35 | fi
36 | fi
37 | echo -e "\n"
38 |
39 | read -p "pod release?(1: yes,anyotherkey: no): " isRelease
40 | if [ ${isRelease} = 1 ]; then
41 | echo -e "\n"
42 |
43 | pod trunk push ${podspecName} --verbose --allow-warnings
44 | carthage build --no-skip-current
45 |
46 | fi
47 | echo -e "\n"
48 |
--------------------------------------------------------------------------------
/Source/Extension/FileManager+SolarNetwork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileManager+Extension.swift
3 | // SolarNetwork
4 | //
5 | // Created by wyhazq on 2019/9/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension SLNamespace where Base == FileManager {
11 |
12 | static func createDirectory(at URL: URL, withIntermediateDirectories createIntermediates: Bool, attributes: [FileAttributeKey : Any]? = nil) {
13 | if !fileExists(at: URL) {
14 | do {
15 | try FileManager.default.createDirectory(at: URL, withIntermediateDirectories: createIntermediates, attributes: attributes)
16 | }
17 | catch {
18 | debugPrint("FileManager.createDirectoryError:\(error)")
19 | }
20 | }
21 | }
22 |
23 | static func removeItem(at URL: URL) {
24 | if fileExists(at: URL) {
25 | do {
26 | try FileManager.default.removeItem(at: URL)
27 | }
28 | catch {
29 | debugPrint("FileManager.removeItemError:\(error)")
30 | }
31 | }
32 | }
33 |
34 | static func fileExists(at URL: URL) -> Bool {
35 | let path = URL.absoluteString.replacingOccurrences(of: "file://", with: "")
36 | return FileManager.default.fileExists(atPath: path)
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/Source/Utility/SLSessionDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLSessionDelegate.swift
3 | // Alamofire
4 | //
5 | // Created by 温一鸿 on 2019/9/26.
6 | //
7 |
8 | import Foundation
9 |
10 | class SLSessionDelegate: SessionDelegate {
11 |
12 | open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
13 |
14 | override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
15 | if let taskDidReceiveChallenge = taskDidReceiveChallenge {
16 | //ServerTrust(HTTPS IP direct) && ClientCertificate
17 | if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust && challenge.protectionSpace.host.sl.isIP) || challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
18 | let result = taskDidReceiveChallenge(session, task, challenge)
19 | completionHandler(result.0, result.1)
20 | return
21 | }
22 | }
23 |
24 | super.urlSession(session, task: task, didReceive: challenge, completionHandler: completionHandler)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Model/GitHubAuthenticationModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubAuthenticationModel.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct GitHubAuthenticationModel: Decodable {
12 |
13 | var id: Int
14 | var url: String
15 | var app: [String: String]
16 |
17 | var token: String
18 | var hashed_token: String
19 | var token_last_eight: String
20 |
21 | var note: String?
22 | var note_url: String?
23 |
24 | var created_at: String
25 | var updated_at: String
26 |
27 | var scopes: [String]
28 |
29 | var fingerprint: String?
30 |
31 | }
32 |
33 | /**
34 | {
35 | "id": 161486498,
36 | "url": "https://api.github.com/authorizations/161486498",
37 | "app": {
38 | "name": "GayHub",
39 | "url": "https://github.com/ThreeGayHub/SolarKit/GayHub",
40 | "client_id": "e02a05e02e13bc1d1e51"
41 | },
42 | "token": "",
43 | "hashed_token": "",
44 | "token_last_eight": "",
45 | "note": null,
46 | "note_url": null,
47 | "created_at": "2018-01-30T02:30:05Z",
48 | "updated_at": "2018-01-30T02:30:05Z",
49 | "scopes": [
50 | "repo",
51 | "user"
52 | ],
53 | "fingerprint": null
54 | }
55 | */
56 |
--------------------------------------------------------------------------------
/Documentation/Response.md:
--------------------------------------------------------------------------------
1 | ## Response
2 |
3 | Response of a request which you can decode to JsonObject or Model.
4 |
5 | #### Decode to Dictionary
6 |
7 | ```swift
8 | HTTPBinNetwork.request(GetDictionaryRequest()) { (response) in
9 | if let dictionary = response.dataDictionary {
10 |
11 | }
12 | else if let error = response.error {
13 | //show error
14 | }
15 | }
16 | ```
17 |
18 | #### Decode to Array
19 |
20 | ```swift
21 | HTTPBinNetwork.request(GetArrayRequest()) { (response) in
22 | if let array = response.dataArray {
23 |
24 | }
25 | else if let error = response.error {
26 | //show error
27 | }
28 | }
29 | ```
30 |
31 | #### Decode to String
32 |
33 | ```swift
34 | HTTPBinNetwork.request(GetStringRequest()) { (response) in
35 | if let string = response.dataString {
36 |
37 | }
38 | else if let error = response.error {
39 | //show error
40 | }
41 | }
42 | ```
43 |
44 | #### Decode to Model
45 |
46 | ```swift
47 | struct User: Decodable { //Swift 4 Codable
48 | var id: Int
49 | var name: String
50 | var token: String
51 | }
52 |
53 | HTTPBinNetwork.request(UserRequest()) { (response) in
54 | if let user = response.decode(to: User.self) {
55 |
56 | }
57 | else if let error = response.error {
58 | //show error
59 | }
60 | }
61 | ```
62 |
63 |
--------------------------------------------------------------------------------
/Source/Extension/URLSessionTask+SolarNetwork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLSessionTask+SolarNetwork.swift
3 | // Alamofire
4 | //
5 | // Created by 温一鸿 on 2019/9/24.
6 | //
7 |
8 | import Foundation
9 |
10 | private let NSURLSessionResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
11 | private let NSURLSessionResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
12 |
13 |
14 | extension SLNamespace where Base == URLSessionTask {
15 |
16 | /// fix 10.0 - 10.1 resumeData bug: https://stackoverflow.com/questions/39346231/resume-nsurlsession-on-ios10/39347461#39347461
17 | ///
18 | /// - Parameter data:
19 | func fixiOS10Task(with data: Data) {
20 | guard #available(iOS 10.2, *) else {
21 | guard let resumeDictionary = SLResumeData.dictionary(of: data) else { return }
22 | if base.originalRequest == nil, let originalReqData = resumeDictionary[NSURLSessionResumeOriginalRequest] as? Data, let originalRequest = NSKeyedUnarchiver.unarchiveObject(with: originalReqData) as? NSURLRequest {
23 | base.setValue(originalRequest, forKey: "originalRequest")
24 | }
25 | if base.currentRequest == nil, let currentReqData = resumeDictionary[NSURLSessionResumeCurrentRequest] as? Data, let currentRequest = NSKeyedUnarchiver.unarchiveObject(with: currentReqData) as? NSURLRequest {
26 | base.setValue(currentRequest, forKey: "currentRequest")
27 | }
28 | return
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Documentation/Download.md:
--------------------------------------------------------------------------------
1 | ## Download
2 |
3 | Configuration of a DownloadRequest or ResumeDownloadRequest.
4 |
5 | #### Override the SLDownloadRequest
6 |
7 | ```swift
8 | import SolarNetwork
9 |
10 | class GitHubDownloadRequest: SLDownloadRequest {
11 |
12 | override func loadRequest() {
13 | super.loadRequest()
14 |
15 | self.URLString = "http://cdnvue.com/video/rzGHzRA19L/64tBZo"
16 | }
17 | }
18 | ```
19 |
20 | #### Usage
21 |
22 | ```swift
23 | let downloadRequest = GitHubDownloadRequest()
24 | GitHubNetwork.download(downloadRequest, progressClosure: { (progress) in
25 |
26 | }) { (response) in
27 |
28 | }
29 |
30 | //if you want to use resume download
31 | downloadRequest.isResume = true
32 |
33 | //if you want to specify the destination URL to receive the file. The default is "/Library/Caches/SLNetwork/Destination/(requestID)"
34 | downloadRequest.destinationURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
35 | ```
36 |
37 | #### Why can resume download work.
38 |
39 | 1.Every time begin a resume download request, SolarNetwork cancel the request and save the `Alamofire.Response.resumeData` to cache path when data is first received. And then send the request again. So it can resume download when the app restart.
40 |
41 | 2.It also have other logic to fix the `Alamofire.Response.resumeData` when app crash or user kill the process.
42 |
43 | 3.After the download is complete, I will check to see if the download file is complete.
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
69 |
--------------------------------------------------------------------------------
/Source/SLPlugin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLPlugin.swift
3 | //
4 | // Created by wyhazq on 2018/1/11.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | public protocol SLPlugin {
27 |
28 |
29 | /// Modify the SLRequest before sending
30 | ///
31 | /// - Parameter request: SLRequest
32 | func willSend(request: SLRequest)
33 |
34 |
35 | /// Modify the SLResponse after response
36 | ///
37 | /// - Parameter response: SLResponse
38 | func didReceive(response: SLResponse)
39 |
40 | }
41 |
42 | public extension SLPlugin {
43 |
44 | func willSend(request: SLRequest) {}
45 |
46 | func didReceive(response: SLResponse) {}
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | SolarNetwork
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UISupportedInterfaceOrientations~ipad
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationPortraitUpsideDown
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Source/Extension/SLNamespace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLNamespace.swift
3 | // SolarNetwork
4 | //
5 | // Created by wyhazq on 2019/1/25.
6 | // Copyright © 2019年 SolarNetwork. All rights reserved.
7 | //
8 |
9 | /**
10 | Use `Reactive` proxy as customization point for constrained protocol extensions.
11 | General pattern would be:
12 | // 1. Extend Reactive protocol with constrain on Base
13 | // Read as: Reactive Extension where Base is a SomeType
14 | extension Reactive where Base: SomeType {
15 | // 2. Put any specific reactive extension for SomeType here
16 | }
17 | With this approach we can have more specialized methods and properties using
18 | `Base` and not just specialized on common base type.
19 | */
20 |
21 | public struct SLNamespace {
22 | /// Base object to extend.
23 | public let base: Base
24 |
25 | /// Creates extensions with base object.
26 | ///
27 | /// - parameter base: Base object.
28 | public init(_ base: Base) {
29 | self.base = base
30 | }
31 | }
32 |
33 | /// A type that has reactive extensions.
34 | public protocol SLNamespaceProtocol {
35 | /// Extended type
36 | associatedtype SLCompatibleType
37 |
38 | /// Reactive extensions.
39 | static var sl: SLNamespace.Type { get set }
40 |
41 | /// Reactive extensions.
42 | var sl: SLNamespace { get set }
43 | }
44 |
45 | extension SLNamespaceProtocol {
46 | /// Reactive extensions.
47 | public static var sl: SLNamespace.Type {
48 | get {
49 | return SLNamespace.self
50 | }
51 | set {
52 | // this enables using Reactive to "mutate" base type
53 | }
54 | }
55 |
56 | /// Reactive extensions.
57 | public var sl: SLNamespace {
58 | get {
59 | return SLNamespace(self)
60 | }
61 | set {
62 | // this enables using Reactive to "mutate" base object
63 | }
64 | }
65 | }
66 |
67 | import class Foundation.NSObject
68 |
69 | extension NSObject: SLNamespaceProtocol { }
70 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Source/SLNetwork+Alamofire.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLNetwork+Alamofire.swift
3 | // SolarNetwork
4 | //
5 | // Created by wyhazq on 2018/2/6.
6 | //
7 |
8 | import Foundation
9 | import Alamofire
10 |
11 | public typealias Session = Alamofire.Session
12 | public typealias URLRequestConvertible = Alamofire.URLRequestConvertible
13 |
14 | public typealias HTTPHeader = Alamofire.HTTPHeader
15 | public typealias HTTPHeaders = Alamofire.HTTPHeaders
16 | public typealias Request = Alamofire.Request
17 | public typealias DownloadRequest = Alamofire.DownloadRequest
18 | public typealias UploadRequest = Alamofire.UploadRequest
19 | public typealias DataRequest = Alamofire.DataRequest
20 | public typealias DataResponse = Alamofire.DataResponse
21 | public typealias SessionDelegate = Alamofire.SessionDelegate
22 | public typealias Destination = DownloadRequest.Destination
23 | public typealias AFError = Alamofire.AFError
24 | public typealias RequestInterceptor = Alamofire.RequestInterceptor
25 |
26 | public typealias HTTPMethod = Alamofire.HTTPMethod
27 | public typealias Parameters = Alamofire.Parameters
28 |
29 | public typealias ParameterEncoding = Alamofire.ParameterEncoding
30 | public typealias JSONEncoding = Alamofire.JSONEncoding
31 | public typealias URLEncoding = Alamofire.URLEncoding
32 |
33 | public typealias Options = Alamofire.DownloadRequest.Options
34 | public typealias MultipartFormData = Alamofire.MultipartFormData
35 |
36 | public typealias ServerTrustManager = Alamofire.ServerTrustManager
37 | public typealias ServerTrustEvaluating = Alamofire.ServerTrustEvaluating
38 | public typealias DefaultTrustEvaluator = Alamofire.DefaultTrustEvaluator
39 | public typealias RevocationTrustEvaluator = Alamofire.RevocationTrustEvaluator
40 | public typealias PinnedCertificatesTrustEvaluator = Alamofire.PinnedCertificatesTrustEvaluator
41 | public typealias PublicKeysTrustEvaluator = Alamofire.PublicKeysTrustEvaluator
42 | public typealias CompositeTrustEvaluator = Alamofire.CompositeTrustEvaluator
43 | public typealias DisabledEvaluator = Alamofire.DisabledEvaluator
44 |
45 | public typealias NetworkReachabilityManager = Alamofire.NetworkReachabilityManager
46 | public typealias ReachabilityListener = Alamofire.NetworkReachabilityManager.Listener
47 |
48 | public let SLHostKey = "Host"
49 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/Target/GitHubTarget.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubTarget.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/12.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SolarNetwork
11 |
12 | struct GitHubTarget: SLTarget {
13 |
14 | var baseURLString: String = "https://api.github.com"
15 |
16 | var IPURLString: String? {
17 | get {
18 | return storeIPURLString
19 | }
20 | set {
21 | storeIPURLString = newValue
22 | }
23 | }
24 |
25 | var parameterEncoding: ParameterEncoding = JSONEncoding.default
26 |
27 | var allHostsMustBeEvaluated: Bool = false
28 |
29 | var serverEvaluators: [String : ServerTrustEvaluating]? {
30 | #if DEBUG
31 | let validateHost = false
32 | #else
33 | let validateHost = true
34 | #endif
35 |
36 | let evaluators: [String: ServerTrustEvaluating] = [
37 | host: PinnedCertificatesTrustEvaluator(validateHost: validateHost)
38 | ]
39 |
40 | return evaluators
41 | }
42 |
43 | var clientTrustPolicy: (secPKCS12Path: String, password: String)? = (secPKCS12Path: Bundle.main.path(forResource: "github", ofType: "p12") ?? "", password: "123456")
44 |
45 | var plugins: [SLPlugin]? = [GitHubPlugin()]
46 |
47 | var reachabilityListener: ReachabilityListener? {
48 | return { (status) in
49 | switch status {
50 |
51 | case .unknown:
52 | debugPrint("unknown")
53 |
54 | case .notReachable:
55 | debugPrint("notReachable")
56 |
57 | case .reachable(let connectionType):
58 | switch connectionType {
59 |
60 | case .ethernetOrWiFi:
61 | debugPrint("ethernetOrWiFi")
62 |
63 | case .cellular:
64 | debugPrint("cellular")
65 |
66 | }
67 | }
68 | }
69 | }
70 |
71 | var storeIPURLString: String?
72 |
73 | }
74 |
75 | //"api.github.com"
76 | //"https://192.30.255.117"
77 |
78 |
--------------------------------------------------------------------------------
/Documentation/Upload.md:
--------------------------------------------------------------------------------
1 | ## Upload
2 |
3 | Configuration of a UploadRequest(Data - File - InputStream - FormData).
4 |
5 | #### Override the SLUploadRequest
6 |
7 | ```swift
8 | import SolarNetwork
9 |
10 | class HTTPBinUploadRequest: SLUploadRequest {
11 |
12 | override func loadRequest() {
13 | super.loadRequest()
14 |
15 | self.path = "/post"
16 | }
17 |
18 | }
19 | ```
20 |
21 | #### Upload Data
22 |
23 | ```swift
24 | let bundle = Bundle.main
25 | let resourcePath = bundle.path(forResource: "SLNetwork", ofType: "png")
26 | do {
27 | if let path = resourcePath {
28 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
29 | let uploadRequest = HTTPBinUploadRequest()
30 | uploadRequest.data = data
31 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
32 |
33 | }) { (response) in
34 |
35 | }
36 | }
37 | }
38 | catch {
39 | debugPrint(error)
40 | }
41 | ```
42 |
43 | #### Upload File
44 |
45 | ```swift
46 | if let path = resourcePath {
47 | let uploadRequest = HTTPBinUploadRequest()
48 | uploadRequest.filePath = path
49 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
50 |
51 | }) { (response) in
52 |
53 | }
54 | }
55 | ```
56 |
57 | #### Upload InputStream
58 |
59 | ```swift
60 | do {
61 | if let path = resourcePath {
62 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
63 | let inputStream = InputStream(data: data)
64 |
65 | let uploadRequest = HTTPBinUploadRequest()
66 | uploadRequest.inputStream = (inputStream, data.count)
67 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
68 |
69 | }) { (response) in
70 |
71 | }
72 | }
73 | }
74 | catch {
75 | debugPrint(error)
76 | }
77 | ```
78 |
79 | #### Upload FormData
80 |
81 | ```swift
82 | if let path = resourcePath {
83 | let uploadRequest = HTTPBinUploadRequest()
84 | uploadRequest.multipartFormDataClosure { (multipartFormData) in
85 | let url = URL(fileURLWithPath: path)
86 | multipartFormData.append(url, withName: "SLNetwork")
87 | }
88 |
89 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
90 |
91 | }) { (response) in
92 |
93 | }
94 | }
95 | ```
96 |
97 |
--------------------------------------------------------------------------------
/Source/SLProgress.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLProgress.swift
3 | //
4 | // Created by wyhazq on 2018/1/23.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | public class SLProgress {
29 |
30 | public weak var request: SLRequest?
31 |
32 | public var originalProgress: Progress?
33 |
34 |
35 | /// The Request's progress: 0-1
36 | public var currentProgress: Double {
37 | return originalProgress?.fractionCompleted ?? 0
38 | }
39 |
40 | /// The Request's progress: 0% - 100%
41 | public var currentProgressString: String {
42 | if let fractionCompleted = originalProgress?.fractionCompleted {
43 | return String(format: "%.0lf%%", fractionCompleted * 100)
44 | }
45 | return ""
46 | }
47 |
48 | init(request: SLRequest) {
49 | self.request = request
50 | }
51 |
52 | }
53 |
54 | extension SLProgress: CustomDebugStringConvertible {
55 |
56 | public var debugDescription: String {
57 | return """
58 |
59 | ------------------------ SLProgress ----------------------
60 | URL:\(request?.URLString ?? "")
61 | Progress:\(currentProgressString)
62 | ----------------------------------------------------------
63 |
64 | """
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/3.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | //1.AF 5.0
10 | //TODO-2.AFResponse接口
11 | //TODO-3.SLResponse序列化(加解密等逻辑)
12 | //TODO-4.序列化完毕后再调用Plugin
13 | //TODO-5.Decoder to [Model]
14 | //TODO-6.缓存路径公开
15 | //TODO-7.multipartFormData支持parameters
16 |
17 | import UIKit
18 |
19 | @UIApplicationMain
20 | class AppDelegate: UIResponder, UIApplicationDelegate {
21 |
22 | var window: UIWindow?
23 |
24 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
25 |
26 | debugPrint("Welcome to SolarNetwork!")
27 | // Override point for customization after application launch.
28 | return true
29 | }
30 |
31 | func applicationWillResignActive(_ application: UIApplication) {
32 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
33 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
34 | }
35 |
36 | func applicationDidEnterBackground(_ application: UIApplication) {
37 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
39 | }
40 |
41 | func applicationWillEnterForeground(_ application: UIApplication) {
42 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
43 | }
44 |
45 | func applicationDidBecomeActive(_ application: UIApplication) {
46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
47 | }
48 |
49 | func applicationWillTerminate(_ application: UIApplication) {
50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
51 | }
52 |
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/SolarNetwork.xcodeproj/xcshareddata/xcschemes/SolarNetwork.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Source/Utility/SLReflection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLReflection.swift
3 | //
4 | // Created by wyhazq on 2018/1/8.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | public protocol SLReflection {
29 |
30 | /// model to jsonObject
31 | var jsonObject: Any? { get }
32 |
33 |
34 | /// properties of model which don't encode to jsonObject
35 | var blackList: [String] { get }
36 | }
37 |
38 | extension SLReflection {
39 |
40 | public var blackList: [String] { return [] }
41 |
42 | }
43 |
44 | extension SLReflection {
45 | public var jsonObject: Any? {
46 | let mirror = Mirror(reflecting: self)
47 | if mirror.children.count > 0, let _ = mirror.displayStyle {
48 | var dict: [String: Any] = [:]
49 | for (optionalKey, value) in mirror.children {
50 | if let propertyNameString = optionalKey, let reflectionValue = value as? SLReflection {
51 | if blackList.contains(propertyNameString) {
52 | continue
53 | }
54 | dict[propertyNameString] = reflectionValue.jsonObject
55 | }
56 | }
57 | return dict.count > 0 ? dict : nil
58 | }
59 | return self
60 | }
61 | }
62 |
63 | extension Optional: SLReflection {
64 | public var jsonObject: Any? {
65 | if let x = self {
66 | if let value = x as? SLReflection {
67 | return value.jsonObject
68 | }
69 | }
70 | return nil
71 | }
72 | }
73 |
74 | extension Array: SLReflection {
75 | public var jsonObject: Any? {
76 | let mirror = Mirror(reflecting: self)
77 | if mirror.children.count > 0 {
78 | var array: [Any] = []
79 | for (_, value) in mirror.children {
80 | if let reflectionValue = value as? SLReflection, let obj = reflectionValue.jsonObject {
81 | array.append(obj)
82 | }
83 | }
84 | return array.count > 0 ? array : nil
85 | }
86 | return self
87 | }
88 | }
89 |
90 | extension Dictionary: SLReflection {
91 | public var jsonObject: Any? {
92 | if self.count > 0 {
93 | var dict: [String: Any] = [:]
94 | for (key, obj) in self {
95 | if let keyString = key as? String, let reflectionValue = obj as? SLReflection {
96 | dict[keyString] = reflectionValue.jsonObject
97 | }
98 | }
99 | return dict.count > 0 ? dict : nil
100 | }
101 | return self
102 | }
103 | }
104 |
105 | extension Bool: SLReflection {}
106 |
107 | extension Int: SLReflection {}
108 | extension Int8: SLReflection {}
109 | extension Int16: SLReflection {}
110 | extension Int32: SLReflection {}
111 | extension Int64: SLReflection {}
112 |
113 | extension Float: SLReflection {}
114 | //extension Float80: SLReflection {}
115 | extension Double: SLReflection {}
116 | extension Decimal: SLReflection {}
117 |
118 | extension String: SLReflection {}
119 |
--------------------------------------------------------------------------------
/Source/SLResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLResponse.swift
3 | //
4 | // Created by wyhazq on 2018/1/11.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | public class SLResponse {
29 |
30 | public weak var request: SLRequest?
31 |
32 | public let urlRequest: URLRequest?
33 |
34 | public let httpURLResponse: HTTPURLResponse?
35 |
36 | public var originData: Data?
37 |
38 | public var data: Any?
39 |
40 | public var error: NSError?
41 |
42 | public var message: String?
43 |
44 | public var fileURL: URL?
45 |
46 | init(request: SLRequest, urlRequest: URLRequest?, httpURLResponse: HTTPURLResponse?) {
47 | self.request = request
48 | self.urlRequest = urlRequest
49 | self.httpURLResponse = httpURLResponse
50 | }
51 |
52 | }
53 |
54 | extension SLResponse {
55 |
56 | public var statusCode: Int {
57 | return httpURLResponse?.statusCode ?? Int.min
58 | }
59 |
60 | public var header: [AnyHashable : Any] {
61 | return httpURLResponse?.allHeaderFields ?? [:]
62 | }
63 |
64 | }
65 |
66 | extension SLResponse {
67 |
68 | public var dataDictionary: [String: Any]? {
69 | if let dataDictionary = data as? [String: Any] {
70 | return dataDictionary
71 | }
72 | return nil
73 | }
74 |
75 | public var dataArray: [[String: Any]]? {
76 | if let dataArray = data as? [[String: Any]] {
77 | return dataArray
78 | }
79 | return nil
80 | }
81 |
82 | public var dataString: String? {
83 | if let data = originData, let dataString = String(data: data, encoding: .utf8) {
84 | return dataString
85 | }
86 | return nil
87 | }
88 |
89 |
90 | /// JsonObject to Model
91 | ///
92 | /// - Parameter Model: Model: Decodable
93 | /// - Returns: Model
94 | public func decode(to Model: T.Type) -> T? {
95 | var decodeData: Data = Data()
96 | do {
97 | if let data = self.data as? Data {
98 | decodeData = data;
99 | }
100 | else {
101 | if let data = self.data {
102 | decodeData = try JSONSerialization.data(withJSONObject: data)
103 | }
104 | }
105 | if let target = self.request?.target {
106 | let data: T = try target.decoder.decode(Model.self, from: decodeData)
107 | return data
108 | }
109 | } catch {
110 | self.error = error as NSError
111 | debugPrint(error)
112 | }
113 | return nil
114 | }
115 |
116 | }
117 |
118 | extension SLResponse: CustomDebugStringConvertible {
119 |
120 | public var debugDescription: String {
121 |
122 | var dataString: String? = "nil"
123 |
124 | if let url = fileURL {
125 | dataString = url.absoluteString
126 | }
127 | else {
128 | dataString = (originData == nil) ? "nil" : String(data: originData!, encoding: .utf8)
129 | }
130 |
131 | return """
132 |
133 | ------------------------ SLResponse ----------------------
134 | URL:\(request?.URLString ?? "")
135 | \(dataString!)
136 | error:\(String(describing: error))
137 | ----------------------------------------------------------
138 |
139 | """
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/Documentation/Request.md:
--------------------------------------------------------------------------------
1 | ## Request
2 |
3 | Configuration of a DataRequest.
4 |
5 | #### Override the SLRequest
6 |
7 | ```swift
8 | import SolarNetwork
9 |
10 | class GitHubSigninRequest: SLRequest {
11 |
12 | // override the loadRequest method
13 | override func loadRequest() {
14 | super.loadRequest()
15 |
16 | //specify the method if is diffrent from target.method
17 | self.method = .put
18 |
19 | //specify the path
20 | self.path = "/authorizations/clients/\(GitHubAppClientID)"
21 | }
22 |
23 | /**
24 | the subClass's properties will encode to Parameters by Reflection.
25 | [
26 | "client_secret" : GitHubAppClientSecret,
27 | "scopes": ["repo", "user"]
28 | ]
29 | */
30 | var client_secret = GitHubAppClientSecret
31 |
32 | var scopes = ["repo", "user"]
33 | }
34 | ```
35 |
36 | #### Usage
37 |
38 | ```swift
39 | //When you click the Signin button.
40 | if let userName = userName, let password = password {
41 |
42 | let signinRequest = GitHubSigninRequest()
43 | //basic Authentication
44 | signinRequest.basicAuthentication = (userName, password)
45 |
46 | GitHubNetwork.request(signinRequest) { [weak self] (response) in
47 |
48 | if let model = response.decode(to: GitHubAuthenticationModel.self) {
49 |
50 | if model.token.count > 0 {
51 | //save token
52 | }
53 |
54 | }
55 | else if let error = response.error {
56 | //show error
57 | }
58 |
59 | }
60 | }
61 | ```
62 | #### You can also specify the following properties if you need.
63 |
64 | ```swift
65 | public var method: HTTPMethod
66 |
67 | public var path: String
68 |
69 | //if specify URLString, it will ignore the target's baseURLString.
70 | public var URLString: String
71 |
72 | //if specify parameters, it will ignore the properties Reflection in subClass.
73 | public var parameters: Parameters?
74 |
75 | //specify the parameterEncoding if is diffrent from target.parameterEncoding.
76 | public var parameterEncoding: ParameterEncoding
77 |
78 | public var headers: [String: String]?
79 |
80 | public var credential: URLCredential?
81 |
82 | public var basicAuthentication: (user: String, password: String)?
83 |
84 | //specify the dataKeyPath if is diffrent from target.status.dataKeyPath.
85 | public var dataKeyPath: String?
86 | ```
87 |
88 | #### Set BlackList of Request
89 |
90 | ```swift
91 | import Foundation
92 | import SolarNetwork
93 |
94 | class HTTPBinPOSTRequest: SLRequest {
95 |
96 | override func loadRequest() {
97 | super.loadRequest()
98 |
99 | method = .post
100 | path = "/post"
101 | // parameterEncoding = JSONEncoding.default
102 |
103 | headers = ["test": "testValue"]
104 | }
105 |
106 | let testPOSTProperty = "testPOSTProperty"
107 |
108 | var name: String?
109 |
110 | let testBlackListProperty = "testBlackListProperty"
111 |
112 | //override the blackList, the parameters will no contain "testBlackListProperty"
113 | override var blackList: [String] {
114 | return ["testBlackListProperty"]
115 | }
116 | }
117 | ```
118 |
119 | #### Control the state of Request
120 |
121 | ```swift
122 | let request = SubSLRequest()
123 |
124 | request.pause()
125 |
126 | request.resume()
127 |
128 | //If cancel, you should send the request again.
129 | request.cancel()
130 | ```
131 |
132 | #### Custom ParameterEncoding
133 |
134 | Alamofire not support String & Array parameters, if you wan to set String & Array parameters, you can use **SLParameterValueJSONEncoding** or **SLParameterValuePropertyListEncoding**.
135 |
136 | ```swift
137 | //String Parameters
138 |
139 | import Foundation
140 | import SolarNetwork
141 |
142 | class HTTPBinStringParametersRequest: SLRequest {
143 |
144 | override func loadRequest() {
145 | super.loadRequest()
146 |
147 | method = .post
148 | path = "/post"
149 | parameterEncoding = SLParameterValueJSONEncoding.default
150 | }
151 |
152 | let anyKey = "anyString"
153 | }
154 |
155 | HTTPBinNetwork.request(HTTPBinStringParametersRequest()) { (response) in
156 |
157 | }
158 | ```
159 |
160 |
161 | ```swift
162 | //Array Parameters
163 |
164 | import Foundation
165 | import SolarNetwork
166 |
167 | class HTTPBinArrayParametersRequest: SLRequest {
168 |
169 | override func loadRequest() {
170 | super.loadRequest()
171 |
172 | method = .post
173 | path = "/post"
174 | parameterEncoding = SLParameterValueJSONEncoding.default
175 | }
176 |
177 | let anyKey: [Any] = ["anyObj0", 1]
178 | }
179 |
180 | HTTPBinNetwork.request(HTTPBinArrayParametersRequest()) { (response) in
181 |
182 | }
183 | ```
184 |
--------------------------------------------------------------------------------
/Source/Utility/SLParameterValueEncoding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLParameterValueEncoding.swift
3 | // SolarNetwork
4 | //
5 | // Created by wyhazq on 2018/9/4.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct SLParameterValueJSONEncoding: ParameterEncoding {
11 |
12 | // MARK: Properties
13 |
14 | /// Returns a `JSONEncoding` instance with default writing options.
15 | public static var `default`: SLParameterValueJSONEncoding { return SLParameterValueJSONEncoding() }
16 |
17 | /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
18 | public static var prettyPrinted: SLParameterValueJSONEncoding { return SLParameterValueJSONEncoding(options: .prettyPrinted) }
19 |
20 | /// The options for writing the parameters as JSON data.
21 | public let options: JSONSerialization.WritingOptions
22 |
23 | // MARK: Initialization
24 |
25 | /// Creates a `JSONEncoding` instance using the specified options.
26 | ///
27 | /// - parameter options: The options for writing the parameters as JSON data.
28 | ///
29 | /// - returns: The new `JSONEncoding` instance.
30 | public init(options: JSONSerialization.WritingOptions = []) {
31 | self.options = options
32 | }
33 |
34 |
35 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
36 | var urlRequest = try urlRequest.asURLRequest()
37 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
38 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
39 | }
40 |
41 | guard let parameters = parameters else { return urlRequest }
42 |
43 | if let value = parameters.values.first {
44 | if let string = value as? String {
45 | if let data = string.data(using: .utf8, allowLossyConversion: false) {
46 | urlRequest.httpBody = data
47 | }
48 | }
49 | else if let array = value as? [Any] {
50 | do {
51 | let data = try JSONSerialization.data(withJSONObject: array, options: options)
52 | urlRequest.httpBody = data
53 | } catch {
54 | throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
55 | }
56 | }
57 | }
58 |
59 | return urlRequest
60 | }
61 | }
62 |
63 | public struct SLParameterValuePropertyListEncoding: ParameterEncoding {
64 |
65 | /// Returns a default `PropertyListEncoding` instance.
66 | public static var `default`: SLParameterValuePropertyListEncoding { return SLParameterValuePropertyListEncoding() }
67 |
68 | /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options.
69 | public static var xml: SLParameterValuePropertyListEncoding { return SLParameterValuePropertyListEncoding(format: .xml) }
70 |
71 | /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options.
72 | public static var binary: SLParameterValuePropertyListEncoding { return SLParameterValuePropertyListEncoding(format: .binary) }
73 |
74 | /// The property list serialization format.
75 | public let format: PropertyListSerialization.PropertyListFormat
76 |
77 | /// The options for writing the parameters as plist data.
78 | public let options: PropertyListSerialization.WriteOptions
79 |
80 | // MARK: Initialization
81 |
82 | /// Creates a `PropertyListEncoding` instance using the specified format and options.
83 | ///
84 | /// - parameter format: The property list serialization format.
85 | /// - parameter options: The options for writing the parameters as plist data.
86 | ///
87 | /// - returns: The new `PropertyListEncoding` instance.
88 | public init(
89 | format: PropertyListSerialization.PropertyListFormat = .xml,
90 | options: PropertyListSerialization.WriteOptions = 0)
91 | {
92 | self.format = format
93 | self.options = options
94 | }
95 |
96 | public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
97 | var urlRequest = try urlRequest.asURLRequest()
98 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
99 | urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
100 | }
101 |
102 | guard let parameters = parameters else { return urlRequest }
103 |
104 | if let value = parameters.values.first {
105 | if let string = value as? String {
106 | if let data = string.data(using: .utf8, allowLossyConversion: false) {
107 | urlRequest.httpBody = data
108 | }
109 | }
110 | else if let array = value as? [Any] {
111 | do {
112 | let data = try PropertyListSerialization.data(
113 | fromPropertyList: array,
114 | format: format,
115 | options: options
116 | )
117 | urlRequest.httpBody = data
118 | } catch {
119 | throw AFError.parameterEncodingFailed(reason: .customEncodingFailed(error: error))
120 | }
121 | }
122 | }
123 |
124 | return urlRequest
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Documentation/Target.md:
--------------------------------------------------------------------------------
1 | ## Target
2 |
3 | You can use Target to Configuration of a Host or a set of Requests for the same configuration.
4 |
5 | ```swift
6 | import SolarNetwork
7 |
8 | struct GitHubTarget: SLTarget {
9 | /// Required: You have to specify baseURLString.
10 | var baseURLString: String { return "https://api.github.com" }
11 |
12 | /// Optional: You can specify the default ParameterEncoding of the host. e.g.: URLEncoding, JSONEncoding, PropertyListEncoding or Custom ParameterEncoding. The default is URLEncoding.default.
13 | var parameterEncoding: ParameterEncoding { return JSONEncoding.default }
14 |
15 | /// Optional:
16 | /**
17 | Optional: The default is nil.
18 |
19 | You can specify the ServerTrustPolicy of the host. This can improve the difficulty of Charles view all of the HTTP and SSL / HTTPS traffic between machine and the Internet.
20 |
21 | First get the Certificates of Host:
22 | openssl s_client -connect api.github.com:443 /dev/null | openssl x509 -outform DER > github.cer
23 |
24 | Then put the Certificates of Host in MainBundle.
25 | Last, specify ServerTrustPolicy like this.
26 | */
27 | var serverTrustPolicies: [String : ServerTrustPolicy]? {
28 |
29 | #if DEBUG
30 | let validateCertificateChain = false
31 | let validateHost = false
32 | #else
33 | let validateCertificateChain = true
34 | let validateHost = true
35 | #endif
36 |
37 | let policies: [String: ServerTrustPolicy] = [
38 | host: .pinCertificates(
39 | certificates: ServerTrustPolicy.certificates(),
40 | validateCertificateChain: validateCertificateChain,
41 | validateHost: validateHost
42 | )
43 | ]
44 | return policies
45 |
46 | }
47 |
48 | /** Optional:
49 | how to use?
50 | First put the p12 of client in MainBundle.
51 | */
52 | var clientTrustPolicy: (secPKCS12Name: String, password: String)? {
53 | return (secPKCS12Name: "github", password: "123456")
54 | }
55 | }
56 | ```
57 |
58 | #### Usage
59 |
60 | ```swift
61 | let GitHubNetwork = SLNetwork(GitHubTarget())
62 | ```
63 | #### You can also specify the following properties if you need.
64 |
65 | ```swift
66 | struct GitHubTarget: SLTarget {
67 | /// Optional: You can specify the default HTTPMethod of the host, so you will not have to specify the method of a request each time. The default is .get.
68 | var method: HTTPMethod { return .get }
69 |
70 | /// Optional: You can specify the default Headers of the host, and you can change the header if you need. The default is nil.
71 | var storeHeader: [String : String]? = ["TestHeaderKey": "TestHeaderValue"]
72 | var headers: [String : String]? {
73 | get {
74 | return storeHeader
75 | }
76 | set {
77 | storeHeader = newValue
78 | }
79 | }
80 |
81 | /// Optional: You can specify the URLSessionConfiguration of the host. The default is this.
82 | var configuration: URLSessionConfiguration { return URLSessionConfiguration.default }
83 |
84 | /// Optional: SolarNetwork resposne in a background serial queue, you can specify the responseQueue if you need. The default is nil.
85 | var responseQueue: DispatchQueue? { return nil }
86 |
87 | /// Optional: You can specify the Plugins of the host. The default is nil.
88 | var plugins: [SLPlugin]? {
89 |
90 | return [GitHubPlugin(), OtherPligin()]
91 | }
92 | /// Optional: You can observe the reachability of the host. The default is nil.
93 | var reachability: NetworkReachabilityManager.Listener? {
94 | return { (status) in
95 | switch status {
96 | case .unknown:
97 | debugPrint("unknown")
98 | case .notReachable:
99 | debugPrint("notReachable")
100 | case .reachable(.wwan):
101 | debugPrint("wwan")
102 | case .reachable(.ethernetOrWiFi):
103 | debugPrint("ethernetOrWiFi")
104 | }
105 | }
106 | }
107 |
108 | /**
109 | Optional: The default is nil.
110 |
111 | In most cases, the response json like this:
112 | {
113 | "code" : 1,
114 | "message" : "succeed",
115 | "data" : {
116 | //json
117 | }
118 | }
119 |
120 | So, you can specify status like this.
121 | If response isn't the case, you don't need to specify it.
122 | */
123 | var status: (codeKey: String, successCode: Int, messageKey: String?, dataKeyPath: String?)? {
124 | return (codeKey: "code", successCode:1, messageKey:"message", dataKeyPath:"data")
125 | }
126 |
127 | /// Optional: You can specify the JSONDecoder of the host. The default is this.
128 | var decoder: JSONDecoder {
129 | let decoder = JSONDecoder()
130 | decoder.dateDecodingStrategy = .secondsSince1970
131 | return decoder
132 | }
133 |
134 | /// Optional: The target's debugPrint Switch, default is on.
135 | var enableLog: Bool { return true }
136 | }
137 | ```
138 |
139 | #### [Pre-populate the DNS cache(HTTPS直连IP、防止DNS劫持)](https://github.com/AFNetworking/AFNetworking/issues/2954)
140 |
141 | ```swift
142 | struct GitHubTarget: SLTarget {
143 |
144 | var baseURLString: String { return "https://api.github.com" }
145 |
146 | var IPURLString: String? {
147 | get {
148 | return storeIPURLString
149 | }
150 | set {
151 | storeIPURLString = newValue
152 | }
153 | }
154 | }
155 |
156 | //after get ip of domain Such as the use HTTPDNS,then
157 | GitHubNetwork.target.IPURLString = "https://IP"
158 | ```
159 |
--------------------------------------------------------------------------------
/Source/SLTarget.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLTarget.swift
3 | //
4 | // Created by wyhazq on 2018/1/9.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | public protocol SLTarget {
29 |
30 | /// The target's baseURLString.
31 | var baseURLString: String { get }
32 |
33 | var IPURLString: String? { get set }
34 |
35 | /// The target's HTTPMethod.
36 | var method: HTTPMethod { get }
37 |
38 | /// The target's HTTPHeaders.
39 | var headers: HTTPHeaders? { get }
40 |
41 | /// The target's ParameterEncoding.
42 | var parameterEncoding: ParameterEncoding { get }
43 |
44 | /// The target's URLSessionConfiguration.
45 | var configuration: URLSessionConfiguration { get }
46 |
47 | var allHostsMustBeEvaluated: Bool { get }
48 |
49 | /// The target's serverTrustPolicies
50 | var serverEvaluators: [String : ServerTrustEvaluating]? { get }
51 |
52 | /// The target's clentTrustPolicy
53 | var clientTrustPolicy: (secPKCS12Path: String, password: String)? { get }
54 |
55 | /// The target's ResponseQueue
56 | var responseQueue: DispatchQueue? { get }
57 |
58 | /// The target's Plugins
59 | var plugins: [SLPlugin]? { get }
60 |
61 | /// The target's Reachability
62 | var reachabilityListener: ReachabilityListener? { get }
63 |
64 | /// The target's Host.
65 | var host: String { get }
66 |
67 | /// The target's Response Status
68 | var status: (codeKey: String, successCode: Int, messageKey: String?, dataKeyPath: String?)? { get }
69 |
70 | /// The target's Response JSONDecoder
71 | var decoder: JSONDecoder { get }
72 |
73 | /// The target's debugPrint Switch, default is on.
74 | var enableLog: Bool { get }
75 |
76 | }
77 |
78 | public extension SLTarget {
79 |
80 | var baseURLString: String { return "" }
81 |
82 | var IPURLString: String? {
83 | get {
84 | return nil
85 | }
86 | set {
87 |
88 | }
89 | }
90 |
91 | var method: HTTPMethod { return .get }
92 |
93 | var headers: HTTPHeaders? {
94 | get {
95 | return nil
96 | }
97 | }
98 |
99 | var parameterEncoding: ParameterEncoding { return URLEncoding.default }
100 |
101 | var configuration: URLSessionConfiguration { return URLSessionConfiguration.af.default }
102 |
103 | var allHostsMustBeEvaluated: Bool { return true }
104 |
105 | /**
106 | how to use?
107 | First get the Certificates of Host:
108 | openssl s_client -connect test.example.com:443 /dev/null | openssl x509 -outform DER > example.cer
109 |
110 | then put the Certificates of Host in MainBundle.
111 |
112 | Example:
113 | ---------------------------------------------------------
114 | var serverEvaluators: [String : ServerTrustEvaluating]? {
115 | #if DEBUG
116 | let validateHost = false
117 | #else
118 | let validateHost = true
119 | #endif
120 |
121 | let evaluators: [String: ServerTrustEvaluating] = [
122 | host: PinnedCertificatesTrustEvaluator(validateHost: validateHost)
123 | ]
124 |
125 | return evaluators
126 | }
127 | ---------------------------------------------------------
128 |
129 | if Debug, advice set
130 | validateHost: false
131 | */
132 | var serverEvaluators: [String : ServerTrustEvaluating]? { return nil }
133 |
134 | /**
135 | how to use?
136 | First put the p12 of client in MainBundle.
137 |
138 | var clientTrustPolicy: (secPKCS12Path: String, password: String)? {
139 | return (secPKCS12Path: Bundle.main.path(forResource: "secPKCS12Name", ofType: "p12") ?? "", password: "password")
140 | }
141 | */
142 | var clientTrustPolicy: (secPKCS12Path: String, password: String)? { return nil }
143 |
144 | var responseQueue: DispatchQueue? { return nil }
145 |
146 | var plugins: [SLPlugin]? { return nil }
147 |
148 | var reachabilityListener: ReachabilityListener? { return nil }
149 |
150 | var host: String {
151 | if let URL = URL(string: baseURLString), let host = URL.host {
152 | return host
153 | }
154 | return ""
155 | }
156 |
157 | var status: (codeKey: String, successCode: Int, messageKey: String?, dataKeyPath: String?)? { return nil }
158 |
159 | var decoder: JSONDecoder {
160 | let decoder = JSONDecoder()
161 | decoder.dateDecodingStrategy = .secondsSince1970
162 | return decoder
163 | }
164 |
165 | var enableLog: Bool { return true }
166 | }
167 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/GitHub/GitHubVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubVC.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SolarNetwork
11 |
12 | private let GitHubTokenKey: String = "GitHubTokenKey"
13 | // d12e11ca90b5e30d55b1819e1c099c935b0d5f12
14 | // x-oauth-basic
15 |
16 | class GitHubVC: UITableViewController {
17 |
18 | enum Section: Int {
19 | case Authorizations
20 | case Public
21 | case Download
22 | }
23 |
24 | enum AuthorizationsRow: Int {
25 | case Signin = 2
26 | case MyInfo
27 | case Signout
28 | }
29 |
30 | enum PublicRow: Int {
31 | case API
32 | case UserInfo
33 | }
34 |
35 | enum DownloadRow: Int {
36 | case Normal
37 | case Resume
38 | }
39 |
40 | var userName: String?
41 | var password: String?
42 |
43 | var token: String?
44 |
45 | lazy var resumeDownloadRequest = GitHubDownloadRequest()
46 | var isDownloading: Bool = false
47 |
48 | override func viewDidLoad() {
49 | super.viewDidLoad()
50 | //First you should get the IP of the domain name.
51 | // GitHubNetwork.target.IPURLString = "https://54.169.195.247"
52 |
53 | token = UserDefaults.standard.string(forKey: GitHubTokenKey)
54 |
55 |
56 | // Uncomment the following line to preserve selection between presentations
57 | // self.clearsSelectionOnViewWillAppear = false
58 |
59 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
60 | // self.navigationItem.rightBarButtonItem = self.editButtonItem
61 | }
62 |
63 | @IBAction func userNameTextFieldDidEndEditing(_ sender: UITextField) {
64 | userName = sender.text
65 | }
66 |
67 | @IBAction func passwordTextFieldDidEndEditing(_ sender: UITextField) {
68 | password = sender.text
69 | }
70 |
71 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
72 | tableView.deselectRow(at: indexPath, animated: false)
73 | view.endEditing(true)
74 |
75 | guard let section = Section(rawValue: indexPath.section) else { return }
76 |
77 | switch section {
78 | case .Authorizations:
79 | guard let row = AuthorizationsRow(rawValue: indexPath.row) else { return }
80 |
81 | switch row {
82 | case .Signin:
83 | if let token = token, token.count > 0 {
84 | debugPrint("You have been sign in.")
85 | }
86 | else {
87 | if let userName = userName, let password = password {
88 |
89 | let signinRequest = GitHubSigninRequest()
90 | let authorizationHeader = HTTPHeader.authorization(username: userName, password: password)
91 | signinRequest.headers = HTTPHeaders(arrayLiteral: authorizationHeader)
92 |
93 | GitHubNetwork.request(signinRequest) { [weak self] (response) in
94 | guard let strongSelf = self else { return }
95 |
96 | if let model = response.decode(to: GitHubAuthenticationModel.self) {
97 |
98 | if model.token.count > 0 {
99 | strongSelf.token = model.token
100 | UserDefaults.standard.set(model.token, forKey: GitHubTokenKey) //You shouldn't actually do that.
101 |
102 | UserDefaults.standard.synchronize()
103 | debugPrint("Sign in succeed.")
104 | }
105 |
106 | }
107 | else if let _ = response.error {
108 | //show error
109 | }
110 |
111 | }
112 | }
113 | }
114 |
115 | case .MyInfo:
116 | if let token = token, token.count > 0 {
117 | let myInfoRequest = GitHubMyInfoRequest()
118 | let authorizationHeader = HTTPHeader.authorization(username: token, password: "x-oauth-basic")
119 | myInfoRequest.headers = HTTPHeaders(arrayLiteral: authorizationHeader)
120 |
121 | GitHubNetwork.request(myInfoRequest) { (response) in
122 |
123 | }
124 |
125 | }
126 | else {
127 | debugPrint("Please sign in.")
128 | }
129 |
130 | case .Signout:
131 | if let token = token, token.count > 0 {
132 | let signoutRequest = GitHubSignoutRequest()
133 | signoutRequest.path = signoutRequest.path + token
134 | GitHubNetwork.request(signoutRequest) { [weak self] (response) in
135 | guard let strongSelf = self else { return }
136 |
137 | if response.httpURLResponse?.statusCode == 204 {
138 | strongSelf.token = nil
139 | UserDefaults.standard.removeObject(forKey: GitHubTokenKey)
140 | UserDefaults.standard.synchronize()
141 | debugPrint("Sign out succeed.")
142 | }
143 | }
144 |
145 | }
146 | else {
147 | debugPrint("Please sign in.")
148 | }
149 | }
150 |
151 | case .Public:
152 | guard let row = PublicRow(rawValue: indexPath.row) else { return }
153 |
154 | switch row {
155 | case .API:
156 | GitHubNetwork.request(GitHubAPIRequest()) { (response) in
157 |
158 | }
159 |
160 | case .UserInfo:
161 | GitHubNetwork.request(GitHubUserInfoRequest()) { (response) in
162 |
163 | }
164 | }
165 |
166 | case .Download:
167 | guard let row = DownloadRow(rawValue: indexPath.row) else { return }
168 |
169 | switch row {
170 | case .Normal:
171 | GitHubNetwork.download(GitHubDownloadRequest(), progressClosure: { (progress) in
172 |
173 | }) { (response) in
174 |
175 | }
176 |
177 | case .Resume:
178 |
179 | if isDownloading {
180 | resumeDownloadRequest.cancel()
181 | isDownloading = false
182 | }
183 | else {
184 |
185 | resumeDownloadRequest.isResume = true
186 |
187 | GitHubNetwork.download(resumeDownloadRequest, progressClosure: { (progress) in
188 |
189 | }) { (response) in
190 |
191 | }
192 |
193 | isDownloading = true
194 | }
195 |
196 |
197 | }
198 | }
199 |
200 | }
201 |
202 | override func didReceiveMemoryWarning() {
203 | super.didReceiveMemoryWarning()
204 | // Dispose of any resources that can be recreated.
205 | }
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample/HTTPBin/HTTPBinVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPBinVC.swift
3 | // SolarKit-SwiftExample
4 | //
5 | // Created by wyhazq on 2018/1/30.
6 | // Copyright © 2018年 SolarKit. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HTTPBinVC: UITableViewController {
12 |
13 | enum Section: Int {
14 | case Data
15 | case Download
16 | case Upload
17 | case Custom
18 | }
19 |
20 | enum DataRow: Int {
21 | case GET
22 | case POST
23 | case PUT
24 | case DELETE
25 | case PATCH
26 | }
27 |
28 | enum DownloadRow: Int {
29 | case Normal
30 | case Resume
31 | }
32 |
33 | enum UploadRow: Int {
34 | case Data
35 | case File
36 | case InputStrame
37 | case FormData
38 | }
39 |
40 | enum CustomRow: Int {
41 | case StringParameters
42 | case ArrayParameters
43 | }
44 |
45 | override func viewDidLoad() {
46 | super.viewDidLoad()
47 |
48 | // Uncomment the following line to preserve selection between presentations
49 | // self.clearsSelectionOnViewWillAppear = false
50 |
51 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
52 | // self.navigationItem.rightBarButtonItem = self.editButtonItem
53 | }
54 |
55 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
56 | tableView.deselectRow(at: indexPath, animated: false)
57 |
58 | guard let section = Section(rawValue: indexPath.section) else { return }
59 |
60 |
61 | switch section {
62 | case .Data:
63 | guard let row = DataRow(rawValue: indexPath.row) else { return }
64 |
65 | switch row {
66 | case .GET:
67 | HTTPBinNetwork.request(HTTPBinGETRequest()) { (response) in
68 |
69 | }
70 |
71 | case .POST:
72 | let postReq = HTTPBinPOSTRequest()
73 | postReq.name = "yourName"
74 | HTTPBinNetwork.request(postReq) { (response) in
75 |
76 | }
77 |
78 | case .PUT:
79 | HTTPBinNetwork.request(HTTPBinPUTRequest()) { (response) in
80 |
81 | }
82 |
83 | case .DELETE:
84 | HTTPBinNetwork.request(HTTPBinDELETERequest()) { (response) in
85 |
86 | }
87 |
88 | case .PATCH:
89 | HTTPBinNetwork.request(HTTPBinPATCHRequest()) { (response) in
90 |
91 | }
92 |
93 | }
94 |
95 | case .Download:
96 | guard let row = DownloadRow(rawValue: indexPath.row) else { return }
97 |
98 | switch row {
99 | case .Normal:
100 | HTTPBinNetwork.download(HTTPBinDownLoadRequest(), progressClosure: { (progress) in
101 |
102 | }) { (resposne) in
103 |
104 | }
105 |
106 | case .Resume:
107 | let downloadRequest = HTTPBinDownLoadRequest()
108 | downloadRequest.isResume = true
109 |
110 | HTTPBinNetwork.download(downloadRequest, progressClosure: { (progress) in
111 |
112 | }) { (resposne) in
113 |
114 | }
115 |
116 | }
117 |
118 | case .Upload:
119 | guard let row = UploadRow(rawValue: indexPath.row) else { return }
120 |
121 | switch row {
122 | case .Data:
123 | let bundle = Bundle.main
124 | let resourcePath = bundle.path(forResource: "SLNetwork", ofType: "png")
125 | do {
126 | if let path = resourcePath {
127 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
128 | let uploadRequest = HTTPBinUploadRequest()
129 | uploadRequest.data = data
130 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
131 |
132 | }) { (response) in
133 |
134 | }
135 | }
136 | }
137 | catch {
138 | debugPrint(error)
139 | }
140 |
141 | case .File:
142 | let bundle = Bundle.main
143 | let resourcePath = bundle.path(forResource: "SLNetwork", ofType: "png")
144 | if let path = resourcePath {
145 | let uploadRequest = HTTPBinUploadRequest()
146 | uploadRequest.filePath = path
147 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
148 |
149 | }) { (response) in
150 |
151 | }
152 | }
153 |
154 | case .InputStrame:
155 | let bundle = Bundle.main
156 | let resourcePath = bundle.path(forResource: "SLNetwork", ofType: "png")
157 | do {
158 | if let path = resourcePath {
159 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
160 | let inputStream = InputStream(data: data)
161 |
162 | let uploadRequest = HTTPBinUploadRequest()
163 | uploadRequest.inputStream = (inputStream, data.count)
164 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
165 |
166 | }) { (response) in
167 |
168 | }
169 | }
170 | }
171 | catch {
172 | debugPrint(error)
173 | }
174 |
175 | case .FormData:
176 | let bundle = Bundle.main
177 | let resourcePath = bundle.path(forResource: "SLNetwork", ofType: "png")
178 | if let path = resourcePath {
179 | let uploadRequest = HTTPBinUploadRequest()
180 | uploadRequest.multipartFormData = { (multipartFormData) in
181 | let url = URL(fileURLWithPath: path)
182 | multipartFormData.append(url, withName: "SLNetwork")
183 | }
184 |
185 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
186 |
187 | }) { (response) in
188 |
189 | }
190 | }
191 |
192 | }
193 |
194 | case .Custom:
195 | guard let row = CustomRow(rawValue: indexPath.row) else { return }
196 |
197 | switch row {
198 |
199 | case .StringParameters:
200 |
201 | HTTPBinNetwork.request(HTTPBinStringParametersRequest()) { (response) in
202 |
203 | }
204 |
205 | case .ArrayParameters:
206 |
207 | HTTPBinNetwork.request(HTTPBinArrayParametersRequest()) { (response) in
208 |
209 | }
210 |
211 | }
212 |
213 | }
214 |
215 | }
216 |
217 | override func didReceiveMemoryWarning() {
218 | super.didReceiveMemoryWarning()
219 | // Dispose of any resources that can be recreated.
220 | }
221 |
222 | }
223 |
224 |
225 |
--------------------------------------------------------------------------------
/ChineseREADME.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 |   
4 |
5 | Elegant network abstraction layer in Swift.
6 |
7 |
8 |
9 | - [Design](#design)
10 | - [Features](#features)
11 | - [Requirements](#requirements)
12 | - [Communication](#communication)
13 | - [Installation](#installation)
14 | - [Usage](#usage)
15 | - [Base Usage](#base-usage) - [Target](#target) - [Request](#request) - [Download](#download) - [Upload](#upload) - [Decode](#decode)
16 | - [Target](Documentation/Target.md) - Configuration of a Host.
17 | - [Request](Documentation/Request.md) - Configuration of a DataRequest.
18 | - [Download](Documentation/Download.md) - Configuration of a DownloadRequest or ResumeDownloadRequest.
19 | - [Upload](Documentation/Upload.md) - Configuration of a UploadRequest(Data - File - InputStream - FormData).
20 | - [Progress](Documentation/Progress.md) - Progress
21 | - [Response](Documentation/Response.md) - Decode - Error
22 | - [Plugin](Documentation/Plugin.md) - WillSend - DidReceive
23 | - [License](#license)
24 |
25 | ---
26 |
27 | # Design
28 |
29 | **Alamofire**和**Moya**都是优雅的网络框架,他们各有其优点。刚开始用Swift做项目时,两个框架都有用过,但慢慢地项目逐渐增大,使用Alamofire时,各种接口和参数难以管理,后来用了Moya,Moya是基于Alamofire的封装,十分优秀。接口和参数很乱的情况稍微好了点,但随着TargetType慢慢增大时,每次看一个接口的信息都感觉很不清晰,太多Switch,case,case,case了……所以一直在想,应该如何结合两者的优点,所以在Alamofire的基础上进行了封装,借鉴了Moya的TargetType和Plugin的设计思想,并保留了Alamofire的部分API。形成了**SolarNetwork**。
30 |
31 | - **SLNetwork** 对应一个SessionManager,负责Request的发送,Progress和Response的回调。
32 | - **SLTarget** 对应一个Host或者一系列具有相同配置请求的配置。
33 | - **SLRequest**, **SLDownloadRequest**, **SLUploadRequest** 分别对应Data, Download和Upload,是这3种请求的配置。
34 | - **SLProgress** 下载和上传进度的回调。
35 | - **SLResponse** 是一个已处理过的Response,你可以进一步选择转换为JsonObject或者Model。
36 | - **SLPlugin** 提供了两个切入点,分别用来在请求发送前修改请求的`willSend` 和 接收到网络回调后修改回调内容的 `didReceive`。
37 | - **SLReflection** 负责将SLRequest的子类属性反射为Alamofire.Parameters。
38 |
39 | 所以一个网络请求的具体流程为:
40 |
41 | ```swift
42 | SLNetwork(SLTarget).request(SLRequest).willSend(SLRequest)
43 | .progressClosure(SLProgress)
44 | .reponseData(OriginalResponse)
45 | .didReceive(SLResponse).decodeTo(Dictionary)
46 | .completionClosure(SLResponse)
47 | .decodeTo(Model: Decodable).dealWithError
48 | ```
49 |
50 | 大多数情况下,你只需要关心的是:
51 |
52 | ```swift
53 | SLNetwork(SLTarget).request(SLRequest)
54 | .progressClosure(SLProgress)
55 | .completionClosure(SLResponse)
56 | ```
57 |
58 | ---
59 |
60 | # Features
61 |
62 | - [x] 灵活的多服务配置(baseURL, Header, ParameterEncoding, URLSessionConfiguration, TSL…...)
63 | - [x] 完整的数据请求方法
64 | - [x] 完整的下载和断点下载方法(意外退出也能恢复)
65 | - [x] 完整的上传方法:File, Data, InputStream, FormData
66 | - [x] 上传和下载的进度回调
67 | - [x] 网络状态Reachability回调
68 | - [x] 保留Alamofire的对外API
69 | - [x] 修改Request和Response的Plugin
70 | - [x] Swift4下利用Decodable将JSON转为Model
71 | - [x] HTTPS IP直连
72 | - [x] 完整的日志打印
73 |
74 | ---
75 |
76 | ## Requirements
77 |
78 | - iOS 8.0+
79 | - Xcode 9+
80 | - Swift 4+
81 |
82 | ---
83 |
84 | ## Communication
85 |
86 | - If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire).
87 | - If you **found a bug**, open an issue.
88 | - If you **have a feature request**, open an issue.
89 | - If you **want to contribute**, submit a pull request.
90 |
91 | ---
92 |
93 | ## Installation
94 |
95 | ### CocoaPods
96 |
97 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
98 |
99 | ```bash
100 | $ gem install cocoapods
101 | ```
102 |
103 | > CocoaPods 1.1+ is required.
104 |
105 | To integrate SolarNetwork into your Xcode project using CocoaPods, specify it in your `Podfile`:
106 |
107 | ```ruby
108 | source 'https://github.com/CocoaPods/Specs.git'
109 | platform :ios, '8.0'
110 | use_frameworks!
111 |
112 | target '' do
113 | pod 'SolarNetwork'
114 | end
115 | ```
116 |
117 | Then, run the following command:
118 |
119 | ```bash
120 | $ pod install
121 | ```
122 |
123 | ### Carthage
124 |
125 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
126 |
127 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
128 |
129 | ```bash
130 | $ brew update
131 | $ brew install carthage
132 | ```
133 |
134 | To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
135 |
136 | ```ogdl
137 | github "ThreeGayHub/SolarNetwork"
138 | ```
139 |
140 | Run `carthage update`
141 |
142 | If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
143 |
144 | ---
145 |
146 | # Usage
147 |
148 | ## Base Usage
149 |
150 | ### Target
151 |
152 | ```swift
153 | import SolarNetwork
154 |
155 | struct HTTPBinTarget: SLTarget {
156 | var baseURLString: String { return "https://httpbin.org" }
157 | }
158 |
159 | let HTTPBinNetwork = SLNetwork(HTTPBinTarget())
160 | ```
161 |
162 | ### Request
163 |
164 | ```swift
165 | import SolarNetwork
166 |
167 | //Mark: - GET
168 | class HTTPBinGETRequest: SLRequest {
169 |
170 | override func loadRequest() {
171 | super.loadRequest()
172 |
173 | self.path = "/get"
174 | }
175 |
176 | }
177 |
178 | HTTPBinNetwork.request(HTTPBinGETRequest()) { (response) in
179 | if let dictionary = response.dataDictionary {
180 |
181 | }
182 | else if let error = response.error {
183 | //show error
184 | }
185 | }
186 |
187 | //Mark: - POST
188 | class HTTPBinPOSTRequest: SLRequest {
189 |
190 | override func loadRequest() {
191 | super.loadRequest()
192 |
193 | self.method = .post
194 | self.path = "/post"
195 | }
196 |
197 | /**
198 | 利用反射,将属性自动转换为Parameters,不需要自己组装了
199 | ["userName": "myUserName",
200 | "password": "myPassword",
201 | "name" : "youName"]
202 | */
203 | let userName = "myUserName"
204 | let password = "myPassword"
205 |
206 | var name: String?
207 | }
208 |
209 | let postReq = HTTPBinPOSTRequest()
210 | postReq.name = "yourName"
211 | HTTPBinNetwork.request(postReq) { (response) in
212 | if let dictionary = response.dataDictionary {
213 |
214 | }
215 | else if let error = response.error {
216 | //show error
217 | }
218 | }
219 | ```
220 |
221 | ### Download
222 |
223 | ```swift
224 | import SolarNetwork
225 |
226 | class HTTPBinDownLoadRequest: SLDownloadRequest {
227 |
228 | override func loadRequest() {
229 | super.loadRequest()
230 |
231 | self.path = "/image/png"
232 | self.isResume = true //control the download request is resume or not, default is false
233 | }
234 | }
235 |
236 | HTTPBinNetwork.download(HTTPBinDownLoadRequest(), progressClosure: { (progress) in
237 |
238 | }) { (resposne) in
239 |
240 | }
241 |
242 | ```
243 |
244 | ### Upload
245 |
246 | ```swift
247 | import SolarNetwork
248 |
249 | class HTTPBinUploadRequest: SLUploadRequest {
250 |
251 | override func loadRequest() {
252 | super.loadRequest()
253 |
254 | self.path = "/post"
255 | }
256 |
257 | }
258 |
259 | let uploadRequest = HTTPBinUploadRequest()
260 | uploadRequest.data = data //data to upload
261 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
262 |
263 | }) { (response) in
264 |
265 | }
266 | ```
267 |
268 | ### Decode
269 |
270 | In Swift 4, you can use Codable.
271 |
272 | ```swift
273 | import SolarNetwork
274 |
275 | struct User: Decodable { //Swift 4 Codable
276 | var id: Int
277 | var name: String
278 | var token: String
279 | }
280 |
281 | HTTPBinNetwork.request(UserRequest()) { (response) in
282 | if let user = response.decode(to: User.self) {
283 |
284 | }
285 | else if let error = response.error {
286 | //show error
287 | }
288 | }
289 | ```
290 |
291 |
292 |
293 | ---
294 |
295 | ## License
296 |
297 | Alamofire is released under the MIT license. [See LICENSE](https://github.com/ThreeGayHub/SolarNetwork/blob/master/LICENSE) for details.
298 |
299 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 |   
4 |
5 | Elegant network abstraction layer in Swift.
6 |
7 |
8 |
9 | - [中文](ChineseREADME.md)
10 | - [Design](#design)
11 | - [Features](#features)
12 | - [Requirements](#requirements)
13 | - [Communication](#communication)
14 | - [Installation](#installation)
15 | - [Usage](#usage)
16 | - [Base Usage](#base-usage) - [Target](#target) - [Request](#request) - [Download](#download) - [Upload](#upload) - [Decode](#decode)
17 | - [Target](Documentation/Target.md) - Configuration of a Host.
18 | - [Request](Documentation/Request.md) - Configuration of a DataRequest.
19 | - [Download](Documentation/Download.md) - Configuration of a DownloadRequest or ResumeDownloadRequest.
20 | - [Upload](Documentation/Upload.md) - Configuration of a UploadRequest(Data - File - InputStream - FormData).
21 | - [Progress](Documentation/Progress.md) - Progress
22 | - [Response](Documentation/Response.md) - Decode - Error
23 | - [Plugin](Documentation/Plugin.md) - WillSend - DidReceive
24 | - [License](#license)
25 | - [UpdateLog](Documentation/UpdateLog.md)
26 |
27 | ---
28 |
29 | # Design
30 |
31 | **[Alamofire](https://github.com/Alamofire/Alamofire)** and **[Moya](https://github.com/Moya/Moya)** are elegant Swift network frames. They each have their own advantages. When I use them, I always want to combine the advantages of both, make them easy to use and retain their original features. So I wrote the **SolarNetwork**.
32 |
33 | - **SLNetwork** corresponds to a SessionManager.
34 | - **SLTarget** corresponds to a Host, or a set of requests for the same configuration.
35 | - **SLRequest**, **SLDownloadRequest**, **SLUploadRequest** corresponds to Request of Data, Download, Upload.
36 | - **SLProgress** return progress when download or upload.
37 | - **SLResponse** response of a request which you can decode to JsonObject or Model.
38 | - **SLPlugin** you can modify SLRequest in `willSend` and modify SLResponse in `didReceive`.
39 | - **SLReflection** reflex properties of SubSLRequest to Alamofire.Parameters.
40 |
41 | So a complete request process is:
42 |
43 | ```swift
44 | SLNetwork(SLTarget).request(SLRequest).willSend(SLRequest)
45 | .progressClosure(SLProgress)
46 | .reponseData(OriginalResponse)
47 | .didReceive(SLResponse).decodeTo(Dictionary)
48 | .completionClosure(SLResponse)
49 | .decodeTo(Model: Decodable).dealWithError
50 | ```
51 |
52 | In most cases, what you need to concerned about is:
53 |
54 | ```swift
55 | SLNetwork(SLTarget).request(SLRequest)
56 | .progressClosure(SLProgress)
57 | .completionClosure(SLResponse)
58 | ```
59 |
60 | ---
61 |
62 | # Features
63 |
64 | - [x] URL / JSON / plist Parameter Encoding
65 | - [x] Upload File / Data / Stream / MultipartFormData
66 | - [x] Download File using Request or Resume Data
67 | - [x] Authentication with URLCredential
68 | - [x] Upload and Download Progress Closures with Progress
69 | - [x] Dynamically Adapt and Retry Requests
70 | - [x] TLS Certificate and Public Key Pinning
71 | - [x] Network Reachability
72 | - [x] Pre-populate the DNS cache
73 | - [x] Complete Logger
74 |
75 | ---
76 |
77 | ## Requirements
78 |
79 | - iOS 8.0+
80 | - Xcode 9+
81 | - Swift 4+
82 |
83 | ---
84 |
85 | ## Communication
86 |
87 | - If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire).
88 | - If you **found a bug**, open an issue.
89 | - If you **have a feature request**, open an issue.
90 | - If you **want to contribute**, submit a pull request.
91 |
92 | ---
93 |
94 | ## Installation
95 |
96 | ### CocoaPods
97 |
98 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
99 |
100 | ```bash
101 | $ gem install cocoapods
102 | ```
103 |
104 | > CocoaPods 1.1+ is required.
105 |
106 | To integrate SolarNetwork into your Xcode project using CocoaPods, specify it in your `Podfile`:
107 |
108 | ```ruby
109 | source 'https://github.com/CocoaPods/Specs.git'
110 | platform :ios, '8.0'
111 | use_frameworks!
112 |
113 | target '' do
114 | pod 'SolarNetwork'
115 | end
116 | ```
117 |
118 | Then, run the following command:
119 |
120 | ```bash
121 | $ pod install
122 | ```
123 |
124 | ### Carthage
125 |
126 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
127 |
128 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
129 |
130 | ```bash
131 | $ brew update
132 | $ brew install carthage
133 | ```
134 |
135 | To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
136 |
137 | ```ogdl
138 | github "ThreeGayHub/SolarNetwork"
139 | ```
140 |
141 | Run `carthage update`
142 |
143 | If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
144 |
145 | ---
146 |
147 | # Usage
148 |
149 | ## Base Usage
150 |
151 | ### Target
152 |
153 | ```swift
154 | import SolarNetwork
155 |
156 | struct HTTPBinTarget: SLTarget {
157 | var baseURLString: String { return "https://httpbin.org" }
158 | }
159 |
160 | let HTTPBinNetwork = SLNetwork(HTTPBinTarget())
161 | ```
162 |
163 | ### Request
164 |
165 | ```swift
166 | import SolarNetwork
167 |
168 | //Mark: - GET
169 | class HTTPBinGETRequest: SLRequest {
170 |
171 | override func loadRequest() {
172 | super.loadRequest()
173 |
174 | self.path = "/get"
175 | }
176 |
177 | }
178 |
179 | HTTPBinNetwork.request(HTTPBinGETRequest()) { (response) in
180 | if let dictionary = response.dataDictionary {
181 |
182 | }
183 | else if let error = response.error {
184 | //show error
185 | }
186 | }
187 |
188 | //Mark: - POST
189 | class HTTPBinPOSTRequest: SLRequest {
190 |
191 | override func loadRequest() {
192 | super.loadRequest()
193 |
194 | self.method = .post
195 | self.path = "/post"
196 | }
197 |
198 | /**
199 | properties will encode to parameters by Reflection
200 | ["userName": "myUserName",
201 | "password": "myPassword",
202 | "name" : "youName"]
203 | */
204 | let userName = "myUserName"
205 | let password = "myPassword"
206 |
207 | var name: String?
208 | }
209 |
210 | let postReq = HTTPBinPOSTRequest()
211 | postReq.name = "yourName"
212 | HTTPBinNetwork.request(postReq) { (response) in
213 | if let dictionary = response.dataDictionary {
214 |
215 | }
216 | else if let error = response.error {
217 | //show error
218 | }
219 | }
220 | ```
221 |
222 | ### Download
223 |
224 | ```swift
225 | import SolarNetwork
226 |
227 | class HTTPBinDownLoadRequest: SLDownloadRequest {
228 |
229 | override func loadRequest() {
230 | super.loadRequest()
231 |
232 | self.path = "/image/png"
233 | self.isResume = true //control the download request is resume or not, default is false
234 | }
235 | }
236 |
237 | HTTPBinNetwork.download(HTTPBinDownLoadRequest(), progressClosure: { (progress) in
238 |
239 | }) { (resposne) in
240 |
241 | }
242 |
243 | ```
244 |
245 | ### Upload
246 |
247 | ```swift
248 | import SolarNetwork
249 |
250 | class HTTPBinUploadRequest: SLUploadRequest {
251 |
252 | override func loadRequest() {
253 | super.loadRequest()
254 |
255 | self.path = "/post"
256 | }
257 |
258 | }
259 |
260 | let uploadRequest = HTTPBinUploadRequest()
261 | uploadRequest.data = data //data to upload
262 | HTTPBinNetwork.upload(uploadRequest, progressClosure: { (progress) in
263 |
264 | }) { (response) in
265 |
266 | }
267 | ```
268 |
269 | ### Decode
270 |
271 | In Swift 4, you can use Codable.
272 |
273 | ```swift
274 | import SolarNetwork
275 |
276 | struct User: Decodable { //Swift 4 Codable
277 | var id: Int
278 | var name: String
279 | var token: String
280 | }
281 |
282 | HTTPBinNetwork.request(UserRequest()) { (response) in
283 | if let user = response.decode(to: User.self) {
284 |
285 | }
286 | else if let error = response.error {
287 | //show error
288 | }
289 | }
290 | ```
291 |
292 |
293 |
294 | ---
295 |
296 | ## License
297 |
298 | Alamofire is released under the MIT license. [See LICENSE](https://github.com/ThreeGayHub/SolarNetwork/blob/master/LICENSE) for details.
299 |
300 |
--------------------------------------------------------------------------------
/Source/SLRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLRequest.swift
3 | //
4 | // Created by wyhazq on 2018/1/6.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | /// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
29 | open class SLRequest: SLReflection {
30 |
31 | public init(method: HTTPMethod = .get,
32 | URLString: String? = nil,
33 | path: String = "",
34 | parameters: Parameters? = nil,
35 | parameterEncoding: ParameterEncoding = URLEncoding.default,
36 | headers: HTTPHeaders? = nil) {
37 | self.storeMethod = method
38 | self.storeURLString = URLString
39 | self.path = path
40 | self.storeParameters = parameters
41 | self.headers = headers
42 |
43 | loadRequest()
44 | }
45 |
46 | open func loadRequest() {}
47 |
48 | public var originalRequest: Request?
49 |
50 | /// Base64 string of the request's URLString + method
51 | public var requestID: String {
52 | let string = URLString + method.rawValue
53 | return string.data(using: .utf8)?.base64EncodedString() ?? ""
54 | }
55 |
56 | public var method: HTTPMethod {
57 | get {
58 | return storeMethod ?? .get
59 | }
60 | set {
61 | storeMethod = newValue
62 | }
63 | }
64 |
65 | public var path: String = ""
66 |
67 | public var URLString: String {
68 | get {
69 | return storeURLString ?? ""
70 | }
71 | set {
72 | storeURLString = newValue
73 | }
74 | }
75 |
76 | public var parameters: Parameters? {
77 | get {
78 | if let parameters = storeParameters {
79 | return parameters
80 | }
81 | else if let parameters = jsonObject as? Parameters {
82 | storeParameters = parameters
83 | return parameters
84 | }
85 | return nil
86 | }
87 | set {
88 | storeParameters = newValue
89 | }
90 | }
91 |
92 | public var parameterEncoding: ParameterEncoding {
93 | get {
94 | return storeParameterEncoding ?? URLEncoding.default
95 | }
96 | set {
97 | storeParameterEncoding = newValue
98 | }
99 | }
100 |
101 | public var target: SLTarget? {
102 | get {
103 | return storeTarget
104 | }
105 | set {
106 | if storeMethod == nil {
107 | storeMethod = newValue?.method
108 | }
109 | if storeURLString == nil {
110 | if let IPURLString = newValue?.IPURLString {
111 | storeURLString = IPURLString + path
112 | if let host = newValue?.host {
113 | if headers == nil {
114 | headers = [SLHostKey : host]
115 | }
116 | else {
117 | headers![SLHostKey] = host
118 | }
119 | }
120 | }
121 | else if let baseURLString = newValue?.baseURLString {
122 | storeURLString = baseURLString + path
123 | }
124 | }
125 | if storeParameterEncoding == nil {
126 | storeParameterEncoding = newValue?.parameterEncoding
127 | }
128 | if let targetHeaders = newValue?.headers {
129 | if let reqHeaders = headers {
130 | for (key, value) in targetHeaders.dictionary {
131 | if reqHeaders.value(for: key) == nil {
132 | headers?.update(name: key, value: value)
133 | }
134 | }
135 | }
136 | else {
137 | headers = targetHeaders
138 | }
139 | }
140 | if dataKeyPath == nil {
141 | dataKeyPath = newValue?.status?.dataKeyPath
142 | }
143 | storeTarget = newValue
144 | }
145 | }
146 |
147 | public var headers: HTTPHeaders?
148 |
149 | public var credential: URLCredential?
150 |
151 | ///custom Request
152 | public var urlRequest: URLRequestConvertible?
153 |
154 | /// The response's dataKey of the request
155 | public var dataKeyPath: String?
156 |
157 | public var enableLog: Bool {
158 | get {
159 | return storeEnableLog ?? target?.enableLog ?? true
160 | }
161 | set {
162 | storeEnableLog = newValue
163 | }
164 | }
165 |
166 | public var userInfo: Parameters?
167 |
168 | //MARK: - Private
169 | private var storeMethod: HTTPMethod?
170 |
171 | private var storeURLString: String?
172 |
173 | private var storeParameters: Parameters?
174 |
175 | private var storeParameterEncoding: ParameterEncoding?
176 |
177 | private var storeTarget: SLTarget?
178 |
179 | private var storeEnableLog: Bool?
180 |
181 | /// Pause the request.
182 | public func pause() {
183 | originalRequest?.suspend()
184 | }
185 |
186 | /// Cancel the request.
187 | public func cancel() {
188 | originalRequest?.cancel()
189 | }
190 |
191 | /// Resumes the request.
192 | public func resume() {
193 | originalRequest?.resume()
194 | }
195 | }
196 |
197 | extension SLRequest {
198 | @objc open var blackList: [String] {
199 | return []
200 | }
201 | }
202 |
203 | extension SLRequest: CustomDebugStringConvertible {
204 |
205 | public var debugDescription: String {
206 |
207 | var headersString: String? = "nil"
208 | var parametersString: String? = "nil"
209 |
210 | if let headers = headers?.dictionary {
211 | let headersData = try? JSONSerialization.data(withJSONObject: headers, options: [.prettyPrinted])
212 | if let data = headersData {
213 | headersString = String(data: data, encoding: .utf8)
214 | }
215 | }
216 | if let parameters = parameters {
217 | let parametersData = try? JSONSerialization.data(withJSONObject: parameters, options: [.prettyPrinted])
218 | parametersString = String(data: parametersData ?? Data(), encoding: .utf8)
219 | }
220 |
221 | return """
222 |
223 | ------------------------ SLRequest -----------------------
224 | URL:\(URLString)
225 | Headers:\(headersString!)
226 | Parameters:\(parametersString!)
227 | ----------------------------------------------------------
228 |
229 | """
230 | }
231 |
232 | }
233 |
234 | open class SLDownloadRequest: SLRequest {
235 |
236 | /// Specifies whether the download request is resume or not.
237 | public var isResume: Bool = false
238 |
239 | internal var hasResume: Bool = false
240 |
241 | /// Specify the destination URL to receive the file. default: "/Library/Caches/SLNetwork/Destination/\(requestID)"
242 | public var destinationURL: URL?
243 |
244 | public var options: Options = [.removePreviousFile, .createIntermediateDirectories]
245 |
246 | open override var blackList: [String] {
247 | return ["isResume", "hasResume"]
248 | }
249 |
250 | public override func cancel() {
251 | if isResume {
252 | if let downloadRequest = originalRequest as? DownloadRequest {
253 | downloadRequest.cancel(producingResumeData: true)
254 | return
255 | }
256 | }
257 |
258 | super.cancel()
259 | }
260 |
261 | }
262 |
263 | open class SLUploadRequest: SLRequest {
264 |
265 | public typealias MultipartFormDataClosure = (MultipartFormData) -> Void
266 |
267 | override open func loadRequest() {
268 | super.loadRequest()
269 | self.method = .post
270 | }
271 |
272 |
273 | /// uploading the `data`.
274 | public var data: Data?
275 |
276 | /// uploading the `file`.
277 | public var filePath: String?
278 |
279 | /// uploading the `inputStream`.
280 | public var inputStream: (intputStream: InputStream, length: Int)?
281 |
282 | /// uploading the `formData`.
283 | public var multipartFormData: MultipartFormDataClosure?
284 |
285 | public var encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold
286 |
287 | }
288 |
--------------------------------------------------------------------------------
/Source/Utility/SLResumeData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLResumeData.swift
3 | // SolarNetwork
4 | //
5 | // Created by wyhazq on 2019/9/23.
6 | //
7 | // Extend form Tiercel
8 |
9 | import UIKit
10 |
11 | private let NSURLSessionResumeInfoVersion: String = "NSURLSessionResumeInfoVersion"
12 | private let NSURLSessionResumeCurrentRequest: String = "NSURLSessionResumeCurrentRequest"
13 | private let NSURLSessionResumeOriginalRequest: String = "NSURLSessionResumeOriginalRequest"
14 | private let NSURLSessionResumeByteRange: String = "NSURLSessionResumeByteRange"
15 | private let NSURLSessionResumeInfoTempFileName: String = "NSURLSessionResumeInfoTempFileName"
16 | private let NSURLSessionResumeInfoLocalPath: String = "NSURLSessionResumeInfoLocalPath"
17 | private let NSURLSessionResumeBytesReceived: String = "NSURLSessionResumeBytesReceived"
18 |
19 | private let NSTemporaryPath: String = NSTemporaryDirectory()
20 |
21 |
22 | class SLResumeData {
23 |
24 | static func data(of url: URL) -> Data? {
25 | guard FileManager.sl.fileExists(at: url) else { return nil }
26 |
27 | do {
28 | let resumeData = try Data(contentsOf: url)
29 | if isValid(of: resumeData) {
30 | return handleResumeData(resumeData)
31 | }
32 |
33 | guard var resumeDict = dictionary(of: resumeData), let tempFileURL = tmpFileURL(of: resumeData), let tempFileData = tmpFileData(of: tempFileURL) else { return nil }
34 |
35 | // fix the resumeData after App crash or App close
36 | resumeDict[NSURLSessionResumeBytesReceived] = tempFileData.count
37 | guard let data = tmpFileData(of: resumeDict) else { return nil }
38 | return handleResumeData(data)
39 |
40 | } catch {
41 | debugPrint("ResumeDataInitError:\(error)")
42 | }
43 |
44 | return nil
45 | }
46 |
47 | static func tmpFileURL(of data: Data) -> URL? {
48 | guard let tmpFileName = tmpFileName(of: data) else { return nil }
49 |
50 | let tmpFilePath = NSTemporaryPath + tmpFileName
51 | guard FileManager.default.fileExists(atPath: tmpFilePath) else { return nil }
52 |
53 | return URL(fileURLWithPath: tmpFilePath)
54 | }
55 |
56 | static func dictionary(of data: Data) -> [String: Any]? {
57 | var dictionary: [String: Any]?
58 | if #available(OSX 10.11, iOS 9.0, *) {
59 | let keyedUnarchiver = NSKeyedUnarchiver(forReadingWith: data)
60 |
61 | do {
62 | if let dict = try keyedUnarchiver.decodeTopLevelObject(forKey: "NSKeyedArchiveRootObjectKey") as? [String: Any] {
63 | dictionary = dict
64 | }
65 | if dictionary == nil {
66 | if let dict = try keyedUnarchiver.decodeTopLevelObject(forKey: NSKeyedArchiveRootObjectKey) as? [String: Any] {
67 | dictionary = dict
68 | }
69 | }
70 | } catch {
71 | debugPrint("ResumeData-NSKeyedUnarchiverError:\(error)")
72 | }
73 | keyedUnarchiver.finishDecoding()
74 | }
75 |
76 | if dictionary == nil {
77 | do {
78 | var propertyListForamt = PropertyListSerialization.PropertyListFormat.xml
79 | if let dict = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: &propertyListForamt) as? [String: Any] {
80 | dictionary = dict
81 | }
82 | } catch {
83 | debugPrint("ResumeData-PropertyListSerializationError:\(error)")
84 | }
85 | }
86 |
87 | return dictionary
88 | }
89 |
90 | private static func handleResumeData(_ data: Data) -> Data? {
91 | if #available(iOS 11.3, *) {
92 | return data
93 | } else if #available(iOS 11.0, *) {
94 | // fix 11.0 - 11.2 bug
95 | return deleteResumeByteRange(data)
96 | } else if #available(iOS 10.2, *) {
97 | return data
98 | } else if #available(iOS 10.0, *) {
99 | // fix 10.0 - 10.1 bug
100 | return correctResumeData(data)
101 | } else {
102 | return data
103 | }
104 | }
105 |
106 | private static func tmpFileData(of url: URL) -> Data? {
107 | do {
108 | let tempFileData = try Data(contentsOf: url)
109 | return tempFileData
110 | } catch {
111 | debugPrint("TempFileDataInitError:\(error)")
112 | }
113 | return nil
114 | }
115 |
116 | private static func tmpFileData(of dictionary: [String : Any]) -> Data? {
117 | do {
118 | let propertyListForamt = PropertyListSerialization.PropertyListFormat.binary
119 | let resumeData: Data = try PropertyListSerialization.data(fromPropertyList: dictionary, format: propertyListForamt, options: 0)
120 | return resumeData
121 | }
122 | catch {
123 | debugPrint("PropertyListSerialization.dataError:\(error)")
124 | }
125 | return nil
126 | }
127 |
128 | private static func tmpFileName(of data: Data) -> String? {
129 | guard let resumeDictionary = dictionary(of: data), let version = resumeDictionary[NSURLSessionResumeInfoVersion] as? Int else { return nil }
130 | if version > 1 {
131 | return resumeDictionary[NSURLSessionResumeInfoTempFileName] as? String
132 | } else {
133 | guard let path = resumeDictionary[NSURLSessionResumeInfoLocalPath] as? String else { return nil }
134 | let url = URL(fileURLWithPath: path)
135 | return url.lastPathComponent
136 | }
137 | }
138 |
139 | /// fix 11.0 - 11.2 resumeData bug
140 | ///
141 | /// - Parameter data:
142 | /// - Returns:
143 | private class func deleteResumeByteRange(_ data: Data) -> Data? {
144 | guard var resumeDictionary = dictionary(of: data) else { return nil }
145 | resumeDictionary.removeValue(forKey: NSURLSessionResumeByteRange)
146 | let result = try? PropertyListSerialization.data(fromPropertyList: resumeDictionary, format: PropertyListSerialization.PropertyListFormat.xml, options: PropertyListSerialization.WriteOptions())
147 | return result
148 | }
149 |
150 | /// fix 10.0 - 10.1 resumeData bug: https://stackoverflow.com/questions/39346231/resume-nsurlsession-on-ios10/39347461#39347461
151 | ///
152 | /// - Parameter data:
153 | /// - Returns:
154 | private class func correctResumeData(_ data: Data) -> Data? {
155 | guard var resumeDictionary = dictionary(of: data) else { return nil }
156 |
157 | resumeDictionary[NSURLSessionResumeCurrentRequest] = correct(requestData: resumeDictionary[NSURLSessionResumeCurrentRequest] as? Data)
158 | resumeDictionary[NSURLSessionResumeOriginalRequest] = correct(requestData: resumeDictionary[NSURLSessionResumeOriginalRequest] as? Data)
159 |
160 | let result = try? PropertyListSerialization.data(fromPropertyList: resumeDictionary, format: PropertyListSerialization.PropertyListFormat.xml, options: PropertyListSerialization.WriteOptions())
161 | return result
162 | }
163 |
164 | private static func correct(requestData data: Data?) -> Data? {
165 | guard let data = data else {
166 | return nil
167 | }
168 | if NSKeyedUnarchiver.unarchiveObject(with: data) != nil {
169 | return data
170 | }
171 | guard let archive = (try? PropertyListSerialization.propertyList(from: data, options: [.mutableContainersAndLeaves], format: nil)) as? NSMutableDictionary else {
172 | return nil
173 | }
174 | // Rectify weird __nsurlrequest_proto_props objects to $number pattern
175 | var k = 0
176 | while ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "$\(k)") != nil {
177 | k += 1
178 | }
179 | var i = 0
180 | while ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_prop_obj_\(i)") != nil {
181 | let arr = archive["$objects"] as? NSMutableArray
182 | if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
183 | dic.setObject(obj, forKey: "$\(i + k)" as NSString)
184 | dic.removeObject(forKey: "__nsurlrequest_proto_prop_obj_\(i)")
185 | arr?[1] = dic
186 | archive["$objects"] = arr
187 | }
188 | i += 1
189 | }
190 | if ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_props") != nil {
191 | let arr = archive["$objects"] as? NSMutableArray
192 | if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
193 | dic.setObject(obj, forKey: "$\(i + k)" as NSString)
194 | dic.removeObject(forKey: "__nsurlrequest_proto_props")
195 | arr?[1] = dic
196 | archive["$objects"] = arr
197 | }
198 | }
199 |
200 | if let obj = (archive["$top"] as? NSMutableDictionary)?.object(forKey: "NSKeyedArchiveRootObjectKey") as AnyObject? {
201 | (archive["$top"] as? NSMutableDictionary)?.setObject(obj, forKey: NSKeyedArchiveRootObjectKey as NSString)
202 | (archive["$top"] as? NSMutableDictionary)?.removeObject(forKey: "NSKeyedArchiveRootObjectKey")
203 | }
204 | // Reencode archived object
205 | let result = try? PropertyListSerialization.data(fromPropertyList: archive, format: PropertyListSerialization.PropertyListFormat.binary, options: PropertyListSerialization.WriteOptions())
206 | return result
207 | }
208 |
209 | private static func count(ofNSURLSessionResumeBytesReceived data: Data) -> Int {
210 | guard let resumeDict = dictionary(of: data), let count = resumeDict[NSURLSessionResumeBytesReceived] as? Int else { return 0 }
211 | return count
212 | }
213 |
214 | private static func isValid(of data: Data) -> Bool {
215 | let dataCount = count(ofNSURLSessionResumeBytesReceived: data)
216 | guard let tmpFileURL = tmpFileURL(of: data), let tmpFileData = tmpFileData(of: tmpFileURL) else {
217 | return false
218 | }
219 | return tmpFileData.count == dataCount
220 | }
221 |
222 | }
223 |
224 |
--------------------------------------------------------------------------------
/SolarNetwork.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F83957572028654500286B8A /* SolarNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = F83957552028654500286B8A /* SolarNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | F83957652028667B00286B8A /* SLTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F839575E2028667B00286B8A /* SLTarget.swift */; };
12 | F83957662028667B00286B8A /* SLResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F839575F2028667B00286B8A /* SLResponse.swift */; };
13 | F83957672028667B00286B8A /* SLRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83957602028667B00286B8A /* SLRequest.swift */; };
14 | F83957682028667B00286B8A /* SLReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83957612028667B00286B8A /* SLReflection.swift */; };
15 | F83957692028667B00286B8A /* SLNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83957622028667B00286B8A /* SLNetwork.swift */; };
16 | F839576A2028667B00286B8A /* SLPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83957632028667B00286B8A /* SLPlugin.swift */; };
17 | F839576B2028667B00286B8A /* SLProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83957642028667B00286B8A /* SLProgress.swift */; };
18 | F839576F2029B11F00286B8A /* SLNetwork+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = F839576E2029B11F00286B8A /* SLNetwork+Alamofire.swift */; };
19 | F8395773202AD40700286B8A /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8395771202AD27A00286B8A /* Alamofire.framework */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | F83957522028654500286B8A /* SolarNetwork.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SolarNetwork.framework; sourceTree = BUILT_PRODUCTS_DIR; };
24 | F83957552028654500286B8A /* SolarNetwork.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SolarNetwork.h; sourceTree = ""; };
25 | F83957562028654500286B8A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | F839575E2028667B00286B8A /* SLTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLTarget.swift; sourceTree = ""; };
27 | F839575F2028667B00286B8A /* SLResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLResponse.swift; sourceTree = ""; };
28 | F83957602028667B00286B8A /* SLRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLRequest.swift; sourceTree = ""; };
29 | F83957612028667B00286B8A /* SLReflection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLReflection.swift; sourceTree = ""; };
30 | F83957622028667B00286B8A /* SLNetwork.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLNetwork.swift; sourceTree = ""; };
31 | F83957632028667B00286B8A /* SLPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLPlugin.swift; sourceTree = ""; };
32 | F83957642028667B00286B8A /* SLProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SLProgress.swift; sourceTree = ""; };
33 | F839576E2029B11F00286B8A /* SLNetwork+Alamofire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SLNetwork+Alamofire.swift"; sourceTree = ""; };
34 | F8395771202AD27A00286B8A /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | F839574E2028654500286B8A /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | F8395773202AD40700286B8A /* Alamofire.framework in Frameworks */,
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | F83957482028654500286B8A = {
50 | isa = PBXGroup;
51 | children = (
52 | F839575D2028667B00286B8A /* Source */,
53 | F83957542028654500286B8A /* SolarNetwork */,
54 | F83957532028654500286B8A /* Products */,
55 | F8395770202AD27900286B8A /* Frameworks */,
56 | );
57 | sourceTree = "";
58 | };
59 | F83957532028654500286B8A /* Products */ = {
60 | isa = PBXGroup;
61 | children = (
62 | F83957522028654500286B8A /* SolarNetwork.framework */,
63 | );
64 | name = Products;
65 | sourceTree = "";
66 | };
67 | F83957542028654500286B8A /* SolarNetwork */ = {
68 | isa = PBXGroup;
69 | children = (
70 | F83957552028654500286B8A /* SolarNetwork.h */,
71 | F83957562028654500286B8A /* Info.plist */,
72 | );
73 | path = SolarNetwork;
74 | sourceTree = "";
75 | };
76 | F839575D2028667B00286B8A /* Source */ = {
77 | isa = PBXGroup;
78 | children = (
79 | F839576E2029B11F00286B8A /* SLNetwork+Alamofire.swift */,
80 | F839575E2028667B00286B8A /* SLTarget.swift */,
81 | F839575F2028667B00286B8A /* SLResponse.swift */,
82 | F83957602028667B00286B8A /* SLRequest.swift */,
83 | F83957612028667B00286B8A /* SLReflection.swift */,
84 | F83957622028667B00286B8A /* SLNetwork.swift */,
85 | F83957632028667B00286B8A /* SLPlugin.swift */,
86 | F83957642028667B00286B8A /* SLProgress.swift */,
87 | );
88 | path = Source;
89 | sourceTree = "";
90 | };
91 | F8395770202AD27900286B8A /* Frameworks */ = {
92 | isa = PBXGroup;
93 | children = (
94 | F8395771202AD27A00286B8A /* Alamofire.framework */,
95 | );
96 | name = Frameworks;
97 | sourceTree = "";
98 | };
99 | /* End PBXGroup section */
100 |
101 | /* Begin PBXHeadersBuildPhase section */
102 | F839574F2028654500286B8A /* Headers */ = {
103 | isa = PBXHeadersBuildPhase;
104 | buildActionMask = 2147483647;
105 | files = (
106 | F83957572028654500286B8A /* SolarNetwork.h in Headers */,
107 | );
108 | runOnlyForDeploymentPostprocessing = 0;
109 | };
110 | /* End PBXHeadersBuildPhase section */
111 |
112 | /* Begin PBXNativeTarget section */
113 | F83957512028654500286B8A /* SolarNetwork */ = {
114 | isa = PBXNativeTarget;
115 | buildConfigurationList = F839575A2028654500286B8A /* Build configuration list for PBXNativeTarget "SolarNetwork" */;
116 | buildPhases = (
117 | F839574D2028654500286B8A /* Sources */,
118 | F839574E2028654500286B8A /* Frameworks */,
119 | F839574F2028654500286B8A /* Headers */,
120 | F83957502028654500286B8A /* Resources */,
121 | );
122 | buildRules = (
123 | );
124 | dependencies = (
125 | );
126 | name = SolarNetwork;
127 | productName = SolarNetwork;
128 | productReference = F83957522028654500286B8A /* SolarNetwork.framework */;
129 | productType = "com.apple.product-type.framework";
130 | };
131 | /* End PBXNativeTarget section */
132 |
133 | /* Begin PBXProject section */
134 | F83957492028654500286B8A /* Project object */ = {
135 | isa = PBXProject;
136 | attributes = {
137 | LastUpgradeCheck = 0920;
138 | ORGANIZATIONNAME = SolarKit;
139 | TargetAttributes = {
140 | F83957512028654500286B8A = {
141 | CreatedOnToolsVersion = 9.2;
142 | ProvisioningStyle = Automatic;
143 | };
144 | };
145 | };
146 | buildConfigurationList = F839574C2028654500286B8A /* Build configuration list for PBXProject "SolarNetwork" */;
147 | compatibilityVersion = "Xcode 8.0";
148 | developmentRegion = en;
149 | hasScannedForEncodings = 0;
150 | knownRegions = (
151 | en,
152 | );
153 | mainGroup = F83957482028654500286B8A;
154 | productRefGroup = F83957532028654500286B8A /* Products */;
155 | projectDirPath = "";
156 | projectRoot = "";
157 | targets = (
158 | F83957512028654500286B8A /* SolarNetwork */,
159 | );
160 | };
161 | /* End PBXProject section */
162 |
163 | /* Begin PBXResourcesBuildPhase section */
164 | F83957502028654500286B8A /* Resources */ = {
165 | isa = PBXResourcesBuildPhase;
166 | buildActionMask = 2147483647;
167 | files = (
168 | );
169 | runOnlyForDeploymentPostprocessing = 0;
170 | };
171 | /* End PBXResourcesBuildPhase section */
172 |
173 | /* Begin PBXSourcesBuildPhase section */
174 | F839574D2028654500286B8A /* Sources */ = {
175 | isa = PBXSourcesBuildPhase;
176 | buildActionMask = 2147483647;
177 | files = (
178 | F839576F2029B11F00286B8A /* SLNetwork+Alamofire.swift in Sources */,
179 | F83957652028667B00286B8A /* SLTarget.swift in Sources */,
180 | F839576B2028667B00286B8A /* SLProgress.swift in Sources */,
181 | F83957692028667B00286B8A /* SLNetwork.swift in Sources */,
182 | F83957672028667B00286B8A /* SLRequest.swift in Sources */,
183 | F83957662028667B00286B8A /* SLResponse.swift in Sources */,
184 | F83957682028667B00286B8A /* SLReflection.swift in Sources */,
185 | F839576A2028667B00286B8A /* SLPlugin.swift in Sources */,
186 | );
187 | runOnlyForDeploymentPostprocessing = 0;
188 | };
189 | /* End PBXSourcesBuildPhase section */
190 |
191 | /* Begin XCBuildConfiguration section */
192 | F83957582028654500286B8A /* Debug */ = {
193 | isa = XCBuildConfiguration;
194 | buildSettings = {
195 | ALWAYS_SEARCH_USER_PATHS = NO;
196 | CLANG_ANALYZER_NONNULL = YES;
197 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
199 | CLANG_CXX_LIBRARY = "libc++";
200 | CLANG_ENABLE_MODULES = YES;
201 | CLANG_ENABLE_OBJC_ARC = YES;
202 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
203 | CLANG_WARN_BOOL_CONVERSION = YES;
204 | CLANG_WARN_COMMA = YES;
205 | CLANG_WARN_CONSTANT_CONVERSION = YES;
206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
207 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
208 | CLANG_WARN_EMPTY_BODY = YES;
209 | CLANG_WARN_ENUM_CONVERSION = YES;
210 | CLANG_WARN_INFINITE_RECURSION = YES;
211 | CLANG_WARN_INT_CONVERSION = YES;
212 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
213 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
214 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
215 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
216 | CLANG_WARN_STRICT_PROTOTYPES = YES;
217 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
218 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
219 | CLANG_WARN_UNREACHABLE_CODE = YES;
220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
221 | CODE_SIGN_IDENTITY = "iPhone Developer";
222 | COPY_PHASE_STRIP = NO;
223 | CURRENT_PROJECT_VERSION = 1;
224 | DEBUG_INFORMATION_FORMAT = dwarf;
225 | ENABLE_STRICT_OBJC_MSGSEND = YES;
226 | ENABLE_TESTABILITY = YES;
227 | GCC_C_LANGUAGE_STANDARD = gnu11;
228 | GCC_DYNAMIC_NO_PIC = NO;
229 | GCC_NO_COMMON_BLOCKS = YES;
230 | GCC_OPTIMIZATION_LEVEL = 0;
231 | GCC_PREPROCESSOR_DEFINITIONS = (
232 | "DEBUG=1",
233 | "$(inherited)",
234 | );
235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
237 | GCC_WARN_UNDECLARED_SELECTOR = YES;
238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
239 | GCC_WARN_UNUSED_FUNCTION = YES;
240 | GCC_WARN_UNUSED_VARIABLE = YES;
241 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
242 | MTL_ENABLE_DEBUG_INFO = YES;
243 | ONLY_ACTIVE_ARCH = YES;
244 | SDKROOT = iphoneos;
245 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
246 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
247 | VERSIONING_SYSTEM = "apple-generic";
248 | VERSION_INFO_PREFIX = "";
249 | };
250 | name = Debug;
251 | };
252 | F83957592028654500286B8A /* Release */ = {
253 | isa = XCBuildConfiguration;
254 | buildSettings = {
255 | ALWAYS_SEARCH_USER_PATHS = NO;
256 | CLANG_ANALYZER_NONNULL = YES;
257 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
259 | CLANG_CXX_LIBRARY = "libc++";
260 | CLANG_ENABLE_MODULES = YES;
261 | CLANG_ENABLE_OBJC_ARC = YES;
262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_COMMA = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
268 | CLANG_WARN_EMPTY_BODY = YES;
269 | CLANG_WARN_ENUM_CONVERSION = YES;
270 | CLANG_WARN_INFINITE_RECURSION = YES;
271 | CLANG_WARN_INT_CONVERSION = YES;
272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
273 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
274 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
275 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
276 | CLANG_WARN_STRICT_PROTOTYPES = YES;
277 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
278 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
279 | CLANG_WARN_UNREACHABLE_CODE = YES;
280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
281 | CODE_SIGN_IDENTITY = "iPhone Developer";
282 | COPY_PHASE_STRIP = NO;
283 | CURRENT_PROJECT_VERSION = 1;
284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
285 | ENABLE_NS_ASSERTIONS = NO;
286 | ENABLE_STRICT_OBJC_MSGSEND = YES;
287 | GCC_C_LANGUAGE_STANDARD = gnu11;
288 | GCC_NO_COMMON_BLOCKS = YES;
289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
291 | GCC_WARN_UNDECLARED_SELECTOR = YES;
292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
293 | GCC_WARN_UNUSED_FUNCTION = YES;
294 | GCC_WARN_UNUSED_VARIABLE = YES;
295 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
296 | MTL_ENABLE_DEBUG_INFO = NO;
297 | SDKROOT = iphoneos;
298 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
299 | VALIDATE_PRODUCT = YES;
300 | VERSIONING_SYSTEM = "apple-generic";
301 | VERSION_INFO_PREFIX = "";
302 | };
303 | name = Release;
304 | };
305 | F839575B2028654500286B8A /* Debug */ = {
306 | isa = XCBuildConfiguration;
307 | buildSettings = {
308 | CODE_SIGN_IDENTITY = "";
309 | CODE_SIGN_STYLE = Automatic;
310 | DEFINES_MODULE = YES;
311 | DYLIB_COMPATIBILITY_VERSION = 1;
312 | DYLIB_CURRENT_VERSION = 1;
313 | DYLIB_INSTALL_NAME_BASE = "@rpath";
314 | FRAMEWORK_SEARCH_PATHS = (
315 | "$(inherited)",
316 | "$(PROJECT_DIR)/Carthage/Build/iOS",
317 | );
318 | INFOPLIST_FILE = SolarNetwork/Info.plist;
319 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
320 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
321 | OTHER_CFLAGS = "-fembed-bitcode";
322 | OTHER_LDFLAGS = "-all_load";
323 | PRODUCT_BUNDLE_IDENTIFIER = com.SolarKit.SolarNetwork;
324 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
325 | SKIP_INSTALL = YES;
326 | SWIFT_VERSION = 4.0;
327 | TARGETED_DEVICE_FAMILY = "1,2";
328 | };
329 | name = Debug;
330 | };
331 | F839575C2028654500286B8A /* Release */ = {
332 | isa = XCBuildConfiguration;
333 | buildSettings = {
334 | CODE_SIGN_IDENTITY = "";
335 | CODE_SIGN_STYLE = Automatic;
336 | DEFINES_MODULE = YES;
337 | DYLIB_COMPATIBILITY_VERSION = 1;
338 | DYLIB_CURRENT_VERSION = 1;
339 | DYLIB_INSTALL_NAME_BASE = "@rpath";
340 | FRAMEWORK_SEARCH_PATHS = (
341 | "$(inherited)",
342 | "$(PROJECT_DIR)/Carthage/Build/iOS",
343 | );
344 | INFOPLIST_FILE = SolarNetwork/Info.plist;
345 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
346 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
347 | OTHER_CFLAGS = "-fembed-bitcode";
348 | OTHER_LDFLAGS = "-all_load";
349 | PRODUCT_BUNDLE_IDENTIFIER = com.SolarKit.SolarNetwork;
350 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
351 | SKIP_INSTALL = YES;
352 | SWIFT_VERSION = 4.0;
353 | TARGETED_DEVICE_FAMILY = "1,2";
354 | };
355 | name = Release;
356 | };
357 | /* End XCBuildConfiguration section */
358 |
359 | /* Begin XCConfigurationList section */
360 | F839574C2028654500286B8A /* Build configuration list for PBXProject "SolarNetwork" */ = {
361 | isa = XCConfigurationList;
362 | buildConfigurations = (
363 | F83957582028654500286B8A /* Debug */,
364 | F83957592028654500286B8A /* Release */,
365 | );
366 | defaultConfigurationIsVisible = 0;
367 | defaultConfigurationName = Release;
368 | };
369 | F839575A2028654500286B8A /* Build configuration list for PBXNativeTarget "SolarNetwork" */ = {
370 | isa = XCConfigurationList;
371 | buildConfigurations = (
372 | F839575B2028654500286B8A /* Debug */,
373 | F839575C2028654500286B8A /* Release */,
374 | );
375 | defaultConfigurationIsVisible = 0;
376 | defaultConfigurationName = Release;
377 | };
378 | /* End XCConfigurationList section */
379 | };
380 | rootObject = F83957492028654500286B8A /* Project object */;
381 | }
382 |
--------------------------------------------------------------------------------
/Source/SLNetwork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SLNetwork.swift
3 | //
4 | // Created by wyhazq on 2018/1/9.
5 | // Copyright © 2018年 SolarNetwork. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import Alamofire
28 |
29 | private let SLNetworkResponseQueue: String = "com.SLNetwork.ResponseQueue"
30 |
31 | private let SLNetworkFolderPath: String = "SLNetwork"
32 | private let SLNetworkDestinationFolderPath: String = "Destination"
33 | private let SLNetworkResumeFolderPath: String = "Resume"
34 |
35 | public class SLNetwork {
36 |
37 | public typealias ProgressClosure = (SLProgress) -> Void
38 | public typealias CompletionClosure = (SLResponse) -> Void
39 |
40 | // MARK: - Properties
41 |
42 | /// The target's SessionManager
43 | public let session: Session
44 |
45 | /// The target of a host
46 | public var target: SLTarget
47 |
48 | /// The target's reachabilityManager
49 | public lazy var reachabilityManager: NetworkReachabilityManager? = {
50 | let reachabilityManager = NetworkReachabilityManager(host: self.target.host)
51 | return reachabilityManager
52 | }()
53 |
54 | private var serverTrustManager: ServerTrustManager?
55 | private lazy var responseQueue = { return DispatchQueue(label: SLNetworkResponseQueue) }()
56 |
57 | private lazy var SLNetworkFolderURL: URL = { return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent(SLNetworkFolderPath) }()
58 | private lazy var SLNetworkDestinationFolderURL: URL = { return SLNetworkFolderURL.appendingPathComponent(SLNetworkDestinationFolderPath) }()
59 | private lazy var SLNetworkResumeFolderURL: URL = { return SLNetworkFolderURL.appendingPathComponent(SLNetworkResumeFolderPath) }()
60 |
61 |
62 | // MARK: - Lifecycle
63 | public init(_ target: SLTarget) {
64 | self.target = target
65 |
66 | let configuration = target.configuration
67 | if configuration.httpAdditionalHeaders == nil {
68 | configuration.headers = HTTPHeaders.default
69 | }
70 |
71 | var trustManager: ServerTrustManager?
72 | if let serverEvaluators = target.serverEvaluators {
73 | trustManager = ServerTrustManager(allHostsMustBeEvaluated: target.allHostsMustBeEvaluated, evaluators: serverEvaluators)
74 | self.serverTrustManager = trustManager
75 | }
76 |
77 | self.session = Session(configuration: configuration, delegate: SLSessionDelegate(), serverTrustManager: serverTrustManager)
78 |
79 | self.handleChallenge()
80 |
81 | if let reachabilityListener = target.reachabilityListener {
82 | self.reachabilityManager?.startListening(onUpdatePerforming: reachabilityListener)
83 | }
84 |
85 | }
86 | }
87 |
88 | extension SLNetwork {
89 |
90 | private func handleChallenge () {
91 | if let delegate = session.delegate as? SLSessionDelegate {
92 | delegate.taskDidReceiveChallenge = { [weak self] (session, task, challenge) in
93 | guard let strongSelf = self else { return (.performDefaultHandling, nil) }
94 |
95 | if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
96 | return strongSelf.serverTrust(session: session, challenge: challenge)
97 | }
98 | else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
99 | return strongSelf.clientTrust(session: session, challenge: challenge)
100 | }
101 |
102 | return (.performDefaultHandling, nil)
103 | }
104 | }
105 | }
106 |
107 | private func serverTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
108 | var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
109 | var credential: URLCredential?
110 |
111 | if let trustManager = self.serverTrustManager {
112 | let host = challenge.protectionSpace.host.sl.isIP ? self.target.host : challenge.protectionSpace.host
113 |
114 | do {
115 | if let serverTrustEvaluator = try trustManager.serverTrustEvaluator(forHost: host), let serverTrust = challenge.protectionSpace.serverTrust {
116 | do {
117 | try serverTrustEvaluator.evaluate(serverTrust, forHost: host)
118 | disposition = .useCredential
119 | credential = URLCredential(trust: serverTrust)
120 | }
121 | catch {
122 | disposition = .cancelAuthenticationChallenge
123 | debugPrint("ServerTrustError:\(error)")
124 | }
125 | }
126 | }
127 | catch {
128 | debugPrint("ServerTrustError:\(error)")
129 | }
130 |
131 | }
132 |
133 | return (disposition, credential)
134 | }
135 |
136 | private func clientTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
137 | var disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
138 | var credential: URLCredential?
139 |
140 | if let (secPKCS12Path, password) = self.target.clientTrustPolicy {
141 |
142 | guard let PKCS12Data = NSData(contentsOfFile: secPKCS12Path) else {
143 | return (disposition, credential)
144 | }
145 |
146 | let key = kSecImportExportPassphrase as NSString
147 | let options : NSDictionary = [key : password]
148 |
149 | var items: CFArray?
150 | let error = SecPKCS12Import(PKCS12Data, options, &items)
151 |
152 | if error == errSecSuccess {
153 | if let itemArr = items as NSArray?, let item = itemArr.firstObject as? Dictionary {
154 | let identityPointer = item[kSecImportItemIdentity as String];
155 | let secIdentityRef = identityPointer as! SecIdentity
156 |
157 | let chainPointer = item[kSecImportItemCertChain as String]
158 | let chainRef = chainPointer as? [Any]
159 |
160 | disposition = .useCredential
161 | credential = URLCredential(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
162 | }
163 | }
164 | }
165 |
166 | return (disposition, credential)
167 | }
168 | }
169 |
170 | extension SLNetwork {
171 |
172 | // MARK: - Data Request
173 |
174 | /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the specified SLRequest.
175 | ///
176 | /// - Parameters:
177 | /// - request: SLRequest
178 | /// - completionClosure: CompletionClosure
179 | public func request(_ request: SLRequest, completionClosure: @escaping CompletionClosure) {
180 | request.target = target
181 |
182 | if request.enableLog { debugPrint(request) }
183 |
184 | willSend(request: request)
185 |
186 | let dataRequest: DataRequest
187 |
188 | if let urlRequest = request.urlRequest {
189 | dataRequest = session.request(urlRequest)
190 | }
191 | else {
192 | dataRequest = session.request(request.URLString, method: request.method, parameters: request.parameters, encoding: request.parameterEncoding, headers: request.headers)
193 | }
194 |
195 | if let credential = request.credential {
196 | dataRequest.authenticate(with: credential)
197 | }
198 |
199 | dataRequest.responseData(queue: target.responseQueue ?? responseQueue) { [weak self] (originalResponse) in
200 | guard let strongSelf = self else { return }
201 |
202 | strongSelf.dealResponseOfDataRequest(request: request, originalResponse: originalResponse, completionClosure: completionClosure)
203 |
204 | }
205 | request.originalRequest = dataRequest
206 | }
207 |
208 | }
209 |
210 | extension SLNetwork {
211 |
212 | // MARK: - Download
213 |
214 | /// Creates a `DownloadRequest` using the `SessionManager` to retrieve the contents of a URL based on the specified `urlRequest` and save them to the `destination`.
215 | ///
216 | /// - Parameters:
217 | /// - request: SLDownloadRequest
218 | /// - progressClosure: ProgressClosure
219 | /// - completionClosure: CompletionClosure
220 | public func download(_ request: SLDownloadRequest, progressClosure: ProgressClosure? = nil, completionClosure: @escaping CompletionClosure) {
221 | request.target = target
222 |
223 | if request.enableLog { debugPrint(request) }
224 |
225 | willSend(request: request)
226 |
227 | let downloadRequest: DownloadRequest
228 |
229 | if let urlRequest = request.urlRequest {
230 | downloadRequest = session.download(urlRequest)
231 | downloadResponse(with: request, downloadRequest: downloadRequest, progressClosure: progressClosure, completionClosure: completionClosure)
232 |
233 | return;
234 | }
235 |
236 | let destinationURL = request.destinationURL ?? SLNetworkDestinationFolderURL.appendingPathComponent(request.requestID)
237 | let destination: Destination = { _, _ in
238 | return (destinationURL, request.options)
239 | }
240 |
241 | if request.isResume {
242 | let resumeDataURL = SLNetworkResumeFolderURL.appendingPathComponent(request.requestID)
243 | if let resumeData = SLResumeData.data(of: resumeDataURL) {
244 | downloadRequest = session.download(resumingWith: resumeData, to: destination)
245 | downloadResponse(with: request, downloadRequest: downloadRequest, progressClosure: progressClosure, completionClosure: completionClosure)
246 | guard #available(iOS 10.2, *) else { return }
247 | // fix 10.0 - 10.1 resumeData bug:
248 | session.requestQueue.async { [weak self] in
249 | guard let strongSelf = self else { return }
250 | strongSelf.session.rootQueue.async {
251 | if let task = downloadRequest.task {
252 | task.sl.fixiOS10Task(with: resumeData)
253 | }
254 | }
255 | }
256 | return
257 | }
258 | }
259 |
260 | downloadRequest = session.download(request.URLString, method: request.method, parameters: request.parameters, encoding: request.parameterEncoding, headers: request.headers, to: destination)
261 | downloadResponse(with: request, downloadRequest: downloadRequest, progressClosure: progressClosure, completionClosure: completionClosure)
262 | }
263 |
264 | private func downloadResponse(with request:SLDownloadRequest, downloadRequest: DownloadRequest, progressClosure: ProgressClosure? = nil, completionClosure: @escaping CompletionClosure) {
265 |
266 | let resumeDataURL = SLNetworkResumeFolderURL.appendingPathComponent(request.requestID)
267 |
268 | if let credential = request.credential {
269 | downloadRequest.authenticate(with: credential)
270 | }
271 |
272 | var totalUnitCount: Int64 = 0
273 | var progress: SLProgress?
274 | if let _ = progressClosure {
275 | progress = SLProgress(request: request)
276 | }
277 | downloadRequest.downloadProgress { (originalProgress) in
278 | if request.isResume && !FileManager.sl.fileExists(at: resumeDataURL) && originalProgress.fractionCompleted < 0.99 {
279 | request.cancel()
280 | }
281 |
282 | if totalUnitCount != originalProgress.totalUnitCount {
283 | totalUnitCount = originalProgress.totalUnitCount
284 | }
285 | if let progressClosure = progressClosure, let progress = progress {
286 | progress.originalProgress = originalProgress
287 | if request.enableLog { debugPrint(progress) }
288 | progressClosure(progress)
289 | }
290 | }
291 |
292 | downloadRequest.responseData(queue: target.responseQueue ?? responseQueue) { [weak self] (originalResponse) in
293 | guard let strongSelf = self else { return }
294 |
295 | let response = SLResponse(request: request, urlRequest: originalResponse.request, httpURLResponse: originalResponse.response)
296 |
297 | switch originalResponse.result {
298 | case .failure(let error):
299 | response.error = error as NSError
300 |
301 | if request.isResume {
302 |
303 | if let errorCode = response.error?.code, errorCode == NSURLErrorCancelled || error.isExplicitlyCancelledError {
304 | FileManager.sl.createDirectory(at: strongSelf.SLNetworkResumeFolderURL, withIntermediateDirectories: true)
305 |
306 | do {
307 | if !FileManager.sl.fileExists(at: resumeDataURL) {
308 | try originalResponse.resumeData?.write(to: resumeDataURL)
309 | DispatchQueue.main.async {
310 | strongSelf.download(request, progressClosure: progressClosure, completionClosure: completionClosure)
311 | }
312 | return
313 | }
314 |
315 | FileManager.sl.removeItem(at: resumeDataURL)
316 | try originalResponse.resumeData?.write(to: resumeDataURL)
317 | if request.enableLog {
318 | debugPrint("\n------------------------ SLResponse ----------------------\n URL:\(request.URLString) \nresumeData has been writed to: \n\(resumeDataURL.absoluteString)\n ----------------------------------------------------------\n")
319 | }
320 | }
321 | catch {
322 | debugPrint("ResumeDataWriteError:\(error)")
323 | }
324 | }
325 | else {
326 | FileManager.sl.removeItem(at: resumeDataURL)
327 |
328 | if let resumeData = originalResponse.resumeData, let tempFileURL = SLResumeData.tmpFileURL(of: resumeData) {
329 | FileManager.sl.removeItem(at: tempFileURL)
330 | }
331 |
332 | if !request.hasResume {
333 | DispatchQueue.main.async {
334 | strongSelf.download(request, progressClosure: progressClosure, completionClosure: completionClosure)
335 | }
336 | request.hasResume = true
337 | return
338 | }
339 | }
340 | }
341 |
342 | case .success(let data):
343 | if request.isResume {
344 | FileManager.sl.removeItem(at: resumeDataURL)
345 | if Int64(data.count) == totalUnitCount || totalUnitCount == 0 {
346 | response.originData = data
347 | response.fileURL = originalResponse.fileURL
348 | }
349 | else {
350 | let error = NSError(domain: strongSelf.target.host, code: NSURLErrorCannotOpenFile, userInfo: [NSLocalizedDescriptionKey : "File is damaged."])
351 | response.error = error
352 |
353 | if let fileURL = originalResponse.fileURL {
354 | FileManager.sl.removeItem(at: fileURL)
355 | }
356 | }
357 | }
358 | else {
359 | response.originData = data
360 | response.fileURL = originalResponse.fileURL
361 | }
362 |
363 | }
364 |
365 | strongSelf.didReceive(response: response)
366 |
367 | if request.enableLog { debugPrint(response) }
368 |
369 | DispatchQueue.main.async {
370 | completionClosure(response)
371 |
372 | request.originalRequest = nil
373 | }
374 | }
375 |
376 | request.originalRequest = downloadRequest
377 | }
378 | }
379 |
380 | extension SLNetwork {
381 |
382 | // MARK: - Upload
383 |
384 | /// Creates an `UploadRequest` using the `SessionManager` from the specified SLUploadRequest.
385 | ///
386 | /// - Parameters:
387 | /// - request: SLUploadRequest
388 | /// - progressClosure: ProgressClosure
389 | /// - completionClosure: CompletionClosure
390 | public func upload(_ request: SLUploadRequest, progressClosure: ProgressClosure? = nil, completionClosure: @escaping CompletionClosure) {
391 | request.target = target
392 |
393 | if request.enableLog { debugPrint(request) }
394 |
395 | willSend(request: request)
396 |
397 | let uploadRequest: UploadRequest
398 |
399 | if let filePath = request.filePath, let fileURL = URL(string: filePath) {
400 |
401 | if let urlRequest = request.urlRequest {
402 | uploadRequest = session.upload(fileURL, with: urlRequest)
403 | }
404 | else {
405 | uploadRequest = session.upload(fileURL, to: request.URLString, method: request.method, headers: request.headers)
406 | }
407 |
408 | }
409 | else if let data = request.data {
410 |
411 | if let urlRequest = request.urlRequest {
412 | uploadRequest = session.upload(data, with: urlRequest)
413 | }
414 | else {
415 | uploadRequest = session.upload(data, to: request.URLString, method: request.method, headers: request.headers)
416 | }
417 |
418 | }
419 | else if let inputStream = request.inputStream {
420 |
421 | if request.headers == nil {
422 | request.headers = HTTPHeaders(["Content-Length" : "\(inputStream.length)"])
423 | }
424 | else {
425 | request.headers?.update(name: "Content-Length", value: "\(inputStream.length)")
426 | }
427 |
428 | if let urlRequest = request.urlRequest {
429 | uploadRequest = session.upload(inputStream.intputStream, with: urlRequest)
430 | }
431 | else {
432 | uploadRequest = session.upload(inputStream.intputStream, to: request.URLString, method: request.method, headers: request.headers)
433 | }
434 |
435 | }
436 | else if let multipartFormData = request.multipartFormData {
437 |
438 | if let urlRequest = request.urlRequest {
439 | uploadRequest = session.upload(multipartFormData: multipartFormData, with: urlRequest)
440 |
441 | }
442 | else {
443 | uploadRequest = session.upload(multipartFormData: multipartFormData, to: request.URLString, usingThreshold: request.encodingMemoryThreshold, method: request.method, headers: request.headers)
444 | }
445 |
446 | }
447 | else { return }
448 | uploadResponse(with: request, uploadRequest: uploadRequest, progressClosure:progressClosure, completionClosure: completionClosure)
449 | }
450 |
451 | private func uploadResponse(with request:SLRequest, uploadRequest: UploadRequest, progressClosure: ProgressClosure? = nil, completionClosure: @escaping CompletionClosure) {
452 |
453 | if let credential = request.credential {
454 | uploadRequest.authenticate(with: credential)
455 | }
456 |
457 | var progress: SLProgress?
458 | if let _ = progressClosure {
459 | progress = SLProgress(request: request)
460 | }
461 | uploadRequest.uploadProgress(closure: { (originalProgress) in
462 | if let progressClosure = progressClosure, let progress = progress {
463 | progress.originalProgress = originalProgress
464 | if request.enableLog { debugPrint(progress) }
465 | progressClosure(progress)
466 | }
467 | })
468 |
469 | uploadRequest.responseData(queue: target.responseQueue ?? responseQueue) { [weak self] (originalResponse) in
470 | guard let strongSelf = self else { return }
471 |
472 | strongSelf.dealResponseOfDataRequest(request: request, originalResponse: originalResponse, completionClosure: completionClosure)
473 |
474 | }
475 |
476 | request.originalRequest = uploadRequest
477 | }
478 |
479 | }
480 |
481 | extension SLNetwork {
482 | // MARK: - Convenience Method
483 |
484 | public func stopReachabilityListening() {
485 | reachabilityManager?.stopListening()
486 | }
487 |
488 | }
489 |
490 | extension SLNetwork {
491 |
492 | // MARK: - Response
493 | private func dealResponseOfDataRequest(request: SLRequest, originalResponse: AFDataResponse, completionClosure: @escaping CompletionClosure) {
494 |
495 | let response = SLResponse(request: request, urlRequest: originalResponse.request, httpURLResponse: originalResponse.response)
496 |
497 | switch originalResponse.result {
498 | case .failure(let error):
499 | response.error = error as NSError
500 |
501 | case .success(let data):
502 | response.originData = data
503 | }
504 |
505 | didReceive(response: response)
506 |
507 | toJsonObject(response: response)
508 |
509 | decode(request: request, response: response)
510 |
511 | if request.enableLog { debugPrint(response) }
512 |
513 | DispatchQueue.main.async {
514 | completionClosure(response)
515 |
516 | request.originalRequest = nil
517 | }
518 |
519 | }
520 |
521 | private func willSend(request: SLRequest) {
522 | if let plugins = target.plugins {
523 | plugins.forEach { $0.willSend(request: request) }
524 | }
525 | }
526 |
527 | private func didReceive(response: SLResponse) {
528 | if Thread.isMainThread {
529 | if let plugins = target.plugins {
530 | plugins.forEach { $0.didReceive(response: response) }
531 | }
532 | }
533 | else {
534 | DispatchQueue.main.sync {
535 | if let plugins = target.plugins {
536 | plugins.forEach { $0.didReceive(response: response) }
537 | }
538 | }
539 | }
540 | }
541 |
542 | private func toJsonObject(response: SLResponse) {
543 | guard let data = response.originData else { return }
544 |
545 | do {
546 | response.data = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
547 | }
548 | catch {
549 | if let dataString = String(data: data, encoding: .utf8) {
550 | response.data = dataString
551 | return
552 | }
553 | response.error = error as NSError
554 | }
555 | }
556 |
557 | private func decode(request:SLRequest, response: SLResponse) {
558 | guard let status = target.status, let dictionary = response.data as? [String: Any] else { return }
559 |
560 | let statusValue = dictionary[status.codeKey] as! Int
561 | if let messageKey = status.messageKey {
562 | response.message = dictionary[messageKey] as? String
563 | }
564 | if statusValue == status.successCode {
565 | if let dataKeyPath = request.dataKeyPath {
566 | if let dataObject = (dictionary as AnyObject).value(forKeyPath: dataKeyPath) {
567 | response.data = dataObject
568 | }
569 | }
570 | }
571 | else {
572 | let error = NSError(domain: target.host, code: statusValue, userInfo: [NSLocalizedDescriptionKey : response.message ?? ""])
573 | response.error = error
574 | }
575 | }
576 |
577 | }
578 |
--------------------------------------------------------------------------------
/SolarNetworkExample/SolarNetworkExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3B4170FC213E55C5005302F4 /* HTTPBinStringParametersRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4170FB213E55C5005302F4 /* HTTPBinStringParametersRequest.swift */; };
11 | 3B4170FE213E634D005302F4 /* HTTPBinArrayParametersRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4170FD213E634D005302F4 /* HTTPBinArrayParametersRequest.swift */; };
12 | 3B85FCE62238E306003E1590 /* PodRelease.sh in Resources */ = {isa = PBXBuildFile; fileRef = 3B85FCE52238E305003E1590 /* PodRelease.sh */; };
13 | 3BAE9C002145FEBB006731B1 /* github.p12 in Resources */ = {isa = PBXBuildFile; fileRef = 3BAE9BFF2145FEBB006731B1 /* github.p12 */; };
14 | A6921FFADB2B69B3AF6389F6 /* Pods_SolarNetworkExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91171F28195C0598BA46A1B7 /* Pods_SolarNetworkExample.framework */; };
15 | F892D9EF202203D7007536ED /* GitHubTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E3202203D7007536ED /* GitHubTarget.swift */; };
16 | F892D9F0202203D7007536ED /* GitHubSignoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E5202203D7007536ED /* GitHubSignoutRequest.swift */; };
17 | F892D9F1202203D7007536ED /* GitHubAPIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E6202203D7007536ED /* GitHubAPIRequest.swift */; };
18 | F892D9F2202203D7007536ED /* GitHubDownloadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E7202203D7007536ED /* GitHubDownloadRequest.swift */; };
19 | F892D9F3202203D7007536ED /* GitHubMyInfoRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E8202203D7007536ED /* GitHubMyInfoRequest.swift */; };
20 | F892D9F4202203D7007536ED /* GitHubSigninRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9E9202203D7007536ED /* GitHubSigninRequest.swift */; };
21 | F892D9F5202203D7007536ED /* GitHubUserInfoRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9EA202203D7007536ED /* GitHubUserInfoRequest.swift */; };
22 | F892D9F6202203D7007536ED /* GitHubPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9EC202203D7007536ED /* GitHubPlugin.swift */; };
23 | F892D9F7202203D7007536ED /* GitHubAuthenticationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9EE202203D7007536ED /* GitHubAuthenticationModel.swift */; };
24 | F892DA0220220433007536ED /* HTTPBinTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9F920220433007536ED /* HTTPBinTarget.swift */; };
25 | F892DA0320220433007536ED /* HTTPBinGETRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9FB20220433007536ED /* HTTPBinGETRequest.swift */; };
26 | F892DA0420220433007536ED /* HTTPBinUploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9FC20220433007536ED /* HTTPBinUploadRequest.swift */; };
27 | F892DA0520220433007536ED /* HTTPBinPUTRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9FD20220433007536ED /* HTTPBinPUTRequest.swift */; };
28 | F892DA0620220433007536ED /* HTTPBinDownLoadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9FE20220433007536ED /* HTTPBinDownLoadRequest.swift */; };
29 | F892DA0720220433007536ED /* HTTPBinPATCHRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892D9FF20220433007536ED /* HTTPBinPATCHRequest.swift */; };
30 | F892DA0820220433007536ED /* HTTPBinPOSTRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892DA0020220433007536ED /* HTTPBinPOSTRequest.swift */; };
31 | F892DA0920220433007536ED /* HTTPBinDELETERequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F892DA0120220433007536ED /* HTTPBinDELETERequest.swift */; };
32 | F892DA0E2022A616007536ED /* github.cer in Resources */ = {isa = PBXBuildFile; fileRef = F892DA0D2022A616007536ED /* github.cer */; };
33 | F892DA102022A622007536ED /* SLNetwork.png in Resources */ = {isa = PBXBuildFile; fileRef = F892DA0F2022A622007536ED /* SLNetwork.png */; };
34 | F8AFC60D2021F5EA00A3F595 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFC60C2021F5EA00A3F595 /* AppDelegate.swift */; };
35 | F8AFC6122021F5EA00A3F595 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8AFC6102021F5EA00A3F595 /* Main.storyboard */; };
36 | F8AFC6142021F5EA00A3F595 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8AFC6132021F5EA00A3F595 /* Assets.xcassets */; };
37 | F8AFC6172021F5EA00A3F595 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8AFC6152021F5EA00A3F595 /* LaunchScreen.storyboard */; };
38 | F8AFC6392021F6A100A3F595 /* HTTPBin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFC6232021F6A100A3F595 /* HTTPBin.swift */; };
39 | F8AFC63B2021F6A100A3F595 /* HTTPBinVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFC6252021F6A100A3F595 /* HTTPBinVC.swift */; };
40 | F8AFC6482021F6A100A3F595 /* GitHubVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFC6332021F6A100A3F595 /* GitHubVC.swift */; };
41 | F8AFC64B2021F6A100A3F595 /* GitHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AFC6362021F6A100A3F595 /* GitHub.swift */; };
42 | /* End PBXBuildFile section */
43 |
44 | /* Begin PBXFileReference section */
45 | 1AAC6A72AF16BEE0789C1E0C /* Pods-SolarNetworkExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarNetworkExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-SolarNetworkExample/Pods-SolarNetworkExample.release.xcconfig"; sourceTree = ""; };
46 | 3B4170FB213E55C5005302F4 /* HTTPBinStringParametersRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPBinStringParametersRequest.swift; sourceTree = ""; };
47 | 3B4170FD213E634D005302F4 /* HTTPBinArrayParametersRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPBinArrayParametersRequest.swift; sourceTree = ""; };
48 | 3B85FCE52238E305003E1590 /* PodRelease.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = PodRelease.sh; path = ../PodRelease.sh; sourceTree = ""; };
49 | 3BAE9BFF2145FEBB006731B1 /* github.p12 */ = {isa = PBXFileReference; lastKnownFileType = file; path = github.p12; sourceTree = ""; };
50 | 5DF05302839F7017B47EC21C /* Pods-SolarNetworkExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarNetworkExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SolarNetworkExample/Pods-SolarNetworkExample.debug.xcconfig"; sourceTree = ""; };
51 | 91171F28195C0598BA46A1B7 /* Pods_SolarNetworkExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolarNetworkExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
52 | F892D9E3202203D7007536ED /* GitHubTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubTarget.swift; sourceTree = ""; };
53 | F892D9E5202203D7007536ED /* GitHubSignoutRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSignoutRequest.swift; sourceTree = ""; };
54 | F892D9E6202203D7007536ED /* GitHubAPIRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubAPIRequest.swift; sourceTree = ""; };
55 | F892D9E7202203D7007536ED /* GitHubDownloadRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubDownloadRequest.swift; sourceTree = ""; };
56 | F892D9E8202203D7007536ED /* GitHubMyInfoRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubMyInfoRequest.swift; sourceTree = ""; };
57 | F892D9E9202203D7007536ED /* GitHubSigninRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSigninRequest.swift; sourceTree = ""; };
58 | F892D9EA202203D7007536ED /* GitHubUserInfoRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubUserInfoRequest.swift; sourceTree = ""; };
59 | F892D9EC202203D7007536ED /* GitHubPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubPlugin.swift; sourceTree = ""; };
60 | F892D9EE202203D7007536ED /* GitHubAuthenticationModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubAuthenticationModel.swift; sourceTree = ""; };
61 | F892D9F920220433007536ED /* HTTPBinTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinTarget.swift; sourceTree = ""; };
62 | F892D9FB20220433007536ED /* HTTPBinGETRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinGETRequest.swift; sourceTree = ""; };
63 | F892D9FC20220433007536ED /* HTTPBinUploadRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinUploadRequest.swift; sourceTree = ""; };
64 | F892D9FD20220433007536ED /* HTTPBinPUTRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinPUTRequest.swift; sourceTree = ""; };
65 | F892D9FE20220433007536ED /* HTTPBinDownLoadRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinDownLoadRequest.swift; sourceTree = ""; };
66 | F892D9FF20220433007536ED /* HTTPBinPATCHRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinPATCHRequest.swift; sourceTree = ""; };
67 | F892DA0020220433007536ED /* HTTPBinPOSTRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinPOSTRequest.swift; sourceTree = ""; };
68 | F892DA0120220433007536ED /* HTTPBinDELETERequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinDELETERequest.swift; sourceTree = ""; };
69 | F892DA0D2022A616007536ED /* github.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = github.cer; sourceTree = ""; };
70 | F892DA0F2022A622007536ED /* SLNetwork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SLNetwork.png; path = ../SLNetwork.png; sourceTree = ""; };
71 | F8AFC6092021F5EA00A3F595 /* SolarNetworkExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SolarNetworkExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
72 | F8AFC60C2021F5EA00A3F595 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
73 | F8AFC6112021F5EA00A3F595 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
74 | F8AFC6132021F5EA00A3F595 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
75 | F8AFC6162021F5EA00A3F595 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
76 | F8AFC6182021F5EA00A3F595 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
77 | F8AFC6232021F6A100A3F595 /* HTTPBin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBin.swift; sourceTree = ""; };
78 | F8AFC6252021F6A100A3F595 /* HTTPBinVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinVC.swift; sourceTree = ""; };
79 | F8AFC6332021F6A100A3F595 /* GitHubVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubVC.swift; sourceTree = ""; };
80 | F8AFC6362021F6A100A3F595 /* GitHub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHub.swift; sourceTree = ""; };
81 | /* End PBXFileReference section */
82 |
83 | /* Begin PBXFrameworksBuildPhase section */
84 | F8AFC6062021F5EA00A3F595 /* Frameworks */ = {
85 | isa = PBXFrameworksBuildPhase;
86 | buildActionMask = 2147483647;
87 | files = (
88 | A6921FFADB2B69B3AF6389F6 /* Pods_SolarNetworkExample.framework in Frameworks */,
89 | );
90 | runOnlyForDeploymentPostprocessing = 0;
91 | };
92 | /* End PBXFrameworksBuildPhase section */
93 |
94 | /* Begin PBXGroup section */
95 | 0E8E12D516B5CE50080579DE /* Pods */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 5DF05302839F7017B47EC21C /* Pods-SolarNetworkExample.debug.xcconfig */,
99 | 1AAC6A72AF16BEE0789C1E0C /* Pods-SolarNetworkExample.release.xcconfig */,
100 | );
101 | name = Pods;
102 | sourceTree = "";
103 | };
104 | 5ED1259F6F2E622144223105 /* Frameworks */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 91171F28195C0598BA46A1B7 /* Pods_SolarNetworkExample.framework */,
108 | );
109 | name = Frameworks;
110 | sourceTree = "";
111 | };
112 | F892D9E2202203D7007536ED /* Target */ = {
113 | isa = PBXGroup;
114 | children = (
115 | F892D9E3202203D7007536ED /* GitHubTarget.swift */,
116 | );
117 | path = Target;
118 | sourceTree = "";
119 | };
120 | F892D9E4202203D7007536ED /* Request */ = {
121 | isa = PBXGroup;
122 | children = (
123 | F892D9E5202203D7007536ED /* GitHubSignoutRequest.swift */,
124 | F892D9E6202203D7007536ED /* GitHubAPIRequest.swift */,
125 | F892D9E7202203D7007536ED /* GitHubDownloadRequest.swift */,
126 | F892D9E8202203D7007536ED /* GitHubMyInfoRequest.swift */,
127 | F892D9E9202203D7007536ED /* GitHubSigninRequest.swift */,
128 | F892D9EA202203D7007536ED /* GitHubUserInfoRequest.swift */,
129 | );
130 | path = Request;
131 | sourceTree = "";
132 | };
133 | F892D9EB202203D7007536ED /* Plugin */ = {
134 | isa = PBXGroup;
135 | children = (
136 | F892D9EC202203D7007536ED /* GitHubPlugin.swift */,
137 | );
138 | path = Plugin;
139 | sourceTree = "";
140 | };
141 | F892D9ED202203D7007536ED /* Model */ = {
142 | isa = PBXGroup;
143 | children = (
144 | F892D9EE202203D7007536ED /* GitHubAuthenticationModel.swift */,
145 | );
146 | path = Model;
147 | sourceTree = "";
148 | };
149 | F892D9F820220433007536ED /* Target */ = {
150 | isa = PBXGroup;
151 | children = (
152 | F892D9F920220433007536ED /* HTTPBinTarget.swift */,
153 | );
154 | path = Target;
155 | sourceTree = "";
156 | };
157 | F892D9FA20220433007536ED /* Request */ = {
158 | isa = PBXGroup;
159 | children = (
160 | F892D9FB20220433007536ED /* HTTPBinGETRequest.swift */,
161 | F892D9FC20220433007536ED /* HTTPBinUploadRequest.swift */,
162 | F892D9FD20220433007536ED /* HTTPBinPUTRequest.swift */,
163 | F892D9FE20220433007536ED /* HTTPBinDownLoadRequest.swift */,
164 | F892D9FF20220433007536ED /* HTTPBinPATCHRequest.swift */,
165 | F892DA0020220433007536ED /* HTTPBinPOSTRequest.swift */,
166 | F892DA0120220433007536ED /* HTTPBinDELETERequest.swift */,
167 | 3B4170FB213E55C5005302F4 /* HTTPBinStringParametersRequest.swift */,
168 | 3B4170FD213E634D005302F4 /* HTTPBinArrayParametersRequest.swift */,
169 | );
170 | path = Request;
171 | sourceTree = "";
172 | };
173 | F892DA0C2022A616007536ED /* Cer */ = {
174 | isa = PBXGroup;
175 | children = (
176 | 3BAE9BFF2145FEBB006731B1 /* github.p12 */,
177 | F892DA0D2022A616007536ED /* github.cer */,
178 | );
179 | path = Cer;
180 | sourceTree = "";
181 | };
182 | F8AFC6002021F5EA00A3F595 = {
183 | isa = PBXGroup;
184 | children = (
185 | 3B85FCE52238E305003E1590 /* PodRelease.sh */,
186 | F892DA0F2022A622007536ED /* SLNetwork.png */,
187 | F8AFC60B2021F5EA00A3F595 /* SolarNetworkExample */,
188 | F8AFC60A2021F5EA00A3F595 /* Products */,
189 | 0E8E12D516B5CE50080579DE /* Pods */,
190 | 5ED1259F6F2E622144223105 /* Frameworks */,
191 | );
192 | sourceTree = "";
193 | };
194 | F8AFC60A2021F5EA00A3F595 /* Products */ = {
195 | isa = PBXGroup;
196 | children = (
197 | F8AFC6092021F5EA00A3F595 /* SolarNetworkExample.app */,
198 | );
199 | name = Products;
200 | sourceTree = "";
201 | };
202 | F8AFC60B2021F5EA00A3F595 /* SolarNetworkExample */ = {
203 | isa = PBXGroup;
204 | children = (
205 | F892DA0C2022A616007536ED /* Cer */,
206 | F8AFC62D2021F6A100A3F595 /* GitHub */,
207 | F8AFC6222021F6A100A3F595 /* HTTPBin */,
208 | F8AFC60C2021F5EA00A3F595 /* AppDelegate.swift */,
209 | F8AFC6102021F5EA00A3F595 /* Main.storyboard */,
210 | F8AFC6132021F5EA00A3F595 /* Assets.xcassets */,
211 | F8AFC6152021F5EA00A3F595 /* LaunchScreen.storyboard */,
212 | F8AFC6182021F5EA00A3F595 /* Info.plist */,
213 | );
214 | path = SolarNetworkExample;
215 | sourceTree = "";
216 | };
217 | F8AFC6222021F6A100A3F595 /* HTTPBin */ = {
218 | isa = PBXGroup;
219 | children = (
220 | F8AFC6232021F6A100A3F595 /* HTTPBin.swift */,
221 | F8AFC6252021F6A100A3F595 /* HTTPBinVC.swift */,
222 | F892D9FA20220433007536ED /* Request */,
223 | F892D9F820220433007536ED /* Target */,
224 | );
225 | path = HTTPBin;
226 | sourceTree = "";
227 | };
228 | F8AFC62D2021F6A100A3F595 /* GitHub */ = {
229 | isa = PBXGroup;
230 | children = (
231 | F8AFC6362021F6A100A3F595 /* GitHub.swift */,
232 | F8AFC6332021F6A100A3F595 /* GitHubVC.swift */,
233 | F892D9ED202203D7007536ED /* Model */,
234 | F892D9EB202203D7007536ED /* Plugin */,
235 | F892D9E4202203D7007536ED /* Request */,
236 | F892D9E2202203D7007536ED /* Target */,
237 | );
238 | path = GitHub;
239 | sourceTree = "";
240 | };
241 | /* End PBXGroup section */
242 |
243 | /* Begin PBXNativeTarget section */
244 | F8AFC6082021F5EA00A3F595 /* SolarNetworkExample */ = {
245 | isa = PBXNativeTarget;
246 | buildConfigurationList = F8AFC61B2021F5EA00A3F595 /* Build configuration list for PBXNativeTarget "SolarNetworkExample" */;
247 | buildPhases = (
248 | 933A03F07C702F20C4FE517E /* [CP] Check Pods Manifest.lock */,
249 | F8AFC6052021F5EA00A3F595 /* Sources */,
250 | F8AFC6062021F5EA00A3F595 /* Frameworks */,
251 | F8AFC6072021F5EA00A3F595 /* Resources */,
252 | D8E94A426470D99834E0D7E8 /* [CP] Embed Pods Frameworks */,
253 | F87CA505205BC1170037DFC1 /* TODO- */,
254 | );
255 | buildRules = (
256 | );
257 | dependencies = (
258 | );
259 | name = SolarNetworkExample;
260 | productName = SolarNetworkExample;
261 | productReference = F8AFC6092021F5EA00A3F595 /* SolarNetworkExample.app */;
262 | productType = "com.apple.product-type.application";
263 | };
264 | /* End PBXNativeTarget section */
265 |
266 | /* Begin PBXProject section */
267 | F8AFC6012021F5EA00A3F595 /* Project object */ = {
268 | isa = PBXProject;
269 | attributes = {
270 | LastSwiftUpdateCheck = 0920;
271 | LastUpgradeCheck = 1000;
272 | ORGANIZATIONNAME = SolarKit;
273 | TargetAttributes = {
274 | F8AFC6082021F5EA00A3F595 = {
275 | CreatedOnToolsVersion = 9.2;
276 | LastSwiftMigration = 1030;
277 | ProvisioningStyle = Automatic;
278 | };
279 | };
280 | };
281 | buildConfigurationList = F8AFC6042021F5EA00A3F595 /* Build configuration list for PBXProject "SolarNetworkExample" */;
282 | compatibilityVersion = "Xcode 8.0";
283 | developmentRegion = en;
284 | hasScannedForEncodings = 0;
285 | knownRegions = (
286 | en,
287 | Base,
288 | );
289 | mainGroup = F8AFC6002021F5EA00A3F595;
290 | productRefGroup = F8AFC60A2021F5EA00A3F595 /* Products */;
291 | projectDirPath = "";
292 | projectRoot = "";
293 | targets = (
294 | F8AFC6082021F5EA00A3F595 /* SolarNetworkExample */,
295 | );
296 | };
297 | /* End PBXProject section */
298 |
299 | /* Begin PBXResourcesBuildPhase section */
300 | F8AFC6072021F5EA00A3F595 /* Resources */ = {
301 | isa = PBXResourcesBuildPhase;
302 | buildActionMask = 2147483647;
303 | files = (
304 | F8AFC6172021F5EA00A3F595 /* LaunchScreen.storyboard in Resources */,
305 | F892DA0E2022A616007536ED /* github.cer in Resources */,
306 | F8AFC6142021F5EA00A3F595 /* Assets.xcassets in Resources */,
307 | 3B85FCE62238E306003E1590 /* PodRelease.sh in Resources */,
308 | F8AFC6122021F5EA00A3F595 /* Main.storyboard in Resources */,
309 | 3BAE9C002145FEBB006731B1 /* github.p12 in Resources */,
310 | F892DA102022A622007536ED /* SLNetwork.png in Resources */,
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | };
314 | /* End PBXResourcesBuildPhase section */
315 |
316 | /* Begin PBXShellScriptBuildPhase section */
317 | 933A03F07C702F20C4FE517E /* [CP] Check Pods Manifest.lock */ = {
318 | isa = PBXShellScriptBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | );
322 | inputPaths = (
323 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
324 | "${PODS_ROOT}/Manifest.lock",
325 | );
326 | name = "[CP] Check Pods Manifest.lock";
327 | outputPaths = (
328 | "$(DERIVED_FILE_DIR)/Pods-SolarNetworkExample-checkManifestLockResult.txt",
329 | );
330 | runOnlyForDeploymentPostprocessing = 0;
331 | shellPath = /bin/sh;
332 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
333 | showEnvVarsInLog = 0;
334 | };
335 | D8E94A426470D99834E0D7E8 /* [CP] Embed Pods Frameworks */ = {
336 | isa = PBXShellScriptBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | );
340 | inputPaths = (
341 | "${PODS_ROOT}/Target Support Files/Pods-SolarNetworkExample/Pods-SolarNetworkExample-frameworks.sh",
342 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
343 | "${BUILT_PRODUCTS_DIR}/SolarNetwork/SolarNetwork.framework",
344 | );
345 | name = "[CP] Embed Pods Frameworks";
346 | outputPaths = (
347 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
348 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SolarNetwork.framework",
349 | );
350 | runOnlyForDeploymentPostprocessing = 0;
351 | shellPath = /bin/sh;
352 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SolarNetworkExample/Pods-SolarNetworkExample-frameworks.sh\"\n";
353 | showEnvVarsInLog = 0;
354 | };
355 | F87CA505205BC1170037DFC1 /* TODO- */ = {
356 | isa = PBXShellScriptBuildPhase;
357 | buildActionMask = 2147483647;
358 | files = (
359 | );
360 | inputPaths = (
361 | );
362 | name = "TODO-";
363 | outputPaths = (
364 | );
365 | runOnlyForDeploymentPostprocessing = 0;
366 | shellPath = /bin/sh;
367 | shellScript = "KEYWORDS=\"TODO-|FIXME-|DevTeam-|XXX-\"\nfind \"${SRCROOT}\" \\( -name \"*.swift\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\"\n";
368 | };
369 | /* End PBXShellScriptBuildPhase section */
370 |
371 | /* Begin PBXSourcesBuildPhase section */
372 | F8AFC6052021F5EA00A3F595 /* Sources */ = {
373 | isa = PBXSourcesBuildPhase;
374 | buildActionMask = 2147483647;
375 | files = (
376 | F8AFC63B2021F6A100A3F595 /* HTTPBinVC.swift in Sources */,
377 | F892D9F5202203D7007536ED /* GitHubUserInfoRequest.swift in Sources */,
378 | F8AFC6482021F6A100A3F595 /* GitHubVC.swift in Sources */,
379 | 3B4170FE213E634D005302F4 /* HTTPBinArrayParametersRequest.swift in Sources */,
380 | F8AFC64B2021F6A100A3F595 /* GitHub.swift in Sources */,
381 | F892DA0320220433007536ED /* HTTPBinGETRequest.swift in Sources */,
382 | F892DA0520220433007536ED /* HTTPBinPUTRequest.swift in Sources */,
383 | F892D9F7202203D7007536ED /* GitHubAuthenticationModel.swift in Sources */,
384 | F892D9F2202203D7007536ED /* GitHubDownloadRequest.swift in Sources */,
385 | F892D9F1202203D7007536ED /* GitHubAPIRequest.swift in Sources */,
386 | F892D9F6202203D7007536ED /* GitHubPlugin.swift in Sources */,
387 | F892D9F3202203D7007536ED /* GitHubMyInfoRequest.swift in Sources */,
388 | F8AFC60D2021F5EA00A3F595 /* AppDelegate.swift in Sources */,
389 | 3B4170FC213E55C5005302F4 /* HTTPBinStringParametersRequest.swift in Sources */,
390 | F892DA0820220433007536ED /* HTTPBinPOSTRequest.swift in Sources */,
391 | F892D9F4202203D7007536ED /* GitHubSigninRequest.swift in Sources */,
392 | F8AFC6392021F6A100A3F595 /* HTTPBin.swift in Sources */,
393 | F892DA0720220433007536ED /* HTTPBinPATCHRequest.swift in Sources */,
394 | F892D9EF202203D7007536ED /* GitHubTarget.swift in Sources */,
395 | F892DA0420220433007536ED /* HTTPBinUploadRequest.swift in Sources */,
396 | F892DA0620220433007536ED /* HTTPBinDownLoadRequest.swift in Sources */,
397 | F892DA0220220433007536ED /* HTTPBinTarget.swift in Sources */,
398 | F892DA0920220433007536ED /* HTTPBinDELETERequest.swift in Sources */,
399 | F892D9F0202203D7007536ED /* GitHubSignoutRequest.swift in Sources */,
400 | );
401 | runOnlyForDeploymentPostprocessing = 0;
402 | };
403 | /* End PBXSourcesBuildPhase section */
404 |
405 | /* Begin PBXVariantGroup section */
406 | F8AFC6102021F5EA00A3F595 /* Main.storyboard */ = {
407 | isa = PBXVariantGroup;
408 | children = (
409 | F8AFC6112021F5EA00A3F595 /* Base */,
410 | );
411 | name = Main.storyboard;
412 | sourceTree = "";
413 | };
414 | F8AFC6152021F5EA00A3F595 /* LaunchScreen.storyboard */ = {
415 | isa = PBXVariantGroup;
416 | children = (
417 | F8AFC6162021F5EA00A3F595 /* Base */,
418 | );
419 | name = LaunchScreen.storyboard;
420 | sourceTree = "";
421 | };
422 | /* End PBXVariantGroup section */
423 |
424 | /* Begin XCBuildConfiguration section */
425 | F8AFC6192021F5EA00A3F595 /* Debug */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | CLANG_ANALYZER_NONNULL = YES;
430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
432 | CLANG_CXX_LIBRARY = "libc++";
433 | CLANG_ENABLE_MODULES = YES;
434 | CLANG_ENABLE_OBJC_ARC = YES;
435 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
436 | CLANG_WARN_BOOL_CONVERSION = YES;
437 | CLANG_WARN_COMMA = YES;
438 | CLANG_WARN_CONSTANT_CONVERSION = YES;
439 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
440 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
441 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INFINITE_RECURSION = YES;
445 | CLANG_WARN_INT_CONVERSION = YES;
446 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
447 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
448 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
451 | CLANG_WARN_STRICT_PROTOTYPES = YES;
452 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
453 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
454 | CLANG_WARN_UNREACHABLE_CODE = YES;
455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
456 | CODE_SIGN_IDENTITY = "iPhone Developer";
457 | COPY_PHASE_STRIP = NO;
458 | DEBUG_INFORMATION_FORMAT = dwarf;
459 | ENABLE_STRICT_OBJC_MSGSEND = YES;
460 | ENABLE_TESTABILITY = YES;
461 | GCC_C_LANGUAGE_STANDARD = gnu11;
462 | GCC_DYNAMIC_NO_PIC = NO;
463 | GCC_NO_COMMON_BLOCKS = YES;
464 | GCC_OPTIMIZATION_LEVEL = 0;
465 | GCC_PREPROCESSOR_DEFINITIONS = (
466 | "DEBUG=1",
467 | "$(inherited)",
468 | );
469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
471 | GCC_WARN_UNDECLARED_SELECTOR = YES;
472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
473 | GCC_WARN_UNUSED_FUNCTION = YES;
474 | GCC_WARN_UNUSED_VARIABLE = YES;
475 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
476 | MTL_ENABLE_DEBUG_INFO = YES;
477 | ONLY_ACTIVE_ARCH = YES;
478 | SDKROOT = iphoneos;
479 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
480 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
481 | };
482 | name = Debug;
483 | };
484 | F8AFC61A2021F5EA00A3F595 /* Release */ = {
485 | isa = XCBuildConfiguration;
486 | buildSettings = {
487 | ALWAYS_SEARCH_USER_PATHS = NO;
488 | CLANG_ANALYZER_NONNULL = YES;
489 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
490 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
491 | CLANG_CXX_LIBRARY = "libc++";
492 | CLANG_ENABLE_MODULES = YES;
493 | CLANG_ENABLE_OBJC_ARC = YES;
494 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
495 | CLANG_WARN_BOOL_CONVERSION = YES;
496 | CLANG_WARN_COMMA = YES;
497 | CLANG_WARN_CONSTANT_CONVERSION = YES;
498 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
499 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
500 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
501 | CLANG_WARN_EMPTY_BODY = YES;
502 | CLANG_WARN_ENUM_CONVERSION = YES;
503 | CLANG_WARN_INFINITE_RECURSION = YES;
504 | CLANG_WARN_INT_CONVERSION = YES;
505 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
506 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
507 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
508 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
509 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
510 | CLANG_WARN_STRICT_PROTOTYPES = YES;
511 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
512 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
513 | CLANG_WARN_UNREACHABLE_CODE = YES;
514 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
515 | CODE_SIGN_IDENTITY = "iPhone Developer";
516 | COPY_PHASE_STRIP = NO;
517 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
518 | ENABLE_NS_ASSERTIONS = NO;
519 | ENABLE_STRICT_OBJC_MSGSEND = YES;
520 | GCC_C_LANGUAGE_STANDARD = gnu11;
521 | GCC_NO_COMMON_BLOCKS = YES;
522 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
523 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
524 | GCC_WARN_UNDECLARED_SELECTOR = YES;
525 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
526 | GCC_WARN_UNUSED_FUNCTION = YES;
527 | GCC_WARN_UNUSED_VARIABLE = YES;
528 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
529 | MTL_ENABLE_DEBUG_INFO = NO;
530 | SDKROOT = iphoneos;
531 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
532 | VALIDATE_PRODUCT = YES;
533 | };
534 | name = Release;
535 | };
536 | F8AFC61C2021F5EA00A3F595 /* Debug */ = {
537 | isa = XCBuildConfiguration;
538 | baseConfigurationReference = 5DF05302839F7017B47EC21C /* Pods-SolarNetworkExample.debug.xcconfig */;
539 | buildSettings = {
540 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
541 | CODE_SIGN_STYLE = Automatic;
542 | DEVELOPMENT_TEAM = "";
543 | INFOPLIST_FILE = SolarNetworkExample/Info.plist;
544 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
546 | PRODUCT_BUNDLE_IDENTIFIER = com.SolarKit.SolarNetwork;
547 | PRODUCT_NAME = "$(TARGET_NAME)";
548 | SWIFT_VERSION = 5.0;
549 | TARGETED_DEVICE_FAMILY = "1,2";
550 | };
551 | name = Debug;
552 | };
553 | F8AFC61D2021F5EA00A3F595 /* Release */ = {
554 | isa = XCBuildConfiguration;
555 | baseConfigurationReference = 1AAC6A72AF16BEE0789C1E0C /* Pods-SolarNetworkExample.release.xcconfig */;
556 | buildSettings = {
557 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
558 | CODE_SIGN_STYLE = Automatic;
559 | DEVELOPMENT_TEAM = "";
560 | INFOPLIST_FILE = SolarNetworkExample/Info.plist;
561 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
562 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
563 | PRODUCT_BUNDLE_IDENTIFIER = com.SolarKit.SolarNetwork;
564 | PRODUCT_NAME = "$(TARGET_NAME)";
565 | SWIFT_VERSION = 5.0;
566 | TARGETED_DEVICE_FAMILY = "1,2";
567 | };
568 | name = Release;
569 | };
570 | /* End XCBuildConfiguration section */
571 |
572 | /* Begin XCConfigurationList section */
573 | F8AFC6042021F5EA00A3F595 /* Build configuration list for PBXProject "SolarNetworkExample" */ = {
574 | isa = XCConfigurationList;
575 | buildConfigurations = (
576 | F8AFC6192021F5EA00A3F595 /* Debug */,
577 | F8AFC61A2021F5EA00A3F595 /* Release */,
578 | );
579 | defaultConfigurationIsVisible = 0;
580 | defaultConfigurationName = Release;
581 | };
582 | F8AFC61B2021F5EA00A3F595 /* Build configuration list for PBXNativeTarget "SolarNetworkExample" */ = {
583 | isa = XCConfigurationList;
584 | buildConfigurations = (
585 | F8AFC61C2021F5EA00A3F595 /* Debug */,
586 | F8AFC61D2021F5EA00A3F595 /* Release */,
587 | );
588 | defaultConfigurationIsVisible = 0;
589 | defaultConfigurationName = Release;
590 | };
591 | /* End XCConfigurationList section */
592 | };
593 | rootObject = F8AFC6012021F5EA00A3F595 /* Project object */;
594 | }
595 |
--------------------------------------------------------------------------------