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.
Related
I am trying to use c++ api with objc native code in flutter.
https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-obj-c-tab
flutter documentation says Uint8List should be stored as FlutterStandardTypedData typedDataWithBytes: in objc do
send argument in flutter
var data = <String, Uint8List>{
"key": byte, //data type is Uint8List
"value": byteBuffer, //data type is Uint8List
};
Uint8List? byteRes;
byteRes = await platform.invokeMethod('SeedDecrypt', data);
get argument in objc (AppDelegate.m)
NSData* key = call.arguments[#"key"];
NSData* value = call.arguments[#"value"];
NSUInteger keyLength = [key length];
NSUInteger valueLength = [value length];
Byte* byteKey = (Byte*)malloc(keyLength);
Byte* byteValue = (Byte*)malloc(valueLength);
memcpy(byteKey, [key bytes], keyLength);
memcpy(byteValue, [value bytes], byteLength);
DWORD roundKey[32];
//Call C++ API
//prototype : void SeedKey(DWORD* roundKey, BYTE* byteKey);
SeedKey(roundKey, byteKey);
//protoType : void Decrypt(BYTE* byteValue, DWORD* roundKey);
Decrypt(byteValue, roundKey);
NSData* res = [NSData dataWithBytes: byteValue length: sizeof(byteValue)];
result(res);
Store the argument as NSData* and copy the memory to a Byte* variable. After executing the C API, it is converted to NSData type. The problem is that when I run it, the device shuts down. I wrote this source referring to the article below. Do you know what my mistake is?
How to convert NSData to byte array in iPhone?
thanks.
Solved
NSNumber* keyLength = call.arguments[#"keyLen"];
NSNumber* valueLength = call.arguments[#"valueLen"];
NSUInteger keyLen = [keyLength integerValue];
NSUInteger valueLen = [valueLength integerValue];
FlutterStandardTypedData* key = call.arguments[#"key"];
FlutterStandardTypedData* value = call.arguments[#"value"];
Byte* byteKey = (Byte*)malloc(keyLen);
Byte* byteValye = (Byte*)malloc(valueLen);
memcpy(byteKey, [key.data bytes], keyLen);
memcpy(byteValue, [value.data bytes], valueLen);
DWORD roundKey[32];
//Call C++ API
NSData* res = [NSData dataWithBytes:keyValue length:keyLen];
FlutterStandardTypedData* rest = [FlutterStandardTypedData typedDataWithBytes: res];
free(byteKey);
free(byteValue);
result(rest);
See https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-obj-c-tab. After matching the data type, match the OBJC data type with the C data type and return the result.
I'm not entirely sure what
NSString * fileName = [self cachedFileNameForKey:[urlString componentsSeparatedByString:#"?"][0]];
Means from this code. I am primely writing in Swift so this notation is a bit confusing for me. What does the double [ ] notation mean? 3d array?
+ (AVPlayerItem *)localDownloadedVideoFromUrl:(NSURL *)url {
NSString * urlString = url.absoluteString;
NSString * fileName = [self cachedFileNameForKey:[urlString componentsSeparatedByString:#"?"][0]];
TWRDownloadManager * manager = [TWRDownloadManager sharedManager];
if ([manager fileExistsWithName:fileName]) {
AVPlayerItem * item = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:[manager localPathForFile:fileName]]];
return item;
}
return [NSNull null];
}
[] in objective-c is not only used to access arrays, but also used to call methods. Surprising, isn't it?
In general,
[xxx someMethod];
is equivalent to:
xxx.someMethod()
in swift.
So here:
[urlString componentsSeparatedByString:#"?"][0]
means
urlString.components(separatedBy: "?")[0] // "[0]" can also be replaced by ".first", which is safer.
This is then passed to the cachedFileNameForKey as a parameter.
In Swift
var urlString: String = "Know someone who can answer? Share a link to this" // Example string or url.absoluteString
let fileName = urlString.components(separatedBy: "?")
print(fileName)
let string1 = fileName[0]
let string2 = fileName[1]
Hope will helpful to you
Ahh so mistake on my part is that I didn't see what the method -cacheFileNameForKey: was doing.
+ (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:#"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%#",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], [key.pathExtension isEqualToString:#""] ? #"" : [NSString stringWithFormat:#".%#", key.pathExtension]];
return filename;
}
I guess its used for a function to generate a file name for a temporary movie file.
I im tring to implement this method from a Obj-C library.
func bleDidReceiveData(data: UnsafeMutablePointer<UInt8>, length: Int32) {
var d = NSData(bytes: data as [UInt8], length: length)
}
However when i tried initializing NSData, it gave me this error
Cannot find an initialiser for type NSData that accepts an argument of type (bytes:[UInt8],length:int32)
In Obj-C it is being done this way
NSData *d = [NSData dataWithBytes:data length:length];
func bleDidReceiveData(data: UnsafeMutablePointer<UInt8>, length: Int32) {
var d = NSData(bytes: UnsafeMutablePointer<Void>(data), length: Int(length))
}
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)
}
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)