My solution for Speakable

Hi everyone,

I’m just working on this challenge and here is my solution. It seems it works but I’m still not very sure about the NSError part. I just check the brand name to see whether it’s in the list and decide to return the error message or not. I think there should be better ways.

[code]- (NSString *)encodeAsSpeakableString
{
// Create the list of all brands
// Keep the NSDictionary object for reuse
static NSDictionary *brandList = nil;
if (!brandList) {
brandList = @{@“Camry”: @0, @“Nikon”: @1, @“Apple”: @2, @“Ford”: @3, @“Audi”: @4,@“Google”: @5,@“Nike”: @6,@“Amazon”: @7,@“Honda”: @8, @“Mazda”: @9,@“Buick”: @10,@“Fiat”: @11, @“Jeep”: @12, @“Lexus”: @13, @“Volvo”: @14, @“Fuji”: @15, @“Sony”: @16, @“Delta”: @17, @“Focus”: @18, @“Puma”: @19, @“Samsung”: @20, @“Tivo”: @21, @“Halo”: @22, @“Sting”: @23, @“Shrek”: @24, @“Avatar”: @25, @“Shell”: @26, @“Visa”: @27, @“Vogue”: @28, @“Twitter”: @29, @“Lego”: @30, @“Pepsi”: @31, };
}

// Get bytes from the NSData object to convert
unsigned char bytes[8];
[self getBytes:bytes];

// Create a mutable string to store converted speakable strings
NSMutableString *dataToString = [[NSMutableString alloc] init];

for (int i = 0; i < [self length]; i++) {
    unsigned char x = bytes[i] >> 5; // Digit part of the string
    unsigned char w = bytes[i] << 3;
    unsigned char y = w >> 3; // Brand part of the string
    NSArray *key = [brandList allKeysForObject:[NSNumber numberWithUnsignedChar:y]]; // Find the key corresponding to the brand number
    NSString *brand = key[0];
    NSString *digit = [NSString stringWithFormat:@"%d ", x + 2]; // To avoid digit 1 and 0
    
    [dataToString appendString:digit];
    [dataToString appendString:brand];
    // add space except for the last brand
    if (i != [self length] - 1) {
        [dataToString appendString:@" "];
    }
}
return [dataToString copy];

}

  • (NSData *)dataWithSpeakableString:(NSString *)string
    error:(NSError **)error
    {
    static NSDictionary *brandList = nil;
    if (!brandList) {
    brandList = @{@“Camry”: @0, @“Nikon”: @1, @“Apple”: @2, @“Ford”: @3, @“Audi”: @4,@“Google”: @5,@“Nike”: @6,@“Amazon”: @7,@“Honda”: @8, @“Mazda”: @9,@“Buick”: @10,@“Fiat”: @11, @“Jeep”: @12, @“Lexus”: @13, @“Volvo”: @14, @“Fuji”: @15, @“Sony”: @16, @“Delta”: @17, @“Focus”: @18, @“Puma”: @19, @“Samsung”: @20, @“Tivo”: @21, @“Halo”: @22, @“Sting”: @23, @“Shrek”: @24, @“Avatar”: @25, @“Shell”: @26, @“Visa”: @27, @“Vogue”: @28, @“Twitter”: @29, @“Lego”: @30, @“Pepsi”: @31, };
    } // Create the list incase it is not created yet

    // Create the character set of 10 digits
    NSCharacterSet *digitSet = [NSCharacterSet decimalDigitCharacterSet];

    NSRange searchRange;
    searchRange.location = 0;
    searchRange.length = [string length];

    unsigned char digits[8]; // Store the digits of the speakable string
    unsigned long digitLocations[8]; // Store the locations of the digits in the speakable string
    // Find the digits one by one and store them in "digits"
    for (int i = 0; i < [string length]; i++) {
    NSRange range = [string rangeOfCharacterFromSet:digitSet
    options:NSLiteralSearch
    range:searchRange];
    if (range.length == 0) {
    break; // Stop the loop if no more digit can be found
    } else {
    int digit = [[string substringWithRange:range] intValue] - 2; // convert the substring of digit to an integer
    digits[i] = (unsigned char)digit; // Store it in a byte of "digits"
    digitLocations[i] = range.location; // Store the location here for future use
    // Define a new range to search for next digit
    searchRange.location = range.location + 1;
    searchRange.length = [string length] - searchRange.location;
    }
    }

    // Now it’s time to convert the brands
    unsigned char brandParts[8]; // Store the number of brands here
    for (int i = 0; i < 8; i++) {
    // Calculate the range of a particular brand
    NSRange brandRange;
    brandRange.location = digitLocations[i] + 2;
    if (i < 7) {
    brandRange.length = digitLocations[i+1] - digitLocations[i] - 3;
    } else {
    brandRange.length = [string length] - brandRange.location;
    }
    NSString *brand = [string substringWithRange:brandRange]; // Get the brand
    NSNumber *brandNumber = [brandList objectForKey:brand];

      // Is the brand string in the brand list?
      if (brandNumber == nil) {
          //Did the user give the address to place error?
          if (error) {
              NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Unable to parse!"};
              *error = [NSError errorWithDomain:@"SpeakableBytes"
                                           code:1
                                       userInfo];
              return nil;
          }
          
      }
      
      brandParts[i] = [brandNumber unsignedCharValue];
    

    }

    // Combine the two parts above
    unsigned char bytesFromString[8];
    for (int i = 0; i < 8; i++) {
    bytesFromString[i] = (digits[i] << 5) | brandParts[i];
    }

    // Create an NSData to wrap the bytes
    NSData *Data = [NSData dataWithBytes:&bytesFromString
    length:8 * sizeof(unsigned char)];

    return Data;
    }
    [/code]