Skip to content

Commit 22e0428

Browse files
committed
Merge pull request #16 from thibaultCha/feature/objc/native
objc: rewrote native with improved syntax
2 parents d4ef9dd + f5cea0b commit 22e0428

File tree

16 files changed

+420
-81
lines changed

16 files changed

+420
-81
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ See it in action on companion service: [APIembed](https://apiembed.com/)
2424

2525
## Targets
2626

27-
currently the following output [targets](/src/targets) are supported:
27+
Currently the following output [targets](/src/targets) are supported:
2828

2929
- [cURL](http://curl.haxx.se/)
3030
- [Go](http://golang.org/pkg/net/http/#NewRequest)
@@ -41,6 +41,8 @@ currently the following output [targets](/src/targets) are supported:
4141
- Python
4242
- [Python 3](https://docs.python.org/3/library/http.client.html)
4343
- [Wget](https://www.gnu.org/software/wget/)
44+
- Objective-C
45+
- [NSURLSession](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html)
4446

4547
## Installation
4648

@@ -102,6 +104,7 @@ process single file (assumes [HAR Request Object](http://www.softwareishard.com/
102104
}
103105
}
104106
```
107+
105108
```shell
106109
httpsnippet my-api-endpoint.json --target php --output ./snippets
107110
```
@@ -149,7 +152,7 @@ console.log(snippet.convert('curl', {
149152
indent: '\t';
150153
}));
151154

152-
// generate nodeJS output
155+
// generate Node.js output
153156
console.log(snippet.convert('node'));
154157

155158
// generate PHP output
@@ -223,6 +226,16 @@ module.exports.info = {
223226
| `indent` | ` ` | line break & indent output value, set to `false` to disable line breaks |
224227
| `verbose` | `false` | by default, `--quiet` is always used, unless `verbose` is set to `true` |
225228

229+
### Objective-C
230+
231+
##### Native
232+
233+
| Option | Default | Description |
234+
| --------- | ------- | ------------------------------------------------------------------------ |
235+
| `timeout` | `10` | NSURLRequest timeout |
236+
| `indent` | ` ` | line break & indent output value, set to `false` to disable line |
237+
| `pretty` | `true` | indent extracted headers/parameters in `NSDictionary` litterals |
238+
226239
## Bugs and feature requests
227240

228241
Have a bug or a feature request? Please first read the [issue guidelines](CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](/issues).

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ var HTTPSnippet = function (req, lang) {
8888
this.source.postData.text += data
8989
}.bind(this)))
9090

91+
this.source.postData.boundary = form.getBoundary()
9192
this.source.headersObj['content-type'] = 'multipart/form-data; boundary=' + form.getBoundary()
9293
break
9394

src/targets/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
'use strict'
22

3-
module.exports = require('require-directory')(module)
3+
module.exports = require('require-directory')(module, { exclude: /helpers\.js$/ })

src/targets/objc/helpers.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict'
2+
3+
var util = require('util')
4+
5+
module.exports = {
6+
/**
7+
* Create an string of given length filled with blank spaces
8+
*
9+
* @param {number} length Length of the array to return
10+
* @return {string}
11+
*/
12+
blankString: function (length) {
13+
return Array.apply(null, new Array(length)).map(String.prototype.valueOf, ' ').join('')
14+
},
15+
16+
/**
17+
* Create a string corresponding to a valid NSDictionary declaration and initialization for Objective-C.
18+
*
19+
* @param {string} name Desired name of the NSDictionary instance
20+
* @param {Object} parameters Key-value object of parameters to translate to an Objective-C NSDictionary litearal
21+
* @param {boolean} indent If true, will declare the NSDictionary litteral by indenting each new key/value pair.
22+
* @return {string} A valid Objective-C declaration and initialization of an NSDictionary.
23+
*
24+
* @example
25+
* nsDictionaryBuilder('params', {a: 'b', c: 'd'}, true)
26+
* // returns:
27+
* NSDictionary *params = @{ @"a": @"b",
28+
* @"c": @"d" };
29+
*
30+
* nsDictionaryBuilder('params', {a: 'b', c: 'd'})
31+
* // returns:
32+
* NSDictionary *params = @{ @"a": @"b", @"c": @"d" };
33+
*/
34+
nsDictionaryBuilder: function (name, parameters, indent) {
35+
var dicOpening = 'NSDictionary *' + name + ' = '
36+
var dicLiteral = this.literalRepresentation(parameters, indent ? dicOpening.length : undefined)
37+
return dicOpening + dicLiteral + ';'
38+
},
39+
40+
/**
41+
* Similar to nsDictionaryBuilder but for NSArray literals.
42+
* @see nsDictionaryBuilder
43+
*/
44+
nsArrayBuilder: function (name, parameters, indent) {
45+
var arrOpening = 'NSArray *' + name + ' = '
46+
var arrLiteral = this.literalRepresentation(parameters, indent ? arrOpening.length : undefined)
47+
return arrOpening + arrLiteral + ';'
48+
},
49+
50+
/**
51+
* Create a valid Objective-C string of a literal value according to its type.
52+
*
53+
* @param {*} value Any JavaScript literal
54+
* @return {string}
55+
*/
56+
literalRepresentation: function (value, indentation) {
57+
var join = indentation === undefined ? ', ' : ',\n ' + this.blankString(indentation)
58+
59+
switch (Object.prototype.toString.call(value)) {
60+
case '[object Number]':
61+
return '@' + value
62+
case '[object Array]':
63+
var values_representation = value.map(function (v) {
64+
return this.literalRepresentation(v)
65+
}.bind(this))
66+
return '@[ ' + values_representation.join(join) + ' ]'
67+
case '[object Object]':
68+
var keyValuePairs = []
69+
for (var k in value) {
70+
keyValuePairs.push(util.format('@"%s": %s', k, this.literalRepresentation(value[k])))
71+
}
72+
return '@{ ' + keyValuePairs.join(join) + ' }'
73+
default:
74+
return '@"' + value.replace(/"/g, '\\"') + '"'
75+
}
76+
}
77+
}

src/targets/objc/native.js

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,109 @@
11
'use strict'
22

33
var util = require('util')
4+
var objcHelpers = require('./helpers')
45

56
module.exports = function (source, options) {
67
var opts = util._extend({
7-
timeout: '10'
8+
timeout: '10',
9+
indent: ' ',
10+
pretty: true
811
}, options)
912

1013
var code = []
1114

12-
// Dependencies
15+
var req = {
16+
hasHeaders: false,
17+
hasBody: false
18+
}
19+
20+
var indent = opts.indent
21+
1322
code.push('#import <Foundation/Foundation.h>')
23+
24+
if (Object.keys(source.allHeaders).length) {
25+
req.hasHeaders = true
26+
code.push(null)
27+
code.push(objcHelpers.nsDictionaryBuilder('headers', source.allHeaders, opts.pretty))
28+
}
29+
30+
if (source.postData.text || source.postData.jsonObj || source.postData.params) {
31+
req.hasBody = true
32+
33+
switch (source.postData.mimeType) {
34+
case 'application/x-www-form-urlencoded':
35+
code.push(null)
36+
// Makes it easier to implement logice than just putting the entire body string
37+
code.push(util.format('NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"%s=%s" dataUsingEncoding:NSUTF8StringEncoding]];', source.postData.params[0].name, source.postData.params[0].value))
38+
for (var i = 1, len = source.postData.params.length; i < len; i++) {
39+
code.push(util.format('[postData appendData:[@"&%s=%s" dataUsingEncoding:NSUTF8StringEncoding]];', source.postData.params[i].name, source.postData.params[i].value))
40+
}
41+
break
42+
43+
case 'application/json':
44+
if (source.postData.jsonObj) {
45+
code.push(objcHelpers.nsDictionaryBuilder('parameters', source.postData.jsonObj, opts.pretty))
46+
code.push(null)
47+
code.push('NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];')
48+
}
49+
break
50+
51+
case 'multipart/form-data':
52+
code.push(objcHelpers.nsArrayBuilder('parameters', source.postData.params, opts.pretty))
53+
code.push(util.format('NSString *boundary = @"%s";', source.postData.boundary))
54+
code.push(null)
55+
code.push('NSError *error;')
56+
code.push('NSMutableString *body = [NSMutableString string];')
57+
code.push('for (NSDictionary *param in parameters) {')
58+
code.push(indent + '[body appendFormat:@"--%@\\r\\n", boundary];')
59+
code.push(indent + 'if (param[@"fileName"]) {')
60+
code.push(indent + indent + '[body appendFormat:@"Content-Disposition:form-data; name=\\"%@\\"; filename=\\"%@\\"\\r\\n", param[@"name"], param[@"fileName"]];')
61+
code.push(indent + indent + '[body appendFormat:@"Content-Type: %@\\r\\n\\r\\n", param[@"contentType"]];')
62+
code.push(indent + indent + '[body appendFormat:@"%@", [[NSString alloc] initWithContentsOfFile:param[@"fileName"]')
63+
code.push(indent + indent + ' encoding:NSUTF8StringEncoding error:&error]];')
64+
code.push(indent + indent + 'if (error) {')
65+
code.push(indent + indent + indent + 'NSLog(@"%@", error);')
66+
code.push(indent + indent + '}')
67+
code.push(indent + '} else {')
68+
code.push(indent + indent + '[body appendFormat:@"Content-Disposition:form-data; name=\\"%@\\"\\r\\n\\r\\n", param[@"name"]];')
69+
code.push(indent + indent + '[body appendFormat:@"%@", param[@"value"]];')
70+
code.push(indent + '}')
71+
code.push('}')
72+
code.push('[body appendFormat:@"\\r\\n--%@--\\r\\n", boundary];')
73+
code.push('NSData *postData = [[NSData alloc] initWithData:[body dataUsingEncoding:NSUTF8StringEncoding]];')
74+
break
75+
76+
default:
77+
code.push(null)
78+
code.push('NSData *postData = [[NSData alloc] initWithData:[@"' + source.postData.text + '" dataUsingEncoding:NSUTF8StringEncoding]];')
79+
}
80+
}
81+
1482
code.push(null)
15-
code.push('NSURLSession *session = [NSURLSession sharedSession];')
16-
code.push(null)
17-
// Create request object
1883
code.push('NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"' + source.fullUrl + '"]')
1984
code.push(' cachePolicy:NSURLRequestUseProtocolCachePolicy')
2085
code.push(' timeoutInterval:' + parseInt(opts.timeout, 10).toFixed(1) + '];')
2186
code.push('[request setHTTPMethod:@"' + source.method + '"];')
2287

23-
// Set headers
24-
Object.keys(source.allHeaders).sort().map(function (key) {
25-
code.push('[request setValue:@"' + source.allHeaders[key] + '" forHTTPHeaderField:@"' + key + '"];')
26-
})
27-
28-
// Set request body
29-
if (source.postData && (source.postData.params || source.postData.text)) {
30-
code.push(null)
31-
32-
if (source.postData.mimeType === 'application/x-www-form-urlencoded' && source.postData.params) {
33-
var params = source.postData.params
34-
code.push('NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"' + params[0].name + '=' + params[0].value + '" dataUsingEncoding:NSUTF8StringEncoding]];')
35-
for (var i = 1, len = params.length; i < len; i++) {
36-
code.push('[postData appendData:[@"&' + params[i].name + '=' + params[i].value + '" dataUsingEncoding:NSUTF8StringEncoding]];')
37-
}
38-
} else if (source.postData.mimeType === 'application/json' && source.postData.text) {
39-
code.push('NSData *postData = [[NSData alloc] initWithData:[@' + JSON.stringify(source.postData.text) + ' dataUsingEncoding:NSUTF8StringEncoding]];')
40-
} else if (source.postData.text) {
41-
code.push('NSData *postData = [[NSData alloc] initWithData:[@"' + source.postData.text + '" dataUsingEncoding:NSUTF8StringEncoding]];')
42-
}
88+
if (req.hasHeaders) {
89+
code.push('[request setAllHTTPHeaderFields:headers];')
90+
}
4391

92+
if (req.hasBody) {
4493
code.push('[request setHTTPBody:postData];')
4594
}
4695

47-
// Set completion block
4896
code.push(null)
97+
code.push('NSURLSession *session = [NSURLSession sharedSession];') // Retrieve shared session for simplicity
4998
code.push('NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request')
5099
code.push(' completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {')
51-
code.push(null)
52-
code.push('}];')
53-
54-
// Start request
55-
code.push(null)
100+
code.push(' ' + indent + 'if (error) {')
101+
code.push(' ' + indent + indent + 'NSLog(@"%@", error);')
102+
code.push(' ' + indent + '} else {')
103+
code.push(' ' + indent + indent + 'NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;')
104+
code.push(' ' + indent + indent + 'NSLog(@"%@", httpResponse);')
105+
code.push(' ' + indent + '}')
106+
code.push(' }];')
56107
code.push('[dataTask resume];')
57108

58109
return code.join('\n')
@@ -62,5 +113,5 @@ module.exports.info = {
62113
key: 'native',
63114
title: 'NSURLSession',
64115
link: 'https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html',
65-
description: "Foundation's NSURLSession request"
116+
description: 'Foundation\'s NSURLSession request'
66117
}
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
#import <Foundation/Foundation.h>
22

3-
NSURLSession *session = [NSURLSession sharedSession];
3+
NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };
4+
5+
NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"foo=bar" dataUsingEncoding:NSUTF8StringEncoding]];
6+
[postData appendData:[@"&hello=world" dataUsingEncoding:NSUTF8StringEncoding]];
47

58
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://mockbin.com/har"]
69
cachePolicy:NSURLRequestUseProtocolCachePolicy
710
timeoutInterval:10.0];
811
[request setHTTPMethod:@"POST"];
9-
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
10-
11-
NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"foo=bar" dataUsingEncoding:NSUTF8StringEncoding]];
12-
[postData appendData:[@"&hello=world" dataUsingEncoding:NSUTF8StringEncoding]];
12+
[request setAllHTTPHeaderFields:headers];
1313
[request setHTTPBody:postData];
1414

15+
NSURLSession *session = [NSURLSession sharedSession];
1516
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
1617
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
17-
18-
}];
19-
18+
if (error) {
19+
NSLog(@"%@", error);
20+
} else {
21+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
22+
NSLog(@"%@", httpResponse);
23+
}
24+
}];
2025
[dataTask resume];
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
#import <Foundation/Foundation.h>
22

3-
NSURLSession *session = [NSURLSession sharedSession];
3+
NSDictionary *headers = @{ @"content-type": @"application/json" };
4+
NSDictionary *parameters = @{ @"number": @1,
5+
@"string": @"f\"oo",
6+
@"arr": @[ @1, @2, @3 ],
7+
@"nested": @{ @"a": @"b" },
8+
@"arr_mix": @[ @1, @"a", @{ @"arr_mix_nested": @{ } } ] };
9+
10+
NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];
411

512
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://mockbin.com/har"]
613
cachePolicy:NSURLRequestUseProtocolCachePolicy
714
timeoutInterval:10.0];
815
[request setHTTPMethod:@"POST"];
9-
[request setValue:@"application/json" forHTTPHeaderField:@"content-type"];
10-
11-
NSData *postData = [[NSData alloc] initWithData:[@"{\"number\": 1, \"string\": \"f\\\"oo\", \"arr\": [1, 2, 3], \"nested\": {\"a\": \"b\"}, \"arr_mix\": [1, \"a\", {\"arr_mix_nested\": {}}] }" dataUsingEncoding:NSUTF8StringEncoding]];
16+
[request setAllHTTPHeaderFields:headers];
1217
[request setHTTPBody:postData];
1318

19+
NSURLSession *session = [NSURLSession sharedSession];
1420
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
1521
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
16-
17-
}];
18-
22+
if (error) {
23+
NSLog(@"%@", error);
24+
} else {
25+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
26+
NSLog(@"%@", httpResponse);
27+
}
28+
}];
1929
[dataTask resume];
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
#import <Foundation/Foundation.h>
22

3-
NSURLSession *session = [NSURLSession sharedSession];
3+
NSDictionary *headers = @{ @"cookie": @"foo=bar; bar=baz" };
44

55
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://mockbin.com/har"]
66
cachePolicy:NSURLRequestUseProtocolCachePolicy
77
timeoutInterval:10.0];
88
[request setHTTPMethod:@"POST"];
9-
[request setValue:@"foo=bar; bar=baz" forHTTPHeaderField:@"cookie"];
9+
[request setAllHTTPHeaderFields:headers];
1010

11+
NSURLSession *session = [NSURLSession sharedSession];
1112
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
1213
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
13-
14-
}];
15-
14+
if (error) {
15+
NSLog(@"%@", error);
16+
} else {
17+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
18+
NSLog(@"%@", httpResponse);
19+
}
20+
}];
1621
[dataTask resume];

0 commit comments

Comments
 (0)