Convert dispatch_data_t to NSString after a read operation - ios

In my small iOS app I have a CSV file that represents an histogram. The content of file is:
label0,value0
...
labelN,valueN
In my ViewController I read the file async with Grand Central Dispatch this way:
//create the channel with which read the CSV file
dispatch_io_t ch = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [[[NSBundle mainBundle] pathForResource:#"histogram1" ofType:#"csv"] UTF8String], O_RDONLY, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int error) {
if(!error){
NSLog(#"Channel created!");
}
});
//read the whole file
dispatch_io_read(ch, 0, SIZE_MAX, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(bool done, dispatch_data_t dataRead, int error) {
if(!error && done){
NSString *ma = ; //???
NSLog(#"%#", ma);
}
});
I'd like to convert the dataRead (of type dispatch_data_t) to NSString to extract the values that represent the bars of the histogram.

There are a number of ways to do this, but the function you're looking for is dispatch_data_apply. Assuming the string you're reading is UTF8, this should work:
#interface NSString (FromDispatchData)
+ (instancetype)stringFromDispatchData: (dispatch_data_t)data;
#end
#implementation NSString (FromDispatchData)
+ (instancetype)stringFromDispatchData: (dispatch_data_t)data
{
if (!data)
return nil;
NSMutableString* str = [NSMutableString string];
dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
[str appendString: [[NSString alloc] initWithBytes:buffer length:size encoding: NSUTF8StringEncoding]];
return true;
});
return [[self class] stringWithString: str];
}
#end

Related

We are facing problem while creating compressed file of transferred socket data in hex string to converting to .tgz file in iOS App

We are facing problem while creating compressed file at iOS Device Document Directory, .tgz file is in Hex string transferring from pin-pad device to iPad iOS App at TCP socket layer. We used below HexToString function to convert that hex string and make file with .tgz. but at the end file is corrupted.
Can anyone please help us here, how to create compress file at iOS level with below hex string ? Please suggest us any code changes.
Note :- we had tried multiple NSStringEncoding technique, like ASCII, Unicode, Utf8, etc.
HEX String:-
1F8B08003058A8620203EDCA3B0A80301045D1594A5660265FB7E036065422A8453282CB57B4B2B112419CD3DCE2BD6966DD8F54925E4A975B62D22551EE741A2A5E199E80BBE8F1681DFDA5270BC6DB60D1398735A0092E0650082F580A53566A6F36F7BFFBFDA39A01841042FCD0062C8057FA00080000
we are using Xcode Version:13.1 and IOS Version 15.1 and above.
//Below function we used for creating .tgz file
//fileName here is abc.tgz which is compress file type
//content here is hex string mention aboved
+ (void)writeToLogFile:(NSString*)content fileName:(NSString*)fileNameString{
content = [NSString stringWithFormat:#"%#",content];
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:fileNameString];
NSData *fileOriginalString = [self HextoString:content];
NSData *fileData = [fileOriginalString dataUsingEncoding:NSASCIIStringEncoding];
***//In alternative we also tried direct hex string to NSData type by calling below commentented method but it still failing
//NSData *fileData = [self dataFromHexString:content];***
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSError *error = nil;
[fileData writeToFile:fileName options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
});
}
//Below function we used for Hex to String conversion
+(NSString*)HextoString:(NSString*)string{
#try{
NSMutableString * StrResult = [[NSMutableString alloc] init];
int i = 0;
while (i < [string length]){
NSString * hexChar = [string substringWithRange: NSMakeRange(i, 2)];
int value = 0;
sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
[StrResult appendFormat:#"%c", (char)value];
i+=2;
}
return StrResult;
}
#catch (NSException *exception){
[AELoggerManager info:[NSString stringWithFormat:#" %s EXCEPTION ::%#",__FUNCTION__,exception]];
}
}
+ (NSData *)dataFromHexString:(NSString *) string {
if([string length] % 2 == 1){
string = [#"0"stringByAppendingString:string];
}
const char *chars = [string UTF8String];
int i = 0, len = (int)[string length];
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:2];
}
return data;
}

iOS Obj-C convert byte array to plist to string

