Skip to content

Commit c76c30e

Browse files
committed
Update.
1 parent fd7db02 commit c76c30e

File tree

5 files changed

+154
-93
lines changed

5 files changed

+154
-93
lines changed

README.md

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,23 @@ It can be used only when the following conditions are satisfied.
1515

1616

1717
```Swift
18-
struct DummyStruct {
18+
// Basic pattern
19+
struct BasicStruct {
1920
let message:String
2021
}
2122

22-
extension DummyStruct : WebInitializable {
23+
extension BasicStruct : WebInitializable {
2324
typealias inputType = TestParam
2425
typealias errorType = ApplicationError
2526

26-
static var path = "http://localhost:8080/dummy"
27+
static var path = "http://localhost:8080/basic"
2728

2829
init (fromJson json:Any) throws{
2930
guard case let dic as [String:Any] = json
30-
else { throw ParseError(code: -1, reason: "not dictionary") }
31+
else { throw ParseError(code: -1, reason: "Return body is not a dictionary.") }
3132

3233
guard case let message as String = dic["message"]
33-
else { throw ParseError(code: -1, reason: "message not found.") }
34+
else { throw ParseError(code: -1, reason: "Message is not found.") }
3435

3536
self.message = message
3637
}
@@ -86,31 +87,53 @@ extension ApplicationError : WebSerializable{
8687
## Struct Initialize
8788

8889
```Swift
89-
let dummy = try? DummyStruct( TestParam(param: "hoge") )
90+
let basic = try? BasicStruct( TestParam(param: "hoge") )
9091
```
9192

9293
# Customize
9394

9495
You can customize the behavior by implementing timeout and configuration.
9596

97+
## Want to extend the timeout
9698
```Swift
9799
extension CustomStruct : WebInitializable {
98-
typealias inputType = TestParam
99-
typealias errorType = ApplicationError
100-
101-
static var path = "http://localhost:8080/timeout"
100+
...
102101
static var timeout = 10
102+
...
103+
}
104+
```
105+
106+
## Want to custom URLSessionConfiguration
107+
```Swift
108+
extension CustomStruct : WebInitializable {
109+
...
103110
static var configuration:URLSessionConfiguration {
104111
let def = URLSessionConfiguration.default
105-
def.allowsCelluarAccess = false
112+
def.allowsCelluarAccess = false // celluar access disabled
106113
return def
107114
}
108-
109-
init (fromJson json:Any) throws{
110-
111-
}
115+
...
116+
}
117+
```
118+
## Want to change HTTP method
119+
```Swift
120+
extension CustomStruct : WebInitializable {
121+
...
122+
static var method = "OPTIONS"
123+
...
112124
}
113125
```
126+
## Want to add HTTP headers
127+
```Swift
128+
extension CustomStruct : WebInitializable {
129+
...
130+
static var headers = [
131+
"hello" : "world"
132+
]
133+
...
134+
}
135+
```
136+
114137
# known Issues
115138
* There was a problem that a segmentation fault occurred when used with Ubuntu.
116-
* I looked up this problem is solved on DEVELOPMENT-SNAPSHOT-2017-02-09-a.
139+
* I looked up this problem is solved on DEVELOPMENT-SNAPSHOT-2017-02-09-a.

Sources/WebStruct.swift

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
// WebStruct.swift
33
// WebStruct
44
//
5-
// Created by Arakane Ikumi on 2016/08/24.
5+
// Created by iq3 on 2016/08/24.
66
// Copyright © 2016年 addli.co.jp. All rights reserved.
77
//
88

99
import Foundation
1010
import Dispatch
1111

12+
/**
13+
WebStruct error type
14+
*/
1215
public indirect enum Error : Swift.Error{
1316
case network(Swift.Error)
1417
case http(Swift.Error)
@@ -17,58 +20,81 @@ public indirect enum Error : Swift.Error{
1720
case application(WebSerializable)
1821
}
1922

23+
/**
24+
Json serializeable protocol
25+
*/
2026
public protocol WebSerializable{
27+
// Must implement
2128
init (fromJson json:Any) throws
2229
}
2330

31+
/**
32+
Json deserializeable protocol
33+
*/
2434
public protocol WebDeserializable {
35+
// Must implement
2536
func toJsonData() -> Any
2637
}
2738

39+
/**
40+
Json initializable protocol
41+
*/
2842
public protocol WebInitializable : WebSerializable {
2943
associatedtype inputType: WebDeserializable
3044
associatedtype errorType: WebSerializable
3145

46+
// Must implement
3247
static var path:String { get }
48+
49+
// Optional
50+
static var method:String { get }
51+
static var headers:[String:String] { get }
3352
static var timeout:TimeInterval { get }
3453
static var configuration:URLSessionConfiguration { get }
54+
static var urlsessionDelegate:URLSessionDelegate? { get }
3555
}
3656

57+
/**
58+
Default implement for WebInitializable
59+
*/
3760
extension WebInitializable{
3861
public init(_ param:Self.inputType) throws {
3962
self = try WebStruct<Self,Self.errorType>().get( param )
4063
}
4164

4265
// default values
66+
static public var method:String { return "POST" }
67+
static public var headers:[String:String] { return [:] }
4368
static public var timeout:TimeInterval { return 5.0 }
4469
static public var configuration:URLSessionConfiguration { return URLSessionConfiguration.default }
70+
static public var urlsessionDelegate:URLSessionDelegate? { return nil }
4571
}
4672

73+
/**
74+
Default implement for WebInitializable
75+
*/
4776
fileprivate struct WebStruct <T:WebInitializable,ERR:WebSerializable>{
4877

4978
fileprivate init(){}
5079

5180
fileprivate func get<P:WebDeserializable>(_ param:P) throws -> T {
5281

53-
guard let url = URL(string: T.path )
54-
else{ fatalError() }
55-
56-
guard let body = try? JSONSerialization.data(withJSONObject: param.toJsonData(), options: JSONSerialization.WritingOptions())
57-
else{ fatalError() }
82+
// verify for request
83+
guard let url = URL(string: T.path ) else{ fatalError() }
84+
guard let body = try? JSONSerialization.data(withJSONObject: param.toJsonData(), options: JSONSerialization.WritingOptions()) else{ fatalError() }
5885

86+
// setup for request
5987
var request = URLRequest(url:url, cachePolicy:.reloadIgnoringLocalCacheData, timeoutInterval:T.timeout)
60-
request.httpMethod = "POST"
88+
request.httpMethod = T.method
6189
request.addValue("application/json", forHTTPHeaderField:"Content-Type")
90+
for (key,value) in T.headers {
91+
request.addValue( value, forHTTPHeaderField: key )
92+
}
6293
request.httpBody = body
6394

64-
#if os(macOS) || os(iOS)
65-
let session = URLSession(configuration: T.configuration, delegate: URLSessionDelegateClass(), delegateQueue: nil)
66-
#else
67-
let session = URLSession(configuration: T.configuration, delegate:nil, delegateQueue: nil)
68-
#endif
69-
95+
// send request
96+
let session = URLSession(configuration: T.configuration, delegate: T.urlsessionDelegate, delegateQueue: nil)
7097
let semaphore = DispatchSemaphore(value: 0)
71-
7298
var data:Data?,response:URLResponse?,error:Swift.Error?
7399
let subtask = session.dataTask(with: request) { (d, r, e) in
74100
data = d; response = r; error = e;
@@ -77,16 +103,19 @@ fileprivate struct WebStruct <T:WebInitializable,ERR:WebSerializable>{
77103
subtask.resume()
78104
let _ = semaphore.wait(timeout: DispatchTime.distantFuture)
79105

106+
// verify for response
80107
if let error = error {
81108
throw Error.network(error)
82109
}
83110

84111
if case let httpResponse as HTTPURLResponse = response{
85112
switch httpResponse.statusCode{
86113
case 200...299: break
87-
default: throw Error.http(NSError(domain: "HTTPError", code: httpResponse.statusCode, userInfo: nil))
114+
default: throw Error.http(NSError(domain: "HTTPError", code: httpResponse.statusCode, userInfo: [ "description" : HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode) ]) )
88115
}
89116
}
117+
118+
// parse
90119
guard let someData = data,
91120
let jsonDic = try? JSONSerialization.jsonObject(with: someData, options:JSONSerialization.ReadingOptions())
92121
else { throw Error.ignoreData }
@@ -102,45 +131,9 @@ fileprivate struct WebStruct <T:WebInitializable,ERR:WebSerializable>{
102131
throw Error.application(err)
103132
}
104133

134+
// complete
105135
return gen
106136
}
107137
}
108138

109139

110-
// Passed self certificate
111-
#if os(macOS) || os(iOS)
112-
fileprivate class URLSessionDelegateClass : NSObject, URLSessionDelegate{
113-
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void){
114-
115-
116-
var disposition: Foundation.URLSession.AuthChallengeDisposition = .performDefaultHandling
117-
var credential: URLCredential?
118-
119-
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
120-
disposition = .useCredential
121-
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
122-
} else {
123-
if challenge.previousFailureCount > 0 {
124-
disposition = .cancelAuthenticationChallenge
125-
} else {
126-
credential = session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
127-
128-
if credential != nil {
129-
disposition = .useCredential
130-
}
131-
}
132-
}
133-
completionHandler(disposition, credential)
134-
}
135-
}
136-
137-
#else
138-
139-
extension URLRequest {
140-
static func allowsAnyHTTPSCertificateForHost(host: String) -> Bool {
141-
return true
142-
}
143-
}
144-
145-
#endif
146-

Tests/WebStructTests/Structs.swift

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,29 @@ import Foundation
1010

1111
@testable import WebStruct
1212

13-
struct DummyStruct {
13+
// Basic pattern
14+
struct BasicStruct {
1415
let message:String
1516
}
1617

17-
extension DummyStruct : WebInitializable {
18+
extension BasicStruct : WebInitializable {
1819
typealias inputType = TestParam
1920
typealias errorType = ApplicationError
2021

21-
static var path = "http://localhost:8080/dummy"
22+
static var path = "http://localhost:8080/basic"
2223

2324
init (fromJson json:Any) throws{
2425
guard case let dic as [String:Any] = json
25-
else { throw ParseError(code: -1, reason: "not dictionary") }
26+
else { throw ParseError(code: -1, reason: "Return body is not a dictionary.") }
2627

2728
guard case let message as String = dic["message"]
28-
else { throw ParseError(code: -1, reason: "message not found.") }
29+
else { throw ParseError(code: -1, reason: "Message is not found.") }
2930

3031
self.message = message
3132
}
3233
}
3334

35+
// Abnormal pattern
3436
struct ErrorStruct {
3537

3638
}
@@ -48,7 +50,7 @@ extension ErrorStruct : WebInitializable {
4850
}
4951

5052

51-
53+
// Added custom property
5254
struct CustomStruct {
5355

5456
}
@@ -72,6 +74,35 @@ extension CustomStruct : WebInitializable {
7274
}
7375

7476

77+
// Added custom http header
78+
struct CustomHeadersStruct {
79+
let headers:[String:String]
80+
}
81+
82+
extension CustomHeadersStruct : WebInitializable {
83+
typealias inputType = TestParam
84+
typealias errorType = ApplicationError
85+
86+
static var path = "http://localhost:8080/headers"
87+
static var method = "OPTIONS"
88+
static var headers = [
89+
"hello" : "world"
90+
]
91+
92+
init (fromJson json:Any) throws{
93+
94+
guard case let dic as [String:Any] = json
95+
else { throw ParseError(code: -1, reason: "Return body is not a dictionary.") }
96+
97+
guard case let headers as [String:String] = dic["YourHTTPHeader"]
98+
else { throw ParseError(code: -1, reason: "YourHTTPHeader is not found.") }
99+
100+
self.headers = headers
101+
}
102+
}
103+
104+
105+
// Posting value
75106
struct TestParam {
76107
let param:String
77108
}
@@ -82,6 +113,8 @@ extension TestParam : WebDeserializable {
82113
}
83114
}
84115

116+
117+
// Error type
85118
struct ParseError : Swift.Error{
86119
let code:Int
87120
let reason:String

0 commit comments

Comments
 (0)