How to check NSData is String or Image - ios

How to check NSData is string or image data , i have receive server can get 2 type of data is string and UIImage data. How to check data is string or image data. Thank in advanced.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventOpenCompleted: {
NSLog(#"NSStreamEventOpenCompleted");
} break;
case NSStreamEventHasBytesAvailable: {
if(!self.data) {
self.data = [[NSMutableData alloc] init];
}
while (self.networkStream.hasBytesAvailable) {
uint8_t buf[1024];
NSInteger len = 0;
len = [self.networkStream read:buf maxLength:1024];
if(len>0) {
[self.data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
self.bytesRead += len;
}else{
}
}
// Event when read input stream done
if (!self.networkStream.hasBytesAvailable) {
[self _didReceiveDataString:self.data];
}
}
}

Agreed with rmaddy. But check image before string, because image data may return a valid string.
if ([UIImage imageWithData:data])
{
// get image
}else if ([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding])
{
// get string
}

I would question a setup that could send string or image with no metadata to tell you which.
But if you always receive the same type of image you can examine the first few bytes to see if it contains the expected "signature". And most image formats will contain some "illegal" Unicode characters in or immediately following the "signature", so you can detect for sure that it's not a string.
It's kind of clumsy to attempt to convert the data & detect failure in order to determine its type.

One option would be to try to create an NSString from the data (NSString initWithData:encoding:). If that fails, then try to create a UIImage (UIImage imageWithData:). If that fails, you have neither.
Another option would be to check the data length. This works if you know that the longest string you will get will never be more data than the smallest image you might get.

Try this:
UIImage *image= [UIImage imageWithData:imagedata];
if(image != nil){
// correct image
} else{
// incorrect image
}

Related

Cast of 'long' to 'id' is disallowed with ARC - iOS Dev

Converting my project to ARC but need help with these two bits of code to do with uploading and downloading files:
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
[delegate performSelector:progressSelector withObject:(id)(100*totalBytesWritten /totalBytesExpectedToWrite)];
and then the following code, specifically the last line:
- (void)connection:(NSURLConnection *)con // IN
didReceiveData:(NSData *)data // IN
{
NSLog(#"%s: self:0x%p\n", __func__, self);
NSInteger dataLength;
const uint8_t * dataBytes;
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
dataLength = data.length;
dataBytes = (const uint8_t * )data.bytes;
bytesWrittenSoFar = 0;
if(fileStream!=NULL)
{
do {
NSLog(#"%d",(int)bytesWrittenSoFar);
bytesWritten = [fileStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1)
{
[self downloadSucceeded:NO];
break;
}
else
{
bytesWrittenSoFar += bytesWritten;
}
}
while (bytesWrittenSoFar != dataLength);
}
dataSize+=data.length;
if(dataSize==downloadSize)
{
downloadDidSucceed=TRUE;
}
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
}
Any help is appreciated
Your problem is here
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
try this
[delegate performSelector:progressSelector withObject:[NSnumber numberWithLong:(100*dataSize/downloadSize)]];
In Objective-C there is distinction between scalar values like int/long/float/double and object types.
the id type is an anonymous object type. You don't know what it is, but you know that it is an object.
A long is not, and cannot be, an object. It is a simple scalar value. that's why the compiler isn't letting you cast your long to an ID type.
Sh_Khan's answer of wrapping your long value in an NSNumber solves the problem by creating an object that contains your value. You could also use an NSValue to get the same effect, but NSValue can represent things like structs that you can't represent with NSNumbers.

NSData to UIImage Conversion is not working?

My server is giving me my jpg image in the following NSData format:
/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA
AAABAAAAJgAAAAAAAqACAAQAAAABAAAGqKADAAQAAAABAAAI4AAAAAD/7QA4UGhvdG9zaG9wIDMu
MAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgI4AaoAwERAAIR
AQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAAB
fQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5/
I am saving it to a file, and while reading that file, the img object in my code below is giving me nil, although imgData object is holding the saved data.
- (void)selectedAttachedFiledownloadedSuccessfully
{
NSLog(#"\nFile has downloaded\n");
NSData *imgData = [NSData dataWithContentsOfFile:[self pathOfTheImage]];
NSString * imageExt = [self contentTypeForImageData:imgData];
UIImage *img = [UIImage imageWithData:imgData];
self.imgView.image = img;
}
Checking NSData for the image formats, it's not matching any and my code below is returning me nil
- (NSString *)contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return #"image/jpeg";
case 0x89:
return #"image/png";
case 0x47:
return #"image/gif";
case 0x49:
case 0x4D:
return #"image/tiff";
}
return nil;
}
I don't know what I am doing wrong over here. Can any one guide me through this plz?
You might have used the wrong encoding (such as NSUTF8StringEncoding) when storing the data.
NSUTF32StringEncoding should be used for image data.
The NSData which you are getting back from server might be corrupted. If the data is not in proper format it will not give you back the proper image
Use + (instancetype)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)mask error:(NSError * _Nullable *)errorPtr to read your image back from file and check the value of errorPtr
Refer this link for explanation for the above method https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/#//apple_ref/occ/clm/NSData/dataWithContentsOfFile:options:error:
The server was sending my the image in base64Binary string format. I changed it to NSData as:
NSData *data = [[NSData alloc] initWithBase64EncodedString:output[1] options:NSDataBase64DecodingIgnoreUnknownCharacters];
I save this data to my image file as:
[data writeToFile:filePath atomically:YES];
Read it to show it on image view as:
- (void)showImage
{
NSData *imgData = [NSData dataWithContentsOfFile:[self pathOfTheImage]];
UIImage *img = [UIImage imageWithData:imgData];
self.imgView.image = img;
}
It's working fine now.

Unable to write data into hardware peripheral

I am using CoreBlueTooth API to write data into a peripheral we have received from some hardware manufacturing company. As per the specs they have given us a bunch of characteristics UUIDs to write data into. Once we want to finish we need to write 0 in one of the characteristics. Now, the problem is that when I am trying to send String/Integer and converting them into NSData then its not working. I think I need to send byte stream in those writable characteristics. Can someone help me as how can I convert my NSString & NSNumber data into byte stream before sending them. Below is my conversion code I tried with:
- (void)writeCharactersticData:(NSDictionary *)iData toPeripheral:(CBPeripheral *)iPeripheral {
NSArray *charactersticsIDs = [NSArray arrayWithArray:iData.allKeys];
self.writeCharactersticsCount = charactersticsIDs.count;
for (CBUUID *uuid in charactersticsIDs) {
if (self.peripheralCharacterstics[uuid]) {
NSData *payload;
if ([iData[uuid] isKindOfClass:[NSNumber class]]) {
NSInteger data = ((NSNumber *)iData[uuid]).integerValue;
// int integerSize = sizeof(data);
//
// uint8_t bytes[integerSize];
//
//
// NSLog(#"Integer data = %d", data);
//
// int8_t tx = (int8_t)data;
// bytes[0] = tx;
// payload = [NSData dataWithBytes:bytes length:sizeof(data)];
payload = [NSData dataWithBytes:&data length:sizeof(data)];
} else if ([iData[uuid] isKindOfClass:[NSString class]]) {
int stringSize = sizeof(iData[uuid]);
uint8_t bytes[stringSize];
NSScanner *scanner = [NSScanner scannerWithString:iData[uuid]];
for (int i=0; i<stringSize; i++) {
unsigned int value;
[scanner scanHexInt:&value];
bytes[i] = (uint8_t)value;
}
payload = [NSData dataWithBytes:bytes length:stringSize];
// payload = [iData[uuid] dataUsingEncoding:NSUTF8StringEncoding];
}
[self.discoveredPeripheral writeValue:payload forCharacteristic:self.peripheralCharacterstics[uuid] type:CBCharacteristicWriteWithResponse];
}
}
}
Here is the fix, It works for me
//Integer to NSData
+(NSData *) IntToNSData:(NSInteger)data
{
Byte *byteData = (Byte*)malloc(4);
byteData[3] = data & 0xff;
byteData[2] = (data & 0xff00) >> 8;
byteData[1] = (data & 0xff0000) >> 16;
byteData[0] = (data & 0xff000000) >> 24;
NSData * result = [NSData dataWithBytes:byteData length:4];
NSLog(#"result=%#",result);
return (NSData*)result;
}
//NSData to Integer
+(NSInteger) NSDataToInt:(NSData *)data
{
unsigned char bytes[4];
[data getBytes:bytes length:4];
NSInteger n = (int)bytes[0] << 24;
n |= (int)bytes[1] << 16;
n |= (int)bytes[2] << 8;
n |= (int)bytes[3];
return n;
}
Thanks to http://coding-snippet.blogspot.in/2013/05/blog-post.html
This is not a Core Bluetooth based problem you have here.
For debugging, you could use
NSLog(#"%#", payload);
For your string to NSData conversion, your approach seems very complicated. I would suggest something simple like
NSData* payload = [iData[uuid] dataUsingEncoding:NSUTF8StringEncoding];
if (payload.length > 20)
{
// handle error. most LE peripherals don't support longer values.
}
Another error could be that you mix ASCII 0 '0' with a binary zero '\0' when writing your value.
Just use
https://github.com/LGBluetooth/LGBluetooth It will make your job 100x easier

How to receive data from APNS feedback server in objective c

Hello I'm trying to to something rather simple I think.
I have made an cocoa application that sends data using APNS, getting the tokens from my database, everything is set up and running perfect.
Now I want to check the APNS feedback server and remove any tokens received from my database.
I have found dozens of examples in php, javascript and so forth, but nothing in Objective C. I have read the programming guide from apple but can't figure out how to do it.
I am establishing a connection to APNS feedback but I don't know how to read the data.
I'm new to cocoa so please explain in detail :)
This is how I connect to the feedback server, it's the same way I connect when sending, just using another host.
- (void)connectToFeedBackServer
{
if(self.certificate == nil)
{
return;
}
NSString *feedBackHost = #"feedback.push.apple.com";
const char *cHost = [feedBackHost UTF8String];
NSLog(#"The size of cHost is: %lu", strlen(cHost));
NSLog(#"Host is: %s", cHost);
// Define result variable.
OSStatus result;
// Establish connection to server.
PeerSpec peer;
result = MakeServerConnection(cHost, 2196, &socket, &peer);
//NSLog(#"MakeServerConnection(): %d", result);
// Create new SSL context.
result = SSLNewContext(false, &context); //NSLog(#"SSLNewContext(): %d", result);
// Set callback functions for SSL context.
result = SSLSetIOFuncs(context, SocketRead, SocketWrite);
// NSLog(#"SSLSetIOFuncs(): %d", result);
// Set SSL context connection.
result = SSLSetConnection(context, socket);
// NSLog(#"SSLSetConnection(): %d", result);
// Set server domain name.
//result = SSLSetPeerDomainName(context, cHost, sizeof(cHost));
NSLog(#"SSLSetPeerDomainName(): %d", result);
result = SSLSetPeerDomainName(context, cHost, strlen(cHost));
result = SecIdentityCopyCertificate(_theIdentity, &(certificate));
// Set client certificate.
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)&_theIdentity, 1, NULL);
result = SSLSetCertificate(context, certificates);// NSLog(#"SSLSetCertificate(): %d", result);
CFRelease(certificates);
// Perform SSL handshake.
do
{
result = SSLHandshake(context); NSLog(#"SSLHandshake(): %d", result);
} while(result == errSSLWouldBlock);
}
And how I try to read the data and save the received the tokens in an array
- (NSMutableArray *)CheckFeedBackServer
{
char feedback[38];
size_t feedBackSize = sizeof(feedback);
size_t processed = 0;
NSMutableData *feedbackData = [[NSMutableData alloc]init];
NSString *token = [[NSString alloc]init];
NSMutableArray *tokenArray = [[NSMutableArray alloc]init];
[self connectToFeedBackServer];
while ([self getSSLContext])
{
int bytesLength = SSLRead([self getSSLContext], &feedback, feedBackSize, &processed);
[feedbackData appendBytes:feedback length:bytesLength];
while ([feedbackData length] > 38)
{
NSData *deviceToken = [NSData dataWithBytes:[feedbackData bytes] + 6 length:32];
token = [self deviceTokenToString:deviceToken];
[tokenArray addObject:token];
[feedbackData replaceBytesInRange: NSMakeRange(0, 38) withBytes: "" length: 0];
}
}
return tokenArray;
}
- (NSString *)deviceTokenToString: (NSData *)deviceToken;
{
NSString *tmpToken = [NSString stringWithFormat:#"%#", deviceToken];
NSUInteger loc_begin = [tmpToken rangeOfString: #"<"].location+1;
NSUInteger loc_end = [tmpToken rangeOfString: #">"].location-1;
return [tmpToken substringWithRange: NSMakeRange(loc_begin, loc_end)];
}
Just if anyone need to do something similar I solved my problem like this.
I use Apples ioSock class, and I have set the certificate in my code by calling the keychain
First I connect to the feedback server with this code
- (void)connectToFeedBackServer
{
if(self.certificate == nil)
{
return;
}
// Get the global variable feedbackHost and make it to a char
const char *cHost = [feedbackHost UTF8String];
NSLog(#"The size of cHost is: %lu", strlen(cHost));
NSLog(#"Host is: %s", cHost);
// Define result variable.
OSStatus result;
// Establish connection to server.
PeerSpec peer;
result = MakeServerConnection(cHost, 2196, &socket, &peer);
// Create new SSL context.
result = SSLNewContext(false, &context);
// Set callback functions for SSL context.
result = SSLSetIOFuncs(context, SocketRead, SocketWrite);
// Set SSL context connection.
result = SSLSetConnection(context, socket);
// Set server domain name.
result = SSLSetPeerDomainName(context, cHost, strlen(cHost));
result = SecIdentityCopyCertificate(_theIdentity, &(certificate));
// Set client certificate.
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)&_theIdentity, 1, NULL);
result = SSLSetCertificate(context, certificates);
CFRelease(certificates);
do
{
result = SSLHandshake(context); NSLog(#"SSLHandshake(): %d", result);
} while(result == errSSLWouldBlock);
}
And then I read the feedback data and add the tokens to an array, like this
- (NSMutableArray *)CheckFeedBackServer
{
OSStatus result;
NSMutableArray *feedbackTokens = [[NSMutableArray alloc]init];
// Retrieve message from SSL.
size_t processed = 0;
char buffer[38];
do
{
// Fetch the next item
result = SSLRead(context, buffer, 38, &processed);
if (result) break;
char *b = buffer;
// Recover Device ID
NSMutableString *deviceID = [NSMutableString string];
b += 6;
for (int i = 0; i < 32; i++)
{
[deviceID appendFormat:#"%02x", (unsigned char)b[i]];
}
[feedbackTokens addObject:deviceID];
} while (processed > 0);
return feedbackTokens;
}

CFStringRef setting a value to ""

I have a variable which is a CFStringRef and I perform a check to make sure its not 1 specific value. If it is then I want to set it to the NSString equivalent of #""
Here's the code
CFStringRef data = = CFDictionaryGetValue(dict, kABPersonAddressStateKey);
NSComparisonResult result = [(NSString *)data compare:element options:compareOptions];
if(NSOrderedAscending == result) {
// Do something here...
}
else if (NSOrderedSame == result) {
// Do another thing here if they match...
data = "";
}
else {
// Try something else...
}
So in the if else block, I want to set it to "" but Xcode warns me that it is an invalid pointer type.
CFStringRef is immutable object so you must create new instance with different value:
data = CFStringCreateWithCString (NULL, "", kCFStringEncodingUTF8);
And remember that you need to release that value.
But since CFStringRef and NSStrings types are toll-free bridged you can just use NSString in your code (that will probably makes it easier to understand and support it later):
NSString *data = (NSString*)CFDictionaryGetValue(dict, kABPersonAddressStateKey);
NSComparisonResult result = [(NSString *)data compare:element options:compareOptions];
if(NSOrderedAscending == result) {
// Do something here...
}
else if (NSOrderedSame == result) {
// Do another thing here if they match...
data = #"";
}
else {
// Try something else...
}

Resources