I can't find any official way to get a UUID string back out of a CBUUID. These UUIDs can be 2 or 16 bytes long.
The goal is to store CBUUIDs in a file somewhere as a string, and then resurrect with [CBUUID UUIDWithString:] etc. Here is what I have so far.
// returns a simple 4 byte string for 16bit uuids, 128 bit uuids are in standard 8-4-4-4-12 format
// the resulting string can be passed into [CBUUID UUIDWithString:]
+(NSString*)CBUUIDToString:(CBUUID*)cbuuid;
{
NSData* data = cbuuid.data;
if ([data length] == 2)
{
const unsigned char *tokenBytes = [data bytes];
return [NSString stringWithFormat:#"%02x%02x", tokenBytes[0], tokenBytes[1]];
}
else if ([data length] == 16)
{
NSUUID* nsuuid = [[NSUUID alloc] initWithUUIDBytes:[data bytes]];
return [nsuuid UUIDString];
}
return [cbuuid description]; // an error?
}
I rigged up the following category to do this for CBUUID:
#interface CBUUID (StringExtraction)
- (NSString *)representativeString;
#end
#implementation CBUUID (StringExtraction)
- (NSString *)representativeString;
{
NSData *data = [self data];
NSUInteger bytesToConvert = [data length];
const unsigned char *uuidBytes = [data bytes];
NSMutableString *outputString = [NSMutableString stringWithCapacity:16];
for (NSUInteger currentByteIndex = 0; currentByteIndex < bytesToConvert; currentByteIndex++)
{
switch (currentByteIndex)
{
case 3:
case 5:
case 7:
case 9:[outputString appendFormat:#"%02x-", uuidBytes[currentByteIndex]]; break;
default:[outputString appendFormat:#"%02x", uuidBytes[currentByteIndex]];
}
}
return outputString;
}
#end
For this input:
NSLog(#"UUID string: %#", [[CBUUID UUIDWithString:#"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"] representativeString]);
NSLog(#"UUID string2: %#", [[CBUUID UUIDWithString:#"1800"] representativeString]);
it produces the following output:
UUID string: 0bd51666-e7cb-469b-8e4d-2742f1ba77cc
UUID string2: 1800
and preserves the appropriate hyphenation for the 16 byte UUIDs, while supporting the simple 2-byte UUIDs.
To all those saying that CBUUID is toll-free bridged with CFUUIDRef, it's not.
CBUUID * foo = [CBUUID UUIDWithString:CBUUIDCharacteristicExtendedPropertiesString];
CFStringRef fooBar = CFUUIDCreateString(NULL, (__bridge CFUUIDRef)foo);
if (![CBUUIDCharacteristicExtendedPropertiesString isEqualToString:(__bridge NSString *)fooBar])
NSLog(#"fubar!");
It's not crashing but you're getting garbage out. It's probably uniquely identifying garbage, but it can't be round-tripped.
PS: This didn't work as a comment because SO comments oddly don't allow code formatting.
iOS 7.1 (beta released yesterday, 11/18/13) introduced the following property on CBUUID:
#property(nonatomic, readonly) NSString *UUIDString
The UUID represented as a string. (read-only)
From CBUUID Class Reference.
It's also worth noting that for comparing a UUID string with a CBUUID, this works:
if ([cbuuidInQuestion isEqual:[CBUUID UUIDWithString:#"1234-5678-9012-1234"]]) {
// isEqual tests for "the same UUID"
// == tests for "the same CBUUID object"
}
I know it's been 7 month since it was asked and answered, but... CBUUID is “toll-free bridged” to CFUUID and the easiest way to convert is
//CBUUID* uuid = descr.UUID;
NSString* str = CFUUIDCreateString(nil, uuid);
Here is swift extension of Brad Larson's answer :
import CoreBluetooth
extension CBUUID {
func representativeString() -> String {
let data = self.data
let bytesToConvert = data.length
let uuidBytes = UnsafePointer<CUnsignedChar>(data.bytes)
var outputString = String()
for currentByteIndex in 0..<bytesToConvert {
switch currentByteIndex {
case 3,5,7,9:
outputString += String(format: "%02x-",uuidBytes[currentByteIndex])
default:
outputString += String(format: "%02x",uuidBytes[currentByteIndex])
}
}
return outputString
}
}
From iOS 7.1 UUIDString property is there but for specific iOS7, above extension is good option.
There is native method in objective C and Swift and it is quite straight forward method.
NSString *str = characteristic.UUID.UUIDstring;
Same thing with Swift language
Link to library->
https://developer.apple.com/documentation/corebluetooth/cbuuid/1518742-uuidstring?language=objc
Brad's answer does its work, but the solution could be simpler (though probably not more efficient) using NSUUID class:
// CBUUID+ToString.h
#import <CoreBluetooth/CoreBluetooth.h>
#interface CBUUID (ToString)
- (NSString *)toString;
#end
// CBUUID+ToString.m
#import "CBUUID+ToString.h"
#implementation CBUUID (ToString)
- (NSString *)toString {
if ([self respondsToSelector:#selector(UUIDString)]) {
return [self UUIDString]; // Available since iOS 7.1
} else {
return [[[NSUUID alloc] initWithUUIDBytes:[[self data] bytes]] UUIDString]; // iOS 6.0+
}
}
#end
The following Worked me without any error:
NSString *str = [[NSString alloc] initWithFormat:#"%#", CFUUIDCreateString(nil, peripheral.UUID) ];
Related
A little background here before I get started, basically we are looking to compare a UDP response with a string stored in Parse's database for our app. This issue is that I can't seem to get the strings to be considered equal by the isEqualToString function. Here's the code I have running now, I have tried a few work-arounds I've seen in other questions but it still doesn't work.
- (BOOL) onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
if(tag == TAG_SINGLE_GRILL)
{
NSString *grillId = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if(grillId.length > 11)
{
grillId = [grillId substringToIndex:11];
}
grillId = [NSString stringWithFormat:#"%#", grillId];
if([grillId hasPrefix:#"GMG"])
{
for(int i = 0; i < [parseGrills count]; i++)
{
NSString *parseGrillId = [[parseGrills objectAtIndex:i] grillId];
parseGrillId = [NSString stringWithFormat:#"%#", parseGrillId];
//If we match the id, add it to found grills
if([grillId isEqualToString:parseGrillId])
{
//do stuff
}
}
}
NSLog(#"Grill ID : %#", grillId);
}
return TRUE;
}
parseGrills is an NSMutableArray with a very basic Grill object, I use synthesize for the properties, otherwise the .m file is essentially empty.
#import <Foundation/Foundation.h>
#interface Grill : NSObject
#property (nonatomic) NSString* grillId;
#property (nonatomic) NSString* ipAddress;
#end
Here's a screen shot of the debugger after it returns false
Any help would be greatly appreciated. Thanks.
I guess that they are of different encoding.
I have run this experiment and see that if the encoding is different, it will return NO. So, try converting parseGrillId to utf8 with the code below.
NSString *s1 = [NSString stringWithCString:"HELLO123" encoding:NSUTF8StringEncoding];
NSString *s2 = [NSString stringWithCString:"HELLO123" encoding:NSUTF16StringEncoding];
NSString *s3 = [NSString stringWithUTF8String:s2.UTF8String];
if ([s1 isEqualToString:s2]) {
NSLog(#"s1 == s2");
}
if ([s1 isEqualToString:s3]) {
NSLog(#"s1 == s3");
}
Will print s1 == s3.
I need to know what language(s) is spoken in a given CLPlacemark.
Any idea?
you can get the country from the place mark and use its primary language -- it isn't a 100% solution but i n 90% of the cases it should work
NSString *countryIso = [self countryIsoFromPlacemark:placemark];
NSString *languageIso = [self languageIsoFroPrimaryLanguageOfCountry:countryIso];
put a plist of the iso code mappings in your app and you're done
I've ended up with the following, 100% automatic solution, and you don't need to collect any ISO code at all.
+ (NSString*)detectBestLanguageBCP47:(CLPlacemark *)placemark {
NSString * countryCode = placemark.ISOcountryCode;
for (AVSpeechSynthesisVoice *voice in AVSpeechSynthesisVoice.speechVoices) {
NSString *langCode = [voice.language substringWithRange:NSMakeRange(0, 2)];
NSString *cc = [voice.language substringWithRange:NSMakeRange(3, 2)];
int c = [cc compare:countryCode];
if (c==0) {
NSString *bcp47 =[ NSString stringWithFormat:#"%#-%#", langCode, cc];
return bcp47;
}
}
return nil; // Not found
}
I have a string Hello-World-Test, I want to split this string by the first dash only.
String 1:Hello
String 2:World-Test
What is the best way to do this? What I am doing right now is use componentsSeparatedByString, get the first object in the array and set it as String 1 then perform substring using the length of String 1 as the start index.
Thanks!
I added a category on NSString to split on the first occurrence of a given string. It may not be ideal to return the results in an array, but otherwise it seems fine. It just uses the NSString method rangeOfString:, which takes an NSString(B) and returns an NSRange showing where that string(B) is located.
#interface NSString (Split)
- (NSArray *)stringsBySplittingOnString:(NSString *)splitString;
#end
#implementation NSString (Split)
- (NSArray *)stringsBySplittingOnString:(NSString *)splitString
{
NSRange range = [self rangeOfString:splitString];
if (range.location == NSNotFound) {
return nil;
} else {
NSLog(#"%li",range.location);
NSLog(#"%li",range.length);
NSString *string1 = [self substringToIndex:range.location];
NSString *string2 = [self substringFromIndex:range.location+range.length];
NSLog(#"String1 = %#",string1);
NSLog(#"String2 = %#",string2);
return #[string1, string2];
}
}
#end
Use rangeOfString to find if split string exits and then use substringWithRange to create new string on bases of NSRange.
For Example :
NSString *strMain = #"Hello-World-Test";
NSRange match = [strMain rangeOfString:#"-"];
if(match.location != NSNotFound)
{
NSString *str1 = [strMain substringWithRange: NSMakeRange (0, match.location)];
NSLog(#"%#",str1);
NSString *str2 = [strMain substringWithRange: NSMakeRange (match.location+match.length,(strMain.length-match.location)-match.length)];
NSLog(#"%#",str2);
}
I can't find any official way to get a UUID string back out of a CBUUID. These UUIDs can be 2 or 16 bytes long.
The goal is to store CBUUIDs in a file somewhere as a string, and then resurrect with [CBUUID UUIDWithString:] etc. Here is what I have so far.
// returns a simple 4 byte string for 16bit uuids, 128 bit uuids are in standard 8-4-4-4-12 format
// the resulting string can be passed into [CBUUID UUIDWithString:]
+(NSString*)CBUUIDToString:(CBUUID*)cbuuid;
{
NSData* data = cbuuid.data;
if ([data length] == 2)
{
const unsigned char *tokenBytes = [data bytes];
return [NSString stringWithFormat:#"%02x%02x", tokenBytes[0], tokenBytes[1]];
}
else if ([data length] == 16)
{
NSUUID* nsuuid = [[NSUUID alloc] initWithUUIDBytes:[data bytes]];
return [nsuuid UUIDString];
}
return [cbuuid description]; // an error?
}
I rigged up the following category to do this for CBUUID:
#interface CBUUID (StringExtraction)
- (NSString *)representativeString;
#end
#implementation CBUUID (StringExtraction)
- (NSString *)representativeString;
{
NSData *data = [self data];
NSUInteger bytesToConvert = [data length];
const unsigned char *uuidBytes = [data bytes];
NSMutableString *outputString = [NSMutableString stringWithCapacity:16];
for (NSUInteger currentByteIndex = 0; currentByteIndex < bytesToConvert; currentByteIndex++)
{
switch (currentByteIndex)
{
case 3:
case 5:
case 7:
case 9:[outputString appendFormat:#"%02x-", uuidBytes[currentByteIndex]]; break;
default:[outputString appendFormat:#"%02x", uuidBytes[currentByteIndex]];
}
}
return outputString;
}
#end
For this input:
NSLog(#"UUID string: %#", [[CBUUID UUIDWithString:#"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"] representativeString]);
NSLog(#"UUID string2: %#", [[CBUUID UUIDWithString:#"1800"] representativeString]);
it produces the following output:
UUID string: 0bd51666-e7cb-469b-8e4d-2742f1ba77cc
UUID string2: 1800
and preserves the appropriate hyphenation for the 16 byte UUIDs, while supporting the simple 2-byte UUIDs.
To all those saying that CBUUID is toll-free bridged with CFUUIDRef, it's not.
CBUUID * foo = [CBUUID UUIDWithString:CBUUIDCharacteristicExtendedPropertiesString];
CFStringRef fooBar = CFUUIDCreateString(NULL, (__bridge CFUUIDRef)foo);
if (![CBUUIDCharacteristicExtendedPropertiesString isEqualToString:(__bridge NSString *)fooBar])
NSLog(#"fubar!");
It's not crashing but you're getting garbage out. It's probably uniquely identifying garbage, but it can't be round-tripped.
PS: This didn't work as a comment because SO comments oddly don't allow code formatting.
iOS 7.1 (beta released yesterday, 11/18/13) introduced the following property on CBUUID:
#property(nonatomic, readonly) NSString *UUIDString
The UUID represented as a string. (read-only)
From CBUUID Class Reference.
It's also worth noting that for comparing a UUID string with a CBUUID, this works:
if ([cbuuidInQuestion isEqual:[CBUUID UUIDWithString:#"1234-5678-9012-1234"]]) {
// isEqual tests for "the same UUID"
// == tests for "the same CBUUID object"
}
I know it's been 7 month since it was asked and answered, but... CBUUID is “toll-free bridged” to CFUUID and the easiest way to convert is
//CBUUID* uuid = descr.UUID;
NSString* str = CFUUIDCreateString(nil, uuid);
Here is swift extension of Brad Larson's answer :
import CoreBluetooth
extension CBUUID {
func representativeString() -> String {
let data = self.data
let bytesToConvert = data.length
let uuidBytes = UnsafePointer<CUnsignedChar>(data.bytes)
var outputString = String()
for currentByteIndex in 0..<bytesToConvert {
switch currentByteIndex {
case 3,5,7,9:
outputString += String(format: "%02x-",uuidBytes[currentByteIndex])
default:
outputString += String(format: "%02x",uuidBytes[currentByteIndex])
}
}
return outputString
}
}
From iOS 7.1 UUIDString property is there but for specific iOS7, above extension is good option.
There is native method in objective C and Swift and it is quite straight forward method.
NSString *str = characteristic.UUID.UUIDstring;
Same thing with Swift language
Link to library->
https://developer.apple.com/documentation/corebluetooth/cbuuid/1518742-uuidstring?language=objc
Brad's answer does its work, but the solution could be simpler (though probably not more efficient) using NSUUID class:
// CBUUID+ToString.h
#import <CoreBluetooth/CoreBluetooth.h>
#interface CBUUID (ToString)
- (NSString *)toString;
#end
// CBUUID+ToString.m
#import "CBUUID+ToString.h"
#implementation CBUUID (ToString)
- (NSString *)toString {
if ([self respondsToSelector:#selector(UUIDString)]) {
return [self UUIDString]; // Available since iOS 7.1
} else {
return [[[NSUUID alloc] initWithUUIDBytes:[[self data] bytes]] UUIDString]; // iOS 6.0+
}
}
#end
The following Worked me without any error:
NSString *str = [[NSString alloc] initWithFormat:#"%#", CFUUIDCreateString(nil, peripheral.UUID) ];
Still learning iOS development with ObjectiveC and iOS, and trying to realy understand memory management! Appreciate any advise on the snippet below, eg:
1) Analyser says there are potential memory leaks, but can't solve them?
2) Should I keep alloc and init the NSStrings in the for loop and when appended to?
Thanks
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
debugPrint ("TRACE", [[#"Lookup Char Name for = " stringByAppendingString: inCharID] UTF8String]);
NSString *tempName = [[NSString alloc] initWithFormat: #""];
if (![inCharID isEqualToString: #""])
{
// Potentially lookup multiple values
//
NSString *newName = [[NSString alloc] initWithFormat: #""];
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
NSLog( #"Lookup %i : %#", [idList count], nextID);
newName = [[NSString alloc] initWithFormat: #"C%#", nextID];
// Append strings
if ([tempName isEqualToString: #""])
tempName = [[NSString alloc] initWithFormat: #"%#", newName];
else
tempName = [[NSString alloc] initWithFormat: #"%#+%#", tempName, newName];
}
[newName release];
}
return [tempName autorelease];
}
You don't need any of the calls to alloc, release, or autorelease. Instead, use [NSString stringWithFormat:] to create instances of NSString that you don't own, and therefore don't need to manage. Also, consider using NSMutableString to simplify your code a bit, for example along the lines of the following (untested) version:
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
NSMutableString *tempName = nil;
if (![inCharID isEqualToString: #""])
{
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
[tempName appendString:#"+"]; // Does nothing if tempName is nil.
if (tempName == nil)
tempName = [NSMutableString string];
[tempName appendFormat:#"C%#", nextID];
}
}
return tempName;
}
You have 2 alloc initWithFormat for tempName. One before the loop and one within the loop.
Use ARC (Automatic Reference Counting) for new projects. For older projects it may be easy to convert them, if not ARC can be disabled on a file-by-file basis where necessary.
Using a mutable string, autoreleased convience methods and a little rerfactoring:
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
NSMutableString *tempName = [NSMutableArray array];
if (inCharID.length)
{
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
if (tempName.length == 0)
[tempName appendFormat: #"%#C", nextID];
else
[tempName appendFormat: #"+%#C", nextID];
}
}
return tempName;
}