On most of the apps I develop, I usually have my own foundation classes, MKViewController, MKButton, MKLabel, MKThis and MKThat. One of the most often used/abused class is the MKObject class. This is the burial ground for methods that don’t fit elsewhere.

MKObject is a subclass of NSObject that adds convenient methods that I believe should be provided by Apple themselves.

I wrote some methods, categories on MKObject that would allow you to serialize any MKObject as JSON (using iOS 5 NSJSONSerialization) or XML (using KissXML). Of late, I have been playing with Parse, the backend as a service and wrote a category addition to this MKObject that would convert any MKObject derived subclass into a PFObject.

When I say, any MKObject derived subclass, don’t fret. You can easily convert an NSObject derived subclass in your project to an MKObject derived subclass without losing features/functionality since MKObject itself is a subclass of NSObject.

Features

So why do you need this MKObject? Let’s consider that you have a model called “User” and “Friend” (both a kind of “MKObject”)

Parse

A user has a list of friends. The traditional way to add a new friend to a user and saving it to Parse would be

PFObject *object = [PFObject objectWithClassName:@"User"];
NSMutableArray *friends = [object objectForKey:@"friends"];
[friends addObject:newFriend];
[object setObject:friends forKey:@"friends"];
[object saveInBackground];

Alternatively, if your User and Friend objects were MKObject subclasses, the above code would be as simple as,

[user addFriend:newFriend];
[[user pfObject] saveInBackground];

The category method, pfObject converts any MKObject subclass to a PFObject that can be saved to Parse easily. You don’t have to mess around with dictionaries and arrays manually. It just works.

XML

Hold on, MKObject is even more powerful than you think. It can convert any object to an equivalent XML tree. What more? MKFoundation is super lean that if you don’t use XML in your application, you don’t have to add the XML category extension classes. The foundation library is designed to avoid bloats in your code.

The classes MKObject+XMLExtentions.h/m provide XML support. It depends on the KissXML library to build the XML tree from your object.

JSON

We are not done yet. How good is a foundation object if it can do XML but not JSON? Does anyone even use XML/SOAP these days? Fret not. MKObject supports built in JSON serialization as well.

The method

[user prettyJSONString]

will convert the complete user object to a JSON string pretty printed for display.

Similarly, the method

[user jsonString]

will convert the complete user object to a JSON string ready to be sent to your server or serialized.

Deserialization

To complete the circle, MKObject also provides convenience methods to deserialize JSON/XML or Parse’s PFObjects back to MKObject objects.

When converting from JSON, call

[[MKObject alloc] initWithJSONString:string];

When converting from XML, call

[[MKObject alloc] initWithXMLString:string];

When converting from a PFObject, call

[MKObject objectFromPFObject:object];

Why should you use MKObject?

Ok, all fine, but why should I convert a JSON string to an object. Why can’t I pass the JSON strings to the view controllers?
The advantage of using a model class is clarity and ability to catch errors at compile time. For example, accessing
[userDictionary objectForKey:@”first_name”] instead of [userDictionary objectForKey:@”firstName”] will fail at run time. But

Accessing user.first_name instead of user.firstName will fail at compile time. Fail early, fail often

If you are using MKNetworkKit, using an MKObject subclass will help you write cleaner code without messing around with raw JSONs. The below code snippet explains this.

 [op onCompletion:^(MKNetworkOperation *completedOperation) {
 
    NSMutableArray *foodList = [NSMutableArray array];
 
	MKUser *user = [[MKUser alloc] initWithJSONString:[completedOperation responseJSON]];
    succeededBlock(user);
  } onError:^(NSError *error) {
 
    DLog(@"%@", error);
  }];

Requirements

MKObject is lean enough that you just have to add the necessary files into your project depending on what serialization support you need.

For JSON support, just copy MKObject.h/m files to your project. Change all your NSObject based models to MKObject based models.

For XML support, just copy MKObject.h/m and MKObject+XMLExtenstions.h/m files to your project. Change all your NSObject based models to MKObject based models. Add KissXML framework to your project. To add KissXML to your project, follow the getting started page here

XML deserialization is slightly tricky. You should add “known deserializers” by calling

[MKObject registerKnownClass:[Friend class]];

For Parse support, copy MKObject.h/m and MKObject+ParseExtensions.h/m files to your project. Change all your NSObject based models to MKObject based models.

Start saving your models to parse by calling

[[myObject pfObject] saveInBackground]

Source code

Complete source code is available royalty free on Github. Feel free to fork or use it in your apps. It’s licensed under a very liberal GPL (pun) MIT license. :)

MKFoundation on Github


Mugunth

