Search code examples
iosswiftdatetimeafnetworkingurl-encoding

AFNetworking URL parameter encoding for datetime with + and : sign


I'm using AFNetworking for iOS and I want to send a request with a query parameter which has a datetime as a value. The wanted behavior should be:

Original: 2016-07-04T14:30:21+0200
Encoded:  2016-07-04T14%3A30%3A21%2B0200
Example:  .../?datetime=2016-07-04T14%3A30%3A21%2B0200

AFNetworking does string encoding by itself which doesn't include special characters like + / & : and a few more (Wikipedia: Percent-encoding), which is fine since they are reserved. So I have to encode the value of my datetime another way to escape the plus and colon sign. But when I manually encode the value before AFNetworking does it escapes the % twice obviously. So it puts a %25 for each %

2016-07-04T14%253A30%253A21%252B0200

I want AFNetworking to use percent encoding for the query with allowed characters like:

query.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLPathAllowedCharacterSet())

I didn't find a solution to change or disable the encoding by AFNetworking to do it completely manually. Do you have any suggestions?


Solution

  • After a little more research I've found a place to inject the encoding I want. This is the way it didn't work:

    ENCODING NOT WORKING

    Init the requestOperationManager:

    self.requestOperationManager = [[AFHTTPRequestOperationManager alloc] init];
    self.requestOperationManager.requestSerializer = [AFJSONRequestSerializer serializer];
    

    Use the requestOperationManager to init operations

    NSURLRequest *request = [NSURLRequest alloc] initWithURL:url]; // The problem is here
    AFHTTPRequestOperation *operation = [self.requestOperationManager HTTPRequestOperationWithRequest:urlRequest success:^(AFHTTPRequestOperation *operation, id responseObject) {
        // Success
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // Failure
    }];
    [self.requestOperationManager.operationQueue addOperation:operation];
    [operation start];
    

    WAY TO HAVE MORE CONTROL

    The AFHTTPRequestSerializer can also create requests and you can use your own serialization.

    Init the requestOperationManager and add a query string serialization block:

    self.requestOperationManager = [[AFHTTPRequestOperationManager alloc] init];
    self.requestOperationManager.requestSerializer = [AFJSONRequestSerializer serializer];
    [self.requestOperationManager.requestSerializer setQueryStringSerializationWithBlock:^NSString * _Nonnull(NSURLRequest * _Nonnull request, id _Nonnull parameters, NSError * _Nullable __autoreleasing * _Nullable error) {
        if ([parameters isKindOfClass:[NSString class]]) {
            NSString *yourEncodedParameterString = // What every you want to do with it.
            return yourEncodedParameterString;
        }
        return parameters;
    }];
    

    Now change how you create your NSURLRequest:

    NSString *method = @"GET";
    NSString *urlStringWithoutQuery = @"http://example.com/";
    NSString *query = @"datetime=2016-07-06T12:15:42+0200"
    NSMutableURLRequest *urlRequest = [self.requestOperationManager.requestSerializer requestWithMethod:method URLString:urlStringWithoutQuery parameters:query error:nil];
    

    It is important that you split your url. Use the url without the query for the URLString parameter and only the query for the parameters parameter. By using requestWithMethod:URLString:parameters:error it will call the query string serialization block you've provided above and encode the parameters as you want.

    AFHTTPRequestOperation *operation = [self.requestOperationManager HTTPRequestOperationWithRequest:urlRequest success:^(AFHTTPRequestOperation *operation, id responseObject) {
        // Success
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // Failure
    }];
    [self.requestOperationManager.operationQueue addOperation:operation];
    [operation start];