├── .gitignore ├── .swift-version ├── Cartfile ├── Cartfile.resolved ├── ChineseREADME.md ├── Documentation ├── Download.md ├── Plugin.md ├── Progress.md ├── Request.md ├── Response.md ├── Target.md ├── UpdateLog.md └── Upload.md ├── LICENSE ├── PodRelease.sh ├── README.md ├── SLNetwork.png ├── SolarNetwork.podspec ├── SolarNetwork.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── SolarNetwork.xcscheme ├── SolarNetwork ├── Info.plist └── SolarNetwork.h ├── SolarNetworkExample ├── Podfile ├── Podfile.lock ├── SolarNetworkExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── SolarNetworkExample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── SolarNetworkExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Cer │ ├── github.cer │ └── github.p12 │ ├── GitHub │ ├── GitHub.swift │ ├── GitHubVC.swift │ ├── Model │ │ └── GitHubAuthenticationModel.swift │ ├── Plugin │ │ └── GitHubPlugin.swift │ ├── Request │ │ ├── GitHubAPIRequest.swift │ │ ├── GitHubDownloadRequest.swift │ │ ├── GitHubMyInfoRequest.swift │ │ ├── GitHubSigninRequest.swift │ │ ├── GitHubSignoutRequest.swift │ │ └── GitHubUserInfoRequest.swift │ └── Target │ │ └── GitHubTarget.swift │ ├── HTTPBin │ ├── HTTPBin.swift │ ├── HTTPBinVC.swift │ ├── Request │ │ ├── HTTPBinArrayParametersRequest.swift │ │ ├── HTTPBinDELETERequest.swift │ │ ├── HTTPBinDownLoadRequest.swift │ │ ├── HTTPBinGETRequest.swift │ │ ├── HTTPBinPATCHRequest.swift │ │ ├── HTTPBinPOSTRequest.swift │ │ ├── HTTPBinPUTRequest.swift │ │ ├── HTTPBinStringParametersRequest.swift │ │ └── HTTPBinUploadRequest.swift │ └── Target │ │ └── HTTPBinTarget.swift │ └── Info.plist └── Source ├── Extension ├── FileManager+SolarNetwork.swift ├── SLNamespace.swift ├── String+SolarNetwork.swift └── URLSessionTask+SolarNetwork.swift ├── SLNetwork+Alamofire.swift ├── SLNetwork.swift ├── SLPlugin.swift ├── SLProgress.swift ├── SLRequest.swift ├── SLResponse.swift ├── SLTarget.swift └── Utility ├── SLParameterValueEncoding.swift ├── SLReflection.swift ├── SLResumeData.swift └── SLSessionDelegate.swift /.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 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.6.0" 2 | -------------------------------------------------------------------------------- /ChineseREADME.md: -------------------------------------------------------------------------------- 1 | # ![SLNetwork](SLNetwork.png) 2 | 3 | ![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SolarNetwork.svg) ![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat) ![Platform](https://img.shields.io/cocoapods/p/SolarNetwork.svg?style=flat) 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![SLNetwork](SLNetwork.png) 2 | 3 | ![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SolarNetwork.svg) ![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat) ![Platform](https://img.shields.io/cocoapods/p/SolarNetwork.svg?style=flat) 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 | -------------------------------------------------------------------------------- /SLNetwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/0f521c6838afffab605e0ad6972099f154210b6a/SLNetwork.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /SolarNetwork.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /SolarNetworkExample/SolarNetworkExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SolarNetworkExample/SolarNetworkExample.xcodeproj/project.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.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /SolarNetworkExample/SolarNetworkExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /SolarNetworkExample/SolarNetworkExample/Cer/github.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/0f521c6838afffab605e0ad6972099f154210b6a/SolarNetworkExample/SolarNetworkExample/Cer/github.cer -------------------------------------------------------------------------------- /SolarNetworkExample/SolarNetworkExample/Cer/github.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreeGayHub/SolarNetwork/0f521c6838afffab605e0ad6972099f154210b6a/SolarNetworkExample/SolarNetworkExample/Cer/github.p12 -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------