Skip to content

Commit 4998fe3

Browse files
committed
Merge pull request Kong#44 from Mashape/feature/swift/nsurlsession
[feat] support for swift/nsurlsession
2 parents 07c84b2 + cc1aec0 commit 4998fe3

22 files changed

Lines changed: 636 additions & 1 deletion

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,13 @@ Currently the following output [targets](/src/targets) are supported:
4747
- [Requests](http://docs.python-requests.org/en/latest/api/#requests.request)
4848
- Objective-C
4949
- [NSURLSession](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html)
50+
- Swift
51+
- [NSURLSession](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html)
5052
- Go
5153
- [Native](http://golang.org/pkg/net/http/#NewRequest)
5254
- OCaml
5355
- [CoHTTP](https://github.com/mirage/ocaml-cohttp)
56+
- [Go](http://golang.org/pkg/net/http/#NewRequest)
5457

5558
## Installation
5659

‎src/targets/objc/helpers.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module.exports = {
1414
},
1515

1616
/**
17-
* Create a string corresponding to a valid declaration and initialization of an Objective-C object litteral.
17+
* Create a string corresponding to a valid declaration and initialization of an Objective-C object literal.
1818
*
1919
* @param {string} nsClass Class of the litteral
2020
* @param {string} name Desired name of the instance

‎src/targets/objc/native.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module.exports = function (source, options) {
2323

2424
var indent = opts.indent
2525
var code = new CodeBuilder(opts.indent)
26+
2627
// Markers for headers to be created as litteral objects and later be set on the NSURLRequest if exist
2728
var req = {
2829
hasHeaders: false,

‎src/targets/swift/helpers.js‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use strict'
2+
3+
var util = require('util')
4+
5+
/**
6+
* Create an string of given length filled with blank spaces
7+
*
8+
* @param {number} length Length of the array to return
9+
* @return {string}
10+
*/
11+
function buildString (length, str) {
12+
return Array.apply(null, new Array(length)).map(String.prototype.valueOf, str).join('')
13+
}
14+
15+
/**
16+
* Create a string corresponding to a Dictionary or Array literal representation with pretty option
17+
* and indentation.
18+
*/
19+
function concatArray (arr, pretty, indentation, indentLevel) {
20+
var currentIndent = buildString(indentLevel, indentation)
21+
var closingBraceIndent = buildString(indentLevel - 1, indentation)
22+
var join = pretty ? ',\n' + currentIndent : ', '
23+
24+
if (pretty) {
25+
return '[\n' + currentIndent + arr.join(join) + '\n' + closingBraceIndent + ']'
26+
} else {
27+
return '[' + arr.join(join) + ']'
28+
}
29+
}
30+
31+
module.exports = {
32+
/**
33+
* Create a string corresponding to a valid declaration and initialization of a Swift array or dictionary literal
34+
*
35+
* @param {string} name Desired name of the instance
36+
* @param {Object} parameters Key-value object of parameters to translate to a Swift object litearal
37+
* @param {Object} opts Target options
38+
* @return {string}
39+
*/
40+
literalDeclaration: function (name, parameters, opts) {
41+
return util.format('let %s = %s', name, this.literalRepresentation(parameters, opts))
42+
},
43+
44+
/**
45+
* Create a valid Swift string of a literal value according to its type.
46+
*
47+
* @param {*} value Any JavaScript literal
48+
* @param {Object} opts Target options
49+
* @return {string}
50+
*/
51+
literalRepresentation: function (value, opts, indentLevel) {
52+
indentLevel = indentLevel === undefined ? 1 : indentLevel + 1
53+
54+
switch (Object.prototype.toString.call(value)) {
55+
case '[object Number]':
56+
return value
57+
case '[object Array]':
58+
// Don't prettify arrays nto not take too much space
59+
var pretty = false
60+
var valuesRepresentation = value.map(function (v) {
61+
// Switch to prettify if the value is a dictionary with multiple keys
62+
if (Object.prototype.toString.call(v) === '[object Object]') {
63+
pretty = Object.keys(v).length > 1
64+
}
65+
return this.literalRepresentation(v, opts, indentLevel)
66+
}.bind(this))
67+
return concatArray(valuesRepresentation, pretty, opts.indent, indentLevel)
68+
case '[object Object]':
69+
var keyValuePairs = []
70+
for (var k in value) {
71+
keyValuePairs.push(util.format('"%s": %s', k, this.literalRepresentation(value[k], opts, indentLevel)))
72+
}
73+
return concatArray(keyValuePairs, opts.pretty && keyValuePairs.length > 1, opts.indent, indentLevel)
74+
default:
75+
return '"' + value.replace(/"/g, '\\"') + '"'
76+
}
77+
}
78+
}

‎src/targets/swift/index.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict'
2+
3+
module.exports = require('require-directory')(module)

‎src/targets/swift/info.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict'
2+
3+
module.exports = {
4+
key: 'swift',
5+
title: 'Swift',
6+
extname: '.swift',
7+
default: 'nsurlsession'
8+
}

‎src/targets/swift/nsurlsession.js‎

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @description
3+
* HTTP code snippet generator for Swift using NSURLSession.
4+
*
5+
* @author
6+
* @thibaultCha
7+
*
8+
* for any questions or issues regarding the generated code snippet, please open an issue mentioning the author.
9+
*/
10+
11+
'use strict'
12+
13+
var util = require('util')
14+
var helpers = require('./helpers')
15+
var CodeBuilder = require('../../helpers/code-builder')
16+
17+
module.exports = function (source, options) {
18+
var opts = util._extend({
19+
timeout: '10',
20+
indent: ' ',
21+
pretty: true
22+
}, options)
23+
24+
var code = new CodeBuilder(opts.indent)
25+
26+
// Markers for headers to be created as litteral objects and later be set on the NSURLRequest if exist
27+
var req = {
28+
hasHeaders: false,
29+
hasBody: false
30+
}
31+
32+
// We just want to make sure people understand that is the only dependency
33+
code.push('import Foundation')
34+
35+
if (Object.keys(source.allHeaders).length) {
36+
req.hasHeaders = true
37+
code.blank()
38+
.push(helpers.literalDeclaration('headers', source.allHeaders, opts))
39+
}
40+
41+
if (source.postData.text || source.postData.jsonObj || source.postData.params) {
42+
req.hasBody = true
43+
44+
switch (source.postData.mimeType) {
45+
case 'application/x-www-form-urlencoded':
46+
// By appending parameters one by one in the resulting snippet,
47+
// we make it easier for the user to edit it according to his or her needs after pasting.
48+
// The user can just add/remove lines adding/removing body parameters.
49+
code.blank()
50+
.push(util.format('var postData = NSMutableData(data: "%s=%s".dataUsingEncoding(NSUTF8StringEncoding)!)', source.postData.params[0].name, source.postData.params[0].value))
51+
for (var i = 1, len = source.postData.params.length; i < len; i++) {
52+
code.push(util.format('postData.appendData("&%s=%s".dataUsingEncoding(NSUTF8StringEncoding)!)', source.postData.params[i].name, source.postData.params[i].value))
53+
}
54+
break
55+
56+
case 'application/json':
57+
if (source.postData.jsonObj) {
58+
code.push(helpers.literalDeclaration('parameters', source.postData.jsonObj, opts))
59+
.blank()
60+
.push('let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)')
61+
}
62+
break
63+
64+
case 'multipart/form-data':
65+
/**
66+
* By appending multipart parameters one by one in the resulting snippet,
67+
* we make it easier for the user to edit it according to his or her needs after pasting.
68+
* The user can just edit the parameters NSDictionary or put this part of a snippet in a multipart builder method.
69+
*/
70+
code.push(helpers.literalDeclaration('parameters', source.postData.params, opts))
71+
.blank()
72+
.push(util.format('let boundary = "%s"', source.postData.boundary))
73+
.blank()
74+
.push('var body = ""')
75+
.push('var error: NSError? = nil')
76+
.push('for param in parameters {')
77+
.push(1, 'let paramName = param["name"]!')
78+
.push(1, 'body += "--\\(boundary)\\r\\n"')
79+
.push(1, 'body += "Content-Disposition:form-data; name=\\"\\(paramName)\\""')
80+
.push(1, 'if let filename = param["fileName"] {')
81+
.push(2, 'let contentType = param["content-type"]!')
82+
.push(2, 'let fileContent = String(contentsOfFile: filename, encoding: NSUTF8StringEncoding, error: &error)')
83+
.push(2, 'if (error != nil) {')
84+
.push(3, 'println(error)')
85+
.push(2, '}')
86+
.push(2, 'body += "; filename=\\"\\(filename)\\"\\r\\n"')
87+
.push(2, 'body += "Content-Type: \\(contentType)\\r\\n\\r\\n"')
88+
.push(2, 'body += fileContent!')
89+
.push(1, '} else if let paramValue = param["value"] {')
90+
.push(2, 'body += "\\r\\n\\r\\n\\(paramValue)"')
91+
.push(1, '}')
92+
.push('}')
93+
break
94+
95+
default:
96+
code.blank()
97+
.push(util.format('let postData = NSData(data: "%s".dataUsingEncoding(NSUTF8StringEncoding)!)', source.postData.text))
98+
}
99+
}
100+
101+
code.blank()
102+
// NSURLRequestUseProtocolCachePolicy is the default policy, let's just always set it to avoid confusion.
103+
.push(util.format('var request = NSMutableURLRequest(URL: NSURL(string: "%s")!,', source.fullUrl))
104+
.push(' cachePolicy: .UseProtocolCachePolicy,')
105+
.push(util.format(' timeoutInterval: %s)', parseInt(opts.timeout, 10).toFixed(1)))
106+
.push(util.format('request.HTTPMethod = "%s"', source.method))
107+
108+
if (req.hasHeaders) {
109+
code.push('request.allHTTPHeaderFields = headers')
110+
}
111+
112+
if (req.hasBody) {
113+
code.push('request.HTTPBody = postData')
114+
}
115+
116+
code.blank()
117+
// Retrieving the shared session will be less verbose than creating a new one.
118+
.push('let session = NSURLSession.sharedSession()')
119+
.push('let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in')
120+
.push(1, 'if (error != nil) {')
121+
.push(2, 'println(error)')
122+
.push(1, '} else {')
123+
// Casting the NSURLResponse to NSHTTPURLResponse so the user can see the status .
124+
.push(2, 'let httpResponse = response as? NSHTTPURLResponse')
125+
.push(2, 'println(httpResponse)')
126+
.push(1, '}')
127+
.push('})')
128+
.blank()
129+
.push('dataTask.resume()')
130+
131+
return code.join()
132+
}
133+
134+
module.exports.info = {
135+
key: 'nsurlsession',
136+
title: 'NSURLSession',
137+
link: 'https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html',
138+
description: 'Foundation\'s NSURLSession request'
139+
}

‎test/fixtures/available-targets.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,20 @@
145145
}
146146
]
147147
},
148+
{
149+
"key": "swift",
150+
"title": "Swift",
151+
"extname": ".swift",
152+
"default": "nsurlsession",
153+
"clients": [
154+
{
155+
"key": "nsurlsession",
156+
"title": "NSURLSession",
157+
"link": "https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html",
158+
"description": "Foundation's NSURLSession request"
159+
}
160+
]
161+
},
148162
{
149163
"key": "go",
150164
"title": "Go",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Foundation
2+
3+
let headers = ["content-type": "application/x-www-form-urlencoded"]
4+
5+
var postData = NSMutableData(data: "foo=bar".dataUsingEncoding(NSUTF8StringEncoding)!)
6+
postData.appendData("&hello=world".dataUsingEncoding(NSUTF8StringEncoding)!)
7+
8+
var request = NSMutableURLRequest(URL: NSURL(string: "http://mockbin.com/har")!,
9+
cachePolicy: .UseProtocolCachePolicy,
10+
timeoutInterval: 10.0)
11+
request.HTTPMethod = "POST"
12+
request.allHTTPHeaderFields = headers
13+
request.HTTPBody = postData
14+
15+
let session = NSURLSession.sharedSession()
16+
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
17+
if (error != nil) {
18+
println(error)
19+
} else {
20+
let httpResponse = response as? NSHTTPURLResponse
21+
println(httpResponse)
22+
}
23+
})
24+
25+
dataTask.resume()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
3+
let headers = ["content-type": "application/json"]
4+
let parameters = [
5+
"number": 1,
6+
"string": "f\"oo",
7+
"arr": [1, 2, 3],
8+
"nested": ["a": "b"],
9+
"arr_mix": [1, "a", ["arr_mix_nested": []]]
10+
]
11+
12+
let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)
13+
14+
var request = NSMutableURLRequest(URL: NSURL(string: "http://mockbin.com/har")!,
15+
cachePolicy: .UseProtocolCachePolicy,
16+
timeoutInterval: 10.0)
17+
request.HTTPMethod = "POST"
18+
request.allHTTPHeaderFields = headers
19+
request.HTTPBody = postData
20+
21+
let session = NSURLSession.sharedSession()
22+
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
23+
if (error != nil) {
24+
println(error)
25+
} else {
26+
let httpResponse = response as? NSHTTPURLResponse
27+
println(httpResponse)
28+
}
29+
})
30+
31+
dataTask.resume()

0 commit comments

Comments
 (0)