Follow me on Twitter

  • GPL MIT license? Woah, impressive crossover!

    • Isn’t GPL getting rendered in strikethrough font? 😛

    • MugunthKumar

      GPL should be in strike through font (pun)
      Not getting rendered like that?

  • Ke

    Hi, Mugunth. I don’t quite understand your implementation of MKObject. I notice that you handle PFObject serialization and deserialization recursively. Why not do the same thing with MKObject?

    • MugunthKumar

      NSJSONSerialization takes care of recursion there. But I might be wrong. Let me check.

  • ntt

    OK. Noobish question.. The title talks about Obj-C runtime. But the post is only about MKFoundation. What am I missing?

    • MugunthKumar

      MKFoundation is written completely using the runtime methods!

  • iOS Noob

    Was very impressed with MKObject XML serialization and deserialization at first… Until I realized during deserialization all of the object’s properties are set through the stringValue:

    [self setValue:[value stringValue] forKey:propertyName];

    Now try to serialize an MKObject with an NSNumber property, deserialize it back from XML, and then assign that deserialize’d object’s NSNumber property to another NSNumber. This WILL throw an exception and kill your app.

    Don’t get me wrong Mugunth, I appreciate your contribution to the open-source community and I especially appreciate the MIT license. It’s just frustrating that I’ve built my app on over-hyped open-source libraries that turn out to be garbage, bringing my app down with it.

    • MugunthKumar

      You are doing it wrong. XML is not a native Foundation object nor it supports a binary style blobs. Converting to XML is “lossy” and it loses some info, in your case it is the data type. XML is string based. Every object has to be converted to strings. and XML doesn’t support datatypes other than strings.
      In your case, you have to override setValue:forKey method to convert the string “1” to a NSNumber.
      What you did is similar to opening a PSD and re-saving it over and over as JPG and blaming the tool for loss of data/clarity.

      • Ios Noob

        No, you are doing it wrong Mugunth.

        Yes, XML is lossy, no argument there. But you are totally barking at the wrong tree. Why look for data types in XML when they are all declared in your MKObject subclasses?

        To follow your analogy (though not necessarily accurate), it’s like creating a lossless PNG image from a lossy JPG source, when you had the original lossless PSD file all along.

      • Ios Noob

        To be fair, I wrote my own XML serializer/deserializer this afternoon inspired by your MKObject+XMLExtensions category. It’s a lot more complex than your implementation but it works like a charm.

        I just noticed that you have a .sg email address. I’m in Singapore as well. If you want, I can explain my code to you over coffee.

        Your treat of course.

        • MugunthKumar

          A more complex implementation works for one project. But it can’t be generically used by everyone. To be fair, Apple’s own KVC implementation loses datatypes.

          • Ios Noob

            I beg to disagree. Most of the time it’s poor design, not complexity, that hinders a generic implementation.

            Just a couple more notes in case you’d want to improve your library. A snippet of your code below:

            id value = nil;if([array count] > 0)   value = [array objectAtIndex:0];

            What’s up with that? If your code was able to deserialize an array of strings (not at all uncommon in XML), you’re only taking the first value and discarding the rest. For an array of 2 strings, that’s 50% data loss (not data type loss), and it all goes downhill from there.

            In the serializing part, another snippet of your code:

            if(firstChar >= ‘A’ && firstChar <= 'Z') {          DDXMLNode *node = [DDXMLNode elementWithName:propertyName stringValue:[value description]];          [rootElement addChild:node];        }

            You are basically ignoring properties that don't start with uppercase characters. Why?

            I genuinely appreciate that you are sharing your library. As I said, my implementation is inspired by your code. I myself will be releasing it royalty-free after I clean it up. I just find it irresponsible for you (an author of an advanced iOS programming book, no less) to claim that "[MKObject] can convert any object to an equivalent XML tree" when clearly it can't.

            I apologize if my comments are harsh, it's just from the frustration that my project almost failed because I was relying on your library.

          • iOS Noob

            Was trying to point out the errors both in your library and in your claims on what it can accomplish, for the sake of other developers seeking a similar solution.

            Granted, the comments have been harsh and possibly unfair, but to delete my last response? Especially after you’ve updated your code to fix the one of the errors that I’ve pointed out? 😀

  • Ryan Sweazey

    Hi Mugunth,
    Just bought the iOS 6 book and love it….quick question about your PFObject extension..

    For fetching of PFObjects from Parse, I run a PFQuery which returns an instance of PFObject. In order to manipulate the data I have tried converting the fetched PFObject using [MKObject objectFromPFObject], however, the objectID is not retained and thus when I perform a save, a new record is created in Parse…do you have any suggestions as to how I can circumvent this issue?

    Thanks,
    Ryan

    • MugunthKumar

      Parse is updating their code too often which is a natural progression for a startup. This code was a snapshot back almost a year ago. You probably have to handle that by adding a property in MKObject and transfer object id to and from Parse.
      Mugunth
      Author | Developer | Trainer
      iostraining.sg
      iOS 6 Programming Pushing the Limits book
      http://mk.sg/ios6book

  • Максим Буньков

    Pls help.. How can i parse MKObject to XML with attributes?

  • WilliamX

    This is the liker error I get when I add MKObject.h and .m to my project.

  • WilliamX

    I’ve just tried to output [orderVO prettyJSONString], where inside orderVO there is a NSArray with n objects of type: “OrderEntry”, and I get the error “NSInvalidArgumentException’, reason: ‘Invalid type in JSON write (OrderEntry)'” any ideas why?

    • Looks like one of your model classes has an embedded object that’s not a MKObject subclass