obj-c to Swift convert code - objective-c

So I'm trying to convert some obj-c code into swift but I'm stuck on a part of it.
How can I do something like this in swift?
- (void)sometMethod:(NSString *)s {
NSString * crlfString = [s stringByAppendingString:#"\r\n"];
uint8_t *buf = (uint8_t *)[crlfString UTF8String];}
The real problem is this line
uint8_t *buf = (uint8_t *)[crlfString UTF8String];

What do you want to do with the UTF-8 buffer? Generally, it is more convenient to convert the string directly to an NSData object. But you can also translate your Obj-C code one by one to Swift. Here are both variants:
import Foundation
func appendCRLFAndConvertToUTF8_1(s: String) -> NSData {
let crlfString: NSString = s.stringByAppendingString("\r\n")
let buffer = crlfString.UTF8String
let bufferLength = crlfString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let data = NSData(bytes: buffer, length: bufferLength)
return data;
}
func appendCRLFAndConvertToUTF8_2(s: String) -> NSData {
let crlfString = s + "\r\n"
return crlfString.dataUsingEncoding(NSUTF8StringEncoding)!
}
let s = "Hello 😄"
let data1 = appendCRLFAndConvertToUTF8_1(s)
data1.description
let data2 = appendCRLFAndConvertToUTF8_2(s)
data2.description
data1 == data2
And if you want to iterate over the UTF-8 code units and not deal with a buffer, use something like:
for codeUnit in String.utf8 {
println(codeUnit)
}

Related

convert (void *) to (SInt16 *) to Swift

I'm trying to convert the following from Objective-C to Swift:
-(int)fillBuffer:(void *)buffer {
SInt16* p = (SInt16 *)buffer;
// ...
p[33] = 0
}
I've come to understand that (void *) maps to the UnsafeMutableRawPointer? type in Swift.
However, I'm missing a step in converting it into something that can take the subscript operation.
So far, I've got this:
func fill(buffer: UnsafeMutableRawPointer!) -> Int {
buffer[33] = 0
}
Looking for feedback and advice. Thanks in advance!
Casting a void pointer to a typed pointer
SInt16* p = (SInt16 *)buffer;
is done in Swift with assumingMemoryBound():
func fillBuffer(_ buffer: UnsafeMutableRawPointer) -> Int {
let p = buffer.assumingMemoryBound(to: Int16.self)
// ...
p[33] = 0
return 0
}
Test code:
var i16Array = Array(repeating: Int16(99), count: 40)
print(i16Array[33]) // 99
_ = fillBuffer(&i16Array) // passes a pointer to the element storage to the function
print(i16Array[33]) // 0

Converting SHA1 string encryption code from objective-c to swift

So this is the original Objective-C code I have:
- (NSString *)calculateSignaturewithAPIKey:(NSString *)apiKey apiKeyPrivate:(NSString *)apiKeyPrivate httpMethod:(NSString *)httpMethod route:(NSString *)theRoute andExpiresIn:(NSString *)expireTime {
NSString *string_to_sign = [NSString stringWithFormat:#"%#:%#:%#:%#",apiKey,httpMethod,theRoute,expireTime];
const char *cKey = [apiKeyPrivate cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [string_to_sign cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *signature = [HMAC base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return signature;
}
In Swift 3 I managed to get as far as:
func calculateSignature(withPublicApiKey publicApiKey: String, andApiPrivateKey privateApiKey: String, withHttpMethod httpMethod: String, andRoute route: String, exiresIn expireTime: String) -> String {
let string_to_sign = "\(publicApiKey):\(httpMethod):\(route):\(expireTime)"
let cKey = privateApiKey.cString(using: String.Encoding.ascii)
let cData = Data.base64EncodedString(Data.init)
var cHMAC = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
But I don't know how to proceed here. I have been able to import Crypto related things into my Swift project. Please assist.
Try this:
func calculateSignature(withPublicApiKey publicApiKey: String, andApiPrivateKey privateApiKey: String, withHttpMethod httpMethod: String, andRoute route: String, exiresIn expireTime: String) -> String {
let string_to_sign = "\(publicApiKey):\(httpMethod):\(route):\(expireTime)"
let cKey = privateApiKey.cString(using: .ascii)!
let cData = string_to_sign.cString(using: .ascii)!
var cHMAC = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, cKey.count - 1, cData, cData.count - 1, &cHMAC)
let HMAC = Data(bytes: &cHMAC, count: Int(CC_SHA1_DIGEST_LENGTH))
return HMAC.base64EncodedString(options: .lineLength64Characters)
}
My personal experience is that Swift really hates pointers. If you code makes heavy use of pointer, it's easer to write them in C/ObjC.

How to pass const void *bytes to an Objective-C block from Swift

I'm writing some Swift (1.2) code that calls an Objective-C library (transcribing some earlier Objective-C code). It's not clear how it's supposed to work. I've tried a bunch of variations, and nothing satisfies the compiler. Here's the Objective-C code I started with:
// Objective-C
NSData *fileData = //
const void *bytes = fileData.bytes;
unsigned int bufferSize = 1024; //Arbitrary
[object writeDataWithBlock:
^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) {
for (NSUInteger offset = 0; offset <= fileData.length; offset += bufferSize) {
unsigned int size = (unsigned int)MIN(fileData.length - i, bufferSize);
writeData(&bytes[i], size);
}
return YES;
}];
So far, I've gotten as far as this Swift code:
// Swift
let fileData = //
let ptr = UnsafePointer<UInt8>(fileData.bytes)
let bufferSize = 1024 // Arbitrary
object.writeDataWithBlock() { (writeData, actionError) -> Bool in
for var offset = 0; offset <= fileData.length; offset += bufferSize {
let size = CUnsignedInt(min(fileData.length - offset, bufferSize))
writeData(UnsafePointer<Void>(ptr[offset]), size)
}
return true
}
That's giving me this error:
Cannot find an initializer for type 'UnsafePointer' that accepts an arguments list of type '(UInt8)'
When I remove the UnsafePointer<Void> conversion to do a more direct translation, yielding this line:
writeData(&ptr[offset], size)
I get this error, pointing at the & character:
Type of expression is ambiguous without more context
Without the &, it yields a UInt8, giving me this error:
Cannot invoke 'writeData' with an argument list of type '(Uint8, UInt32)'
What do I need to do to read the bytes out of the NSData sequentially and pass them onto another method?
Pointer arithmetic should (and does) get you the results you're looking for.
let fileData = //
let bufferSize = 1024 // Arbitrary
object.writeDataWithBlock() { (writeData, actionError) -> Bool in
for var offset = 0; offset <= fileData.length; offset += bufferSize {
let size = CUnsignedInt(min(fileData.length - offset, bufferSize))
writeData(fileData.bytes + offset, size)
}
return true
}

How to convert NSUUID to NSData

I need to get NSData from vendorIdentifier without converting to NSString, clear bytes.
How to convert NSUUID using getUUIDBytes: to NSData?
NSUUID *vendorIdentifier = [[UIDevice currentDevice] identifierForVendor];
uuid_t uuid;
[vendorIdentifier getUUIDBytes:uuid];
NSData *vendorData = [NSData dataWithBytes:uuid length:16];
Use this to get the vendorIdentifier as Data (Swift 4):
var uuidBytes = UIDevice.current.identifierForVendor!.uuid
let uuidData = NSData(bytes: &uuidBytes, length: 16) as Data
Keep in mind to guard against UIDevice.current.identifierForVendor being nil as the documentation says.
Original answer (Swift 2 Version):
var uuidBytes: [UInt8] = [UInt8](count: 16, repeatedValue: 0)
UIDevice.currentDevice().identifierForVendor!.getUUIDBytes(&uuidBytes)
let uuidData = NSData(bytes: &uuidBytes, length: 16)
Swift 3 Version:
let vendorId = UIDevice.current.identifierForVendor!
let uuidBytes = Mirror(reflecting: vendorId.uuid).children.map { $0.1 as! UInt8 }
let data = Data(bytes: uuidBytes)

Generate SHA256 hash in Objective-C

So I need to generate a Sha256 password in Objective-C, and can't figure out for the life of me how to do it! Is there something easy I'm just missing?
I've tried implementing the following method (which was written for iPhone, but I figured maybe it'd work cross-platform, as some Objective-C code does)
-(NSString*)sha256HashFor:(NSString*)input
{
const char* str = [input UTF8String];
unsigned char result[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(str, strlen(str), result);
NSMutableString *ret = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH*2];
for(int i = 0; i<CC_SHA256_DIGEST_LENGTH; i++)
{
[ret appendFormat:#"%02x",result[i]];
}
return ret;
}
But that just spat out errors about CC_SHA256_DIGEST_LENGTH being an undeclared identifier.
You need to include the appropriate header file:
#include <CommonCrypto/CommonDigest.h>
According to the Cryptographic Services documentation this should be available on both iOS and OS X.
In OS X v10.5 and later and iOS 5.0 and later, Common Crypto provides low-level C support for encryption and decryption. Common Crypto is not as straightforward as Security Transforms, but provides a wider range of features, including additional hashing schemes, cipher modes, and so on.
#import <CommonCrypto/CommonDigest.h>
Objective-C: SHA256 is only two lines:
+ (NSData *)doSha256:(NSData *)dataIn {
NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(dataIn.bytes, dataIn.length, macOut.mutableBytes);
return macOut;
}
Swift 3
func sha256Hex(string: String) -> String? {
guard let messageData = string.data(using:String.Encoding.utf8) else { return nil }
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData.map { String(format: "%02hhx", $0) }.joined()
}
// Test
let sha256HexString = sha256Hex(string:"Hello")
print("sha256HexString: \(sha256HexString!)")
sha256HexString: "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"
Objective-C method whose output matches output from random site online
-(NSString*)sha256HashForText:(NSString*)text {
const char* utf8chars = [text UTF8String];
unsigned char result[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(utf8chars, (CC_LONG)strlen(utf8chars), result);
NSMutableString *ret = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH*2];
for(int i = 0; i<CC_SHA256_DIGEST_LENGTH; i++) {
[ret appendFormat:#"%02x",result[i]];
}
return ret;
}
Taken from this Gist.
a modified version of #zaph answer in Objective-C with NSString as input and output:
-(NSString*)sha256HashFor:(NSString*)input
{
NSData* data = [input dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *sha256Data = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256([data bytes], (CC_LONG)[data length], [sha256Data mutableBytes]);
return [sha256Data base64EncodedStringWithOptions:0];
}
Check out the NSHash cocoa pod. It has a bunch of different hashing methods including SHA256.
https://github.com/jerolimov/NSHash