ahmadnassri/httpsnippet

View on GitHub
src/targets/objc/nsurlsession/client.ts

Summary

Maintainability
A
2 hrs
Test Coverage
/**
 * @description
 * HTTP code snippet generator for Objective-C using NSURLSession.
 *
 * @author
 * @thibaultCha
 *
 * for any questions or issues regarding the generated code snippet, please open an issue mentioning the author.
 */

import { CodeBuilder } from '../../../helpers/code-builder';
import { Client } from '../../targets';
import { nsDeclaration } from '../helpers';

export interface NsurlsessionOptions {
  pretty?: boolean;
  timeout?: number;
}

export const nsurlsession: Client<NsurlsessionOptions> = {
  info: {
    key: 'nsurlsession',
    title: 'NSURLSession',
    link: 'https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/index.html',
    description: "Foundation's NSURLSession request",
  },
  convert: ({ allHeaders, postData, method, fullUrl }, options) => {
    const opts = {
      indent: '    ',
      pretty: true,
      timeout: 10,
      ...options,
    };

    const { push, join, blank } = new CodeBuilder({ indent: opts.indent });
    // Markers for headers to be created as literal objects and later be set on the NSURLRequest if exist
    const req = {
      hasHeaders: false,
      hasBody: false,
    };

    // We just want to make sure people understand that is the only dependency
    push('#import <Foundation/Foundation.h>');

    if (Object.keys(allHeaders).length) {
      req.hasHeaders = true;
      blank();
      push(nsDeclaration('NSDictionary', 'headers', allHeaders, opts.pretty));
    }

    if (postData.text || postData.jsonObj || postData.params) {
      req.hasBody = true;

      switch (postData.mimeType) {
        case 'application/x-www-form-urlencoded':
          if (postData.params?.length) {
            // By appending parameters one by one in the resulting snippet,
            // we make it easier for the user to edit it according to his or her needs after pasting.
            // The user can just add/remove lines adding/removing body parameters.
            blank();

            const [head, ...tail] = postData.params;
            push(
              `NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"${head.name}=${head.value}" dataUsingEncoding:NSUTF8StringEncoding]];`,
            );

            tail.forEach(({ name, value }) => {
              push(
                `[postData appendData:[@"&${name}=${value}" dataUsingEncoding:NSUTF8StringEncoding]];`,
              );
            });
          } else {
            req.hasBody = false;
          }
          break;

        case 'application/json':
          if (postData.jsonObj) {
            push(nsDeclaration('NSDictionary', 'parameters', postData.jsonObj, opts.pretty));
            blank();
            push(
              'NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];',
            );
          }
          break;

        case 'multipart/form-data':
          // By appending multipart parameters one by one in the resulting snippet,
          // we make it easier for the user to edit it according to his or her needs after pasting.
          // The user can just edit the parameters NSDictionary or put this part of a snippet in a multipart builder method.
          push(nsDeclaration('NSArray', 'parameters', postData.params || [], opts.pretty));
          push(`NSString *boundary = @"${postData.boundary}";`);
          blank();
          push('NSError *error;');
          push('NSMutableString *body = [NSMutableString string];');
          push('for (NSDictionary *param in parameters) {');
          push('[body appendFormat:@"--%@\\r\\n", boundary];', 1);
          push('if (param[@"fileName"]) {', 1);
          push(
            '[body appendFormat:@"Content-Disposition:form-data; name=\\"%@\\"; filename=\\"%@\\"\\r\\n", param[@"name"], param[@"fileName"]];',
            2,
          );
          push('[body appendFormat:@"Content-Type: %@\\r\\n\\r\\n", param[@"contentType"]];', 2);
          push(
            '[body appendFormat:@"%@", [NSString stringWithContentsOfFile:param[@"fileName"] encoding:NSUTF8StringEncoding error:&error]];',
            2,
          );
          push('if (error) {', 2);
          push('NSLog(@"%@", error);', 3);
          push('}', 2);
          push('} else {', 1);
          push(
            '[body appendFormat:@"Content-Disposition:form-data; name=\\"%@\\"\\r\\n\\r\\n", param[@"name"]];',
            2,
          );
          push('[body appendFormat:@"%@", param[@"value"]];', 2);
          push('}', 1);
          push('}');
          push('[body appendFormat:@"\\r\\n--%@--\\r\\n", boundary];');
          push('NSData *postData = [body dataUsingEncoding:NSUTF8StringEncoding];');
          break;

        default:
          blank();
          push(
            `NSData *postData = [[NSData alloc] initWithData:[@"${postData.text}" dataUsingEncoding:NSUTF8StringEncoding]];`,
          );
      }
    }

    blank();
    push(
      `NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"${fullUrl}"]`,
    );

    // NSURLRequestUseProtocolCachePolicy is the default policy, let's just always set it to avoid confusion.
    push(
      '                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy',
    );
    push(
      `                                                   timeoutInterval:${opts.timeout.toFixed(
        1,
      )}];`,
    );
    push(`[request setHTTPMethod:@"${method}"];`);

    if (req.hasHeaders) {
      push('[request setAllHTTPHeaderFields:headers];');
    }

    if (req.hasBody) {
      push('[request setHTTPBody:postData];');
    }

    blank();

    // Retrieving the shared session will be less verbose than creating a new one.
    push('NSURLSession *session = [NSURLSession sharedSession];');
    push('NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request');
    push(
      '                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {',
    );
    push('                                            if (error) {', 1);
    push('                                            NSLog(@"%@", error);', 2);
    push('                                            } else {', 1);

    // Casting the NSURLResponse to NSHTTPURLResponse so the user can see the status     .
    push(
      '                                            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;',
      2,
    );
    push('                                            NSLog(@"%@", httpResponse);', 2);
    push('                                            }', 1);
    push('                                            }];');
    push('[dataTask resume];');

    return join();
  },
};