0

When attempting to save a PFObject, I was receiving the error "Error: object not found for update (Code: 101, Version: 1.2.15)" (logged automatically), and the localized description was logging: The operation couldn’t be completed. (Parse error 101.)

I eventually isolated the problem: the NSDictionary I was assigning to one of the fields on the PFObject had quotation marks around key names; turns out these are added automatically by the NSDictionary class when a key name contains nonalphanumeric characters.

Here's an example of a (logged) PFObject whose save was giving an error:

"<PlaybackPositionTracker:MKXTjOXg07:(null)> {\n ACL = \"<PFACL: 0xc1e80f0>\";\n aclRead = (\n \"\"\n );\n aclWrite = (\n \"\"\n );\n deletedAt = \"<null>\";\n nowPlaying = \"<null>\";\n playbackPositionDictionary = {\n \"http://podcastdownload.npr.org/anon.npr-podcasts/podcast/35/290491304/npr_290491304.mp3\" = 0;\n };\n}"

I suppose Parse interprets quotation marks to indicate a link to another Parse object or something (or else I'm at a loss to explain why this gives the "object not found" error).

How can I use a url string as a key in a dictionary that is assigned to a field on a PFObject? I could remove all nonalphanumeric characters from the string, but that would be a messy solution. What other options might there be? Thank you.

GenieWanted
  • 4,473
  • 4
  • 24
  • 35
mkc842
  • 2,361
  • 2
  • 26
  • 38

1 Answers1

0

Is there a reason you can't store and retrieve them separately?

NSString * url = @"http://podcastdownload.npr.org/anon.npr-podcasts/podcast/35/290491304/npr_290491304.mp3";
CGFloat currentPlaybackPosition = 0.0; // position in seconds
NSMutableDictionary * playbackPositionDictionary = [NSMutableDictionary new];
playbackPositionDictionary[@"URL"] = url;
playbackPositionDictionary[@"Seconds"] = [NSNumber numberWithFloat:currentPlaybackPosition];

PFObject * ob = [PFObject objectWithClassName:@"TrialClass"];
ob[@"playbackPositionDictionary"] = playbackPositionDictionary;
[ob saveInBackground];

// Retrieve

NSDictionary * retrievedPlaybackPositionDictionary = ob[@"playbackPositionDictionary"];
NSString * urlForPlaybackPosition = retrievedPlaybackPositionDictionary[@"URL"];
CGFloat playbackPositionInSeconds = [retrievedPlaybackPositionDictionary[@"Seconds"]floatValue];

NSLog(@"Start playing: %@ starting at: %@", urlForPlaybackPosition, playbackPositionInSeconds);

The only solutions I can think of involve converting the URL to a more accessible key. Would this work as an alternative solution?

I'm not sure if this is improved, but if you want to store these dictionaries on Parse, perhaps converting them to data strings is another work around:

Declare Methods:

- (NSString *) hexStringFromDictionary:(NSDictionary *)dict {
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:0
                                                     error:&error];
    return [jsonData description];
}


- (NSDictionary *) dictionaryFromHexString:(NSString *)string {

    string = [string lowercaseString];
    NSMutableData *data= [NSMutableData new];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i = 0;
    int length = string.length;
    while (i < length-1) {
        char c = [string characterAtIndex:i++];
        if (c < '0' || (c > '9' && c < 'a') || c > 'f')
            continue;
        byte_chars[0] = c;
        byte_chars[1] = [string characterAtIndex:i++];
        whole_byte = strtol(byte_chars, NULL, 16);
        [data appendBytes:&whole_byte length:1];

    }

    return [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
}

Conversion From HexString Modified From HERE!

Create Your Dictionary:

NSString * url = @"http://podcastdownload.npr.org/anon.npr-podcasts/podcast/35/290491304/npr_290491304.mp3";
CGFloat currentPlaybackPosition = 0.0; // position in seconds
NSMutableDictionary * playbackPositionDictionary = [NSMutableDictionary new];
playbackPositionDictionary[url] = [NSNumber numberWithFloat:currentPlaybackPosition];

Save To PFObject:

PFObject * ob = [PFObject objectWithClassName:@"ParseClass"];
NSString * dictString = [self hexStringFromDictionary:playbackPositionDictionary];
ob[@"playbackPositionDictionaryString"] = dictString;

Retrieve From PFObject:

NSString * hexString = ob[@"playbackPositionDictionaryString"];
NSDictionary * playbackPositionDictionary = [self dictionaryFromHexString:hexString];
Community
  • 1
  • 1
Logan
  • 52,262
  • 20
  • 99
  • 128
  • The chief purpose of objects in the PlaybackPositionTracker class is to store dictionaries that correlate audio-file urls (the keys) to numbers (the number of seconds of playback that have occurred for the audio file at the corresponding url). This enables the app to resume playback where the user last left off. Seems like a reasonable use of a dictionary to me. – mkc842 Mar 18 '14 at 17:49
  • @mkc842 - That sounds reasonable to me, I thought maybe you were storing your URL's there for retrieval, I'll reevaluate my answer. – Logan Mar 18 '14 at 17:52
  • @mkc842 - I don't know a good way to do that without utilizing some sort of conversion. Could you organize similar to my suggestion? See update. – Logan Mar 18 '14 at 18:08
  • Thanks for the input. Your proposal would handle a single URL/seconds pair just fine. But in my app, an indefinite number of audio-file urls will need to have their playback positions tracked. So if I were to adapt your proposal to that situation, what comes to mind is an array of single-entry dictionaries like the one your code creates. That is feasible, but of course the more deeply the data is buried in objs/arrays/dicts, the more difficult it becomes to manipulate and filter it. But there may be no good solution to my problem. Thanks again, and let me know if you have any further thoughts. – mkc842 Mar 18 '14 at 19:02
  • I have one more try, I'm not sure about system performance, but it is a potential work around. – Logan Mar 18 '14 at 19:55
  • Interesting to know this is an option; thanks for your input. I'll eventually accept if no one else makes a better proposal. – mkc842 Mar 19 '14 at 13:57