Swift NSData Initialiser - objective-c

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))
}

Related

Flutter how to convert NSData* to Byte* in objc

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.

Convert AVPlayer NSData to NSString (Or player format)

I need to know how to convert saved NSData from an AVPlayer back into a playable format. But I cannot figure out how to convert this NSData into a dataString, which would then allow me to create and NSUrl. Let's say I have the following code:
NSURL *videoUrl=(NSURL*)[info objectForKey:UIImagePickerControllerMediaURL];
self.data=[NSData dataWithContentsOfURL:videoUrl];
Now later on when I get this data back, I call:
NSString *dataString = [NSString stringWithUTF8String:[self.data bytes]];
But the dataString is always nil. Why? Am I decoding it in the wrong format or something?
A URL is a reference to data. The data is a (possibly complicated) encoding of something like a movie.
I am not sure exactly what you are asking, but I think you want to get the data (as an NSData object) and then save it somewhere. If this is correct, then what you need to do is
[self.data writeToFile:myFilePath atomically:YES]
where myFilePath is a path to somewhere where you can store files.
You could implement AVAssetResourceLoaderDelegate to provide data for AVPlayer.
func resourceLoader(resourceLoader: AVAssetResourceLoader!, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest!) -> Bool {
if let data = videoData {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if let infoRequest = loadingRequest.contentInformationRequest {
infoRequest.contentType = "public.mpeg-4" // UTI
infoRequest.contentLength = Int64(data.length)
infoRequest.byteRangeAccessSupported = true
}
if let request = loadingRequest.dataRequest {
let part = data.subdataWithRange(NSRange(location: Int(request.requestedOffset), length: Int(request.requestedLength)))
request.respondWithData(part)
}
loadingRequest.finishLoading()
}
return true
}
return false
}
To create an AVPlayer:
let asset = AVURLAsset(URL: NSURL(scheme: "yourcustomscheme", host: nil, path: "/pathtovideo"), options: nil)
asset.resourceLoader.setDelegate(self, queue: dispatch_get_main_queue())
let item = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: item)

obj-c to Swift convert code

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)
}

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)

Converting NSArray Contents to a varargs (With ARC) For Use With NSString initWithFormat

We have some code today that takes an NSArray and passes it as a argument list to -[NSString initWithFormat:arguments] and we're trying to get this to work with ARC. Here's the code were using
NSString* format = #"Item %s and Item %s"; // Retrieved elsewhere
NSArray* args = [NSArray arrayWithObjects:#"1", #"2", nil]; // Retrieved elsewhere
// http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
char* argsList = (char*) malloc(sizeof(NSString*) * args.count);
[args getObjects:(id*) argsList];
NSString* message = [[[NSString alloc] initWithFormat:format arguments:argsList] autorelease];
free(argsList);
Any recommendations on how to make this ARC compliant? Or we're even open to a better way of doing it.
This only works for arrays with a single element
The answer by chrisco was working well, until I went to compile with 64-bit architecture. This caused an error:
EXC_BAD_ADDRESS type EXC_I386_GPFLT
The solution was to use a slightly different approach for passing the argument list to the method:
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
{
__unsafe_unretained id * argList = (__unsafe_unretained id *) calloc(1UL, sizeof(id) * arguments.count);
for (NSInteger i = 0; i < arguments.count; i++) {
argList[i] = arguments[i];
}
NSString* result = [[NSString alloc] initWithFormat:format, *argList] ;// arguments:(void *) argList];
free (argList);
return result;
}
Cannot find a way to do this obj-c but a swift helper class finally got this working (my whole project is obj-c except this class)
#objc class StringFormat: NSObject {
class func format(key: String, args: [AnyObject]) -> String {
let locArgs: [CVarArgType] = args.map({ (arg: AnyObject) -> CVarArgType in
if let iArg = (arg is NSNumber ? arg.intValue : nil) {
return iArg
}
return arg as! CVarArgType
});
return String(format: key, arguments: locArgs)
}
}
There is some magic going on, to do with how [CVarArgType] doesn't behave like a normal array - but this works in the flexible cross architecture way you expect it to.
Expanding on #mcfedr's answer, this Swift 3 helper does the job:
import Foundation
#objc (FTStringFormat) public class StringFormat: NSObject {
#objc public class func format(key: String, args: [AnyObject]) -> String {
let locArgs: [CVarArg] = args.flatMap({ (arg: AnyObject) -> CVarArg? in
if let arg = arg as? NSNumber {
return arg.intValue
}
if let arg = arg as? CustomStringConvertible {
return arg.description
}
return nil
});
return String(format: key, arguments: locArgs)
}
}
Calling from Objective-C:
[FTStringFormat formatWithKey:#"name: %# age: %d" args:#[#"John", #(42)]]
For the %# format specifier we're using Swift's CustomStringConvertible protocol in order to call description on all of the array members.
Supporting all number format specifiers like %d and %f is not really possible because the NSNumber object doesn't reveal if it's an integer or float. So we could only support one or the other. Here we use intValue, so %d is supported but %f and %g are not.
The only thing you need to do is remove the autorelease.
You're malloc'ing and free'ing yourself - ARC doesn't care about that.
I write solution use NSInvocation and signatures.
Answer create in this.
Also I write detailed description how it work but only on Russian ((
Maybe it help for someone.
I tried mcfedr's code. Somehow, my Xcode 11 treated CVarArgType as undeclared type, so I investigated into this for a while.
I didn't not understand the closure part of his/her code. And, I just simplified to hard casted each element to CVarArg using as! operator.
func format(key: String, args: [Any]) -> String {
return String(format: key, arguments: args.map { ($0 as! CVarArg) })
}
let doubleValue: Double = 1.25
let floatValue: Float = 2.75
let intValue: Int = 3
let numberValue: NSNumber = 4.5 as NSNumber
let hello: String = "Hello"
let world: NSString = "World" as NSString
print(format(key: "double: %f, float: %f, int: %d, number: %#, %#, %#", args: [doubleValue, floatValue, intValue, numberValue, hello, world]))
// double: 1.250000, float: 2.750000, int: 3, number: 4.5, Hello, World
It seems it's working fine under swift 5.1, but there may be some pitfalls.