How do I do base64 encoding on iOS?
This is a good use case for Objective C categories.
For Base64 encoding:
#import <Foundation/NSString.h>@interface NSString (NSStringAdditions)+ (NSString *) base64StringFromData:(NSData *)data length:(int)length;@end-------------------------------------------#import "NSStringAdditions.h"static char base64EncodingTable[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};@implementation NSString (NSStringAdditions)+ (NSString *) base64StringFromData: (NSData *)data length: (int)length { unsigned long ixtext, lentext; long ctremaining; unsigned char input[3], output[4]; short i, charsonline = 0, ctcopy; const unsigned char *raw; NSMutableString *result; lentext = [data length]; if (lentext < 1) return @""; result = [NSMutableString stringWithCapacity: lentext]; raw = [data bytes]; ixtext = 0; while (true) { ctremaining = lentext - ixtext; if (ctremaining <= 0) break; for (i = 0; i < 3; i++) { unsigned long ix = ixtext + i; if (ix < lentext) input[i] = raw[ix]; else input[i] = 0; } output[0] = (input[0] & 0xFC) >> 2; output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4); output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6); output[3] = input[2] & 0x3F; ctcopy = 4; switch (ctremaining) { case 1: ctcopy = 2; break; case 2: ctcopy = 3; break; } for (i = 0; i < ctcopy; i++) [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]]; for (i = ctcopy; i < 4; i++) [result appendString: @"="]; ixtext += 3; charsonline += 4; if ((length > 0) && (charsonline >= length)) charsonline = 0; } return result;}@end
For Base64 decoding:
#import <Foundation/Foundation.h>@class NSString;@interface NSData (NSDataAdditions)+ (NSData *) base64DataFromString:(NSString *)string;@end-------------------------------------------#import "NSDataAdditions.h"@implementation NSData (NSDataAdditions)+ (NSData *)base64DataFromString: (NSString *)string{ unsigned long ixtext, lentext; unsigned char ch, inbuf[4], outbuf[3]; short i, ixinbuf; Boolean flignore, flendtext = false; const unsigned char *tempcstring; NSMutableData *theData; if (string == nil) { return [NSData data]; } ixtext = 0; tempcstring = (const unsigned char *)[string UTF8String]; lentext = [string length]; theData = [NSMutableData dataWithCapacity: lentext]; ixinbuf = 0; while (true) { if (ixtext >= lentext) { break; } ch = tempcstring [ixtext++]; flignore = false; if ((ch >= 'A') && (ch <= 'Z')) { ch = ch - 'A'; } else if ((ch >= 'a') && (ch <= 'z')) { ch = ch - 'a' + 26; } else if ((ch >= '0') && (ch <= '9')) { ch = ch - '0' + 52; } else if (ch == '+') { ch = 62; } else if (ch == '=') { flendtext = true; } else if (ch == '/') { ch = 63; } else { flignore = true; } if (!flignore) { short ctcharsinbuf = 3; Boolean flbreak = false; if (flendtext) { if (ixinbuf == 0) { break; } if ((ixinbuf == 1) || (ixinbuf == 2)) { ctcharsinbuf = 1; } else { ctcharsinbuf = 2; } ixinbuf = 3; flbreak = true; } inbuf [ixinbuf++] = ch; if (ixinbuf == 4) { ixinbuf = 0; outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4); outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2); outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F); for (i = 0; i < ctcharsinbuf; i++) { [theData appendBytes: &outbuf[i] length: 1]; } } if (flbreak) { break; } } } return theData;} @end
A really, really fast implementation which was ported (and modified/improved) from the PHP Core library into native Objective-C code is available in the QSStrings Class from the QSUtilities Library. I did a quick benchmark: a 5.3MB image (JPEG) file took < 50ms to encode, and about 140ms to decode.
The code for the entire library (including the Base64 Methods) are available on GitHub.
Or alternatively, if you want the code to just the Base64 methods themselves, I've posted it here:
First, you need the mapping tables:
static const char _base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";static const short _base64DecodingTable[256] = { -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2};
To Encode:
+ (NSString *)encodeBase64WithString:(NSString *)strData { return [QSStrings encodeBase64WithData:[strData dataUsingEncoding:NSUTF8StringEncoding]];}+ (NSString *)encodeBase64WithData:(NSData *)objData { const unsigned char * objRawData = [objData bytes]; char * objPointer; char * strResult; // Get the Raw Data length and ensure we actually have data int intLength = [objData length]; if (intLength == 0) return nil; // Setup the String-based Result placeholder and pointer within that placeholder strResult = (char *)calloc((((intLength + 2) / 3) * 4) + 1, sizeof(char)); objPointer = strResult; // Iterate through everything while (intLength > 2) { // keep going until we have less than 24 bits *objPointer++ = _base64EncodingTable[objRawData[0] >> 2]; *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)]; *objPointer++ = _base64EncodingTable[((objRawData[1] & 0x0f) << 2) + (objRawData[2] >> 6)]; *objPointer++ = _base64EncodingTable[objRawData[2] & 0x3f]; // we just handled 3 octets (24 bits) of data objRawData += 3; intLength -= 3; } // now deal with the tail end of things if (intLength != 0) { *objPointer++ = _base64EncodingTable[objRawData[0] >> 2]; if (intLength > 1) { *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)]; *objPointer++ = _base64EncodingTable[(objRawData[1] & 0x0f) << 2]; *objPointer++ = '='; } else { *objPointer++ = _base64EncodingTable[(objRawData[0] & 0x03) << 4]; *objPointer++ = '='; *objPointer++ = '='; } } // Terminate the string-based result *objPointer = '\0'; // Create result NSString object NSString *base64String = [NSString stringWithCString:strResult encoding:NSASCIIStringEncoding]; // Free memory free(strResult); return base64String;}
To Decode:
+ (NSData *)decodeBase64WithString:(NSString *)strBase64 { const char *objPointer = [strBase64 cStringUsingEncoding:NSASCIIStringEncoding]; size_t intLength = strlen(objPointer); int intCurrent; int i = 0, j = 0, k; unsigned char *objResult = calloc(intLength, sizeof(unsigned char)); // Run through the whole string, converting as we go while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) { if (intCurrent == '=') { if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) { // the padding character is invalid at this point -- so this entire string is invalid free(objResult); return nil; } continue; } intCurrent = _base64DecodingTable[intCurrent]; if (intCurrent == -1) { // we're at a whitespace -- simply skip over continue; } else if (intCurrent == -2) { // we're at an invalid character free(objResult); return nil; } switch (i % 4) { case 0: objResult[j] = intCurrent << 2; break; case 1: objResult[j++] |= intCurrent >> 4; objResult[j] = (intCurrent & 0x0f) << 4; break; case 2: objResult[j++] |= intCurrent >>2; objResult[j] = (intCurrent & 0x03) << 6; break; case 3: objResult[j++] |= intCurrent; break; } i++; } // mop things up if we ended on a boundary k = j; if (intCurrent == '=') { switch (i % 4) { case 1: // Invalid state free(objResult); return nil; case 2: k++; // flow through case 3: objResult[k] = 0; } } // Cleanup and setup the return NSData NSData * objData = [[[NSData alloc] initWithBytes:objResult length:j] autorelease]; free(objResult); return objData;}
At the time this question was originally posted, people were understandably directing you to third-party base 64 libraries because of the lack of any native routines. But iOS 7 introduced base 64 encoding routines (which actually simply just exposes private methods iOS had going back to iOS 4).
So, you can use the NSData
method base64EncodedStringWithOptions:
to create a base-64 string from a NSData
.
NSString *string = [data base64EncodedStringWithOptions:kNilOptions];
And you can use initWithBase64EncodedString:options:
to convert a base-64 string back to a NSData
:
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions];
Or, in Swift:
let string = data.base64EncodedString()
And
let data = Data(base64Encoded: string)