Argh, I'm going nuts.
I've got the following function which I am trying to log with a custom logger
CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer,
CFIndex bufferLength) {
NSData *data = [[NSData alloc] initWithBytes:buffer length: bufferLength];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString * str = (NSString*)[NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
custom_log(CF, "CFURLCreateWithString: %s", str);
}
When I use my custom logger I get rubbish output
CFURLCreateWithString: < °3€
But when using NSLog, everything works fine,
Feb 10 00:36:39: {
bundleID = "com.test.testapp";
pid = 2852;
}
Custom Logger
EXPORT void custom_log(unsigned int facility, const char *msg, ...) {
if (minLogLevel <= INFO) {
char *msgBuffer;
va_list args;
va_start(args, msg);
vasprintf(&msgBuffer, msg, args);
va_end(args);
dispatch_async(logQueue, ^{
write(facility, INFO, msgBuffer);
});
}
}
Please tell me where I'm going wrong, I have spent the past 3 hours trying to convert to different data types. No luck.
Also, is it possible to get the output from NSLog into a string and then I'll just pass it to my logger instead?
One of the problems that you might be experiencing here is that NSString is not the same as the c_str that your vasprintf method is likely expecting to substitute for %s.
To compound this issue, I'm pretty sure you can't directly convert NSPropertyListSerialization to NSString, though I didn't test it myself. You might be looking for such an alternative instead:
NSString * str = [NSString stringWithFormat:#"%#", [NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc]];
custom_log(CF, "CFURLCreateWithString: %s", [str UTF8String]);
Of course, since you're already compositing a string, why not just do it all in the same place?
NSString * str = [NSString stringWithFormat:#"CFURLCreateWithString: %#", [NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc]];
custom_log(CF, [str UTF8String]);
As a fun side project, you might consider doing something like this in your main.mm. Replace stderr with stdout if you want more than just error stuff:
#if COPY_NSLOG_TO_CUSTOM
typedef int (*MyStdWriter)(void *, const char *, int);
static MyStdWriter _oldStdWrite;
int __customStderrWrite(void *inFD, const char *buffer, int size) {
if (minLogLevel <= INFO) {
// write to your custom stream here.
}
return _oldStdWrite(inFD, buffer, size);
}
void __copyNSLogToCustom(void) {
_oldStdWrite = stderr->_write;
stderr->_write = __customStderrWrite;
}
#endif
int main(int argc, char *argv[]) {
#if COPY_NSLOG_TO_CUSTOM
__copyNSLogToCustom();
#endif
// ...
}

Converting an Sha256 hashed value into NSString

I have a requirement to integrate with a web service that serves as a login. The hash needs to be generated on the client. I am able to produce the correct hash as NSMutableData, but then I need to convert it to a string, without the spaces or brackets produced when the NSMutableData object is rendered as a string in the output console. I have read several posts, all seeming to say the same thing:
NSString *newstring = [[NSString alloc] initWithDSata:dataToConvert encoding:NSUTF8StringEncoding];
Unfortunately, this doesnt work for me. Using NSUTF8StringEncoding returns null. NSASCIIStringEncoding is even worse.
Here is my code:
NSString *password = [NSString stringWithFormat:#"%#%#", kPrefix, [self.txtPassword text]];
NSLog(#"PLAIN: %#", password);
NSData *data = [password dataUsingEncoding:NSASCIIStringEncoding];
NSMutableData *sha256Out = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(data.bytes, data.length, sha256Out.mutableBytes);
NSString *preppedPassword = [[NSString alloc] initWithData:sha256Out encoding:NSASCIIStringEncoding];
NSLog(#"HASH: %#\n", preppedPassword);
How can I convert the NSMutableData to string?
My problem is that I need to from this
<7e8df5b3 17c99263 e4fe6220 bb75b798 4a41de45 44464ba8 06266397 f165742e>
to this
7e8df5b317c99263e4fe6220bb75b7984a41de4544464ba806266397f165742e
See How to convert an NSData into an NSString Hex string?
I use a slightly modified version myself:
#implementation NSData (Hex)
- (NSString *)hexRepresentationWithSpaces:(BOOL)spaces uppercase:(BOOL)uppercase {
const unsigned char *bytes = (const unsigned char *)[self bytes];
NSUInteger nbBytes = [self length];
// If spaces is true, insert a space every this many input bytes (twice this many output characters).
static const NSUInteger spaceEveryThisManyBytes = 4UL;
// If spaces is true, insert a line-break instead of a space every this many spaces.
static const NSUInteger lineBreakEveryThisManySpaces = 4UL;
const NSUInteger lineBreakEveryThisManyBytes = spaceEveryThisManyBytes * lineBreakEveryThisManySpaces;
NSUInteger strLen = 2 * nbBytes + (spaces ? nbBytes / spaceEveryThisManyBytes : 0);
NSMutableString *hex = [[NSMutableString alloc] initWithCapacity:strLen];
for (NSUInteger i = 0; i < nbBytes; ) {
if (uppercase) {
[hex appendFormat:#"%02X", bytes[i]];
} else {
[hex appendFormat:#"%02x", bytes[i]];
}
// We need to increment here so that the every-n-bytes computations are right.
++i;
if (spaces) {
if (i % lineBreakEveryThisManyBytes == 0) {
[hex appendString:#"\n"];
} else if (i % spaceEveryThisManyBytes == 0) {
[hex appendString:#" "];
}
}
}
return hex;
}
#end

Massive allocations with with CFString while writing to NSOutputStream in iOS

I have problem with large number of allocations while writing to NSOutputStream. I am using few classes together that are supposed to stream data from AudioUnit to remote server. Code below:
AudioProcessor.m
Data *data = [Data sharedData]; //it's shared data I use to store information for the final socket
intFromBuffer = audioBufferList->mBuffers[0].mData;
NSUInteger length = audioBufferList->mBuffers[0].mDataByteSize;
NSMutableData *dataOut = [[NSMutableData alloc] initWithCapacity:0];
[dataOut appendBytes:(const void *)intFromBuffer length:length * sizeof(SInt16)];
[data setOutput:dataOut]; //it writes data to the shared data
Data.m
#implementation Data
NSMutableData *output;
NSMutableData *input;
#synthesize output;
#synthesize input;
+(id)sharedData {
static Data *sharedData = nil;
#synchronized(self) {
if (sharedData == nil) {
sharedData = [[self alloc] init];
}
return sharedData;
}
}
-(void) setOutput:(NSMutableData*)outputt{
output = outputt;
}
-(void) setInput:(NSMutableData*)inputt{
input = inputt;
}
#end
NetworkCommunication.m
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
//NSLog(#"changed!!!!");
if ([keyPath isEqualToString:#"output"]) {
Data *data = [Data sharedData];
[self writeOut:data.output];
}
}
- (void)writeOut:(NSString *)s {
NSString *dataTo = [NSString stringWithFormat:#"%#\n", s];
NSData *data = [[NSData alloc] initWithData:[dataTo dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
It generally creates great amount of data, which in 2,5 min was about 200 MB in virtual memory. I tried to simply pass stringWithFormat: directly to my writeOut: function however it gives nothing, not idea how to deal with it.
If someone asks why I use stringWithFormat, simply because I need to put \n for server to read the message.
Any help much appreciated.
EDIT 1:
I tied it different way. In my AudioProcessor.m (because that's the data is created - in the recorder callback) I set it this way:
dataOut = [[NSMutableData alloc] initWithCapacity:0];
[dataOut appendBytes:(const void *)intFromBuffer length:length * sizeof(SInt16)];
net = [NetworkCommunication init];
const char *s = [dataOut bytes];
[[net outputStream] write:s maxLength:strlen(s)];
Now it generates insane allocations of type Malloc 1,00KB, and Malloc 2,00 KB with NSThread callStackSymbol and NSThread callStackReturnAddresses. What's still wrong?
Did you try sending C strings?
const char *s = [dataTo UTF8String];
[outputStream write:s maxLength:strlen(s)];

addSkipBackupAttributeToItemAtURL -> NSString parameter?

In order to follow the Data Storage Guidelines I must use the below method to add a flag to say to not back it up to iCloud. However, the parameter here is for a NSURL. I need to pass it a NSString like from a line like so
return [[self offlineQueuePath] stringByAppendingPathComponent:#"SHKOfflineQueue.plist"];
Here is the method that takes in a URL.
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
Anyway, how would I modify the method above to achieve the same while taking in a NSString as a parameter?
Thanks!
You don't need to modify the method. Convert your string to URL.
NSURL *url = [NSURL URLWithString:#"your string"];
Use this method
NSURL *pathURL113= [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#",Your string]];
this is perfect code.

Resources