sending link lists using NSData - iphone

i am currently in the middle of implementing the network side of my multi player game. So far i am sending classes across by serialising my objects into NSData. The trouble lies in the fact that i have link lists within my class referencing other objects of the same type. If i transferred all the objects across and decoded on the receiving end using initWithCoder: (NSCoder *) decoder would the objects within the class still hold the correct references? If not can anyone recommend a clean solution to this problem?

NSCoder should work fine for this. When you encode, encode the head of the list and use the list structure itself to guide the rest. Say the list has a string for it's data and a simple forward link. Just encode the head...
// in ListNode.m
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.theString forKey:#"thisIsMyListsData"];
if (self.nextNode) {
[encoder encodeObject:self.nextNode forKey:#"nextNode"];
}
}

Related

How do I convert a NSSet to Data in Swift

I need to send a NSSet over the network, so I need to convert it to Data.
I have tried this:
let setData : Data = withUnsafeBytes(of: mySet) { Data($0) }
without success.
Convert to NSData is easy but what about Data?
How do I do that?
Either convert it to a general-purpose serialization form (JSON or binary Plist) using Codable (if it needs to be parsed by another system in a portable format), or hand it to NSKeyedArchiver using NSCoder (if it needs to be read just by Cocoa apps, and you want to save the "set-ness" and any object relationships).
Details on NSKeyedArchiver are in the Archives and Serializations Programming Guide.
Details on Codable are in Encoding, Decoding, and Serialization.
NSSet isn't Codable directly, but that doesn't matter because JSON and plists don't support sets anyway. You can just convert it to an Array (or Set).
Since you said that converting to NSData is easy, maybe you're already aware of all this, and all you need is Martin's comment: add as Data.

How to use my Class with PList in objective-c?

I have a Class for handling my data in my project, and now I need to store the data.
I'd like to use a Plist but I'm a bit unsure of how to start.
My class is pretty simple - 6 pieces of data, flat (no hierarchy).
I want my app to start with no data, so can I assume that I should create the PList programmatically once the User creates their first piece of data? (That is, don't create a .plist file in 'Supporting Files' prior to distribution?)
Then, when the app starts the next time, read the data and create an NSMUtableArray array of Class instances?
To create a property list, all you need to do is use appropriate types (i.e. those that support the property list format: NSData, NSString, NSDictionary, NSNumber, NSDate, NSArray), store them in a single container, and tell the containing object to write itself to a file. To read the data, you can initialize that same type using a path. For example:
// writing some data to a property list
NSString *somePath = ... // replace ... with the path where you want to store the plist file
NSMutableDictionary myDict = [NSMutableDictionary dictionary];
[myDict setObject:#"Caleb" forKey:#"name"];
[myDict setObject:[NSNumber numberWithInt:240] forKey:#"cholesterolOrIQ"];
[myDict writeToFile:somePath atomically:YES];
// reading the file again
NSDictionary *readDict = [NSDictionary dictionaryWithContentsOfFile:somePath];
The simplest way is to simple save an NSArray or NSDictionary to disk. Caleb's answer goes into detail there so I won't repeat it, other than to say you might have to convert a non-compatible object like NSColor to an property list object like NSData. It's up to you to do this each time you save or load your data.
NSKeyedArchiver and NSKeyedUnarchiver give you a little more control over the process, but work pretty much the same way. You provide (or get back) a plist compatible root object (usually an NSDictionary) that contains your data. I recommend creating a dictionary that includes your data structure as well as an arbitrary number (your app's build number is a good choice) to use as a version indicator. This way if you ever update your data model you can easily determine if you need to do anything to convert it to the new version.
If you're putting your own objects into the data file, look into NSCoding. The protocol gives you two methods using NSKeyedArchiver and NSKeyedUnarchiver to save and restore your data. This is by far the most straightforward approach if your data model consists of anything more than a few simple strings and numbers, since you're dealing with your own native objects. In your case, you would have your data class implement NSCoding and use the NSKeyedArchiver and NSKeyedUnarchiver methods to encode your six instance variables. When it's time to save or load, pack the instance of your class into an NSDictionary (along with a versioning number as I mentioned above) and call NSKeyedArchiver's archiveRootObject:toFile:. Your save an load methods deal only with your own data object, which makes things easy for you. The common pitfall to watch out for here is if your custom data object contains other custom object. This is fine, but you have to make sure every object that's going to be saved has its own NSCoding implementation.
Two things you can do:
Use NSUserDefaults:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
The objectForKey method is the one you want to use to store your class. But, as pointed out in the comments, this shouldn't really be used for storing lots of user data; it's best for saving preferences.
For storing more data, you might want to look at Core Data. It's more complex, but should be better suited to your needs. Here's a tutorial on it:
http://mobile.tutsplus.com/tutorials/iphone/iphone-core-data/
Neither of these seems best for your simple application, but I leave this answer up since it gives alternatives for saving data to the iPhone.

Adding Instance Variable to NSData

So, I understand that Categories in Objective-C can be used to add methods to classes without the need for subclassing. I also understand that these Categories cannot be used to add instance variables to classes.
I did a little bit of reading about Class Extensions, which can be used to add instance variables, but I don't understand how I can use Class Extensions to modify an existing class such as NSData.
My problem is the following:
I have a Core Data Model that contains a NSURL and NSData. The NSData displays the data for the NSURL. When a view needs to display the data, I do the following check:
--- If [NSData bytes] > 0, display the NSData.
--- Otherwise, fetch the data at NSURL and display the data when it returns
Simple enough. However, I run into problems when the NSURL is updated. So, if I modify the NSURL path with a new image, because [NSData bytes] is already greater than 0, I don't make the additional call to fetch the new image.
What I would like to do is add an instance variable to NSData called URLKey that would hold information about where the data comes from. I can't subclass NSData because I'm using CoreData.
Does anyone know some simple solutions for this? Perhaps there's a gap in my understanding of Class Extensions, or maybe there's just no simple way.
Class Extensions should be used on classes you implement yourself as a way of keeping ivars and some properties hidden from the header File, that should contain only stuff that should be visible outside the class (and ivars are't that kind of stuff).
Categories are used on classes already implemented, as a way of adding additional functionality. They are usually needed when you want to add a general kind of behavior to a known Class. E.g. adding a method to NSString +(NSString*)reversedString; that returns a reversed instance so you can then use it like this:
NSString *someString = #"string";
NSString *reverse = [someString reversedString];
NSLog(#"%#", someString); //this would output "gnirts"
.
Regarding your particular problem, I can assure you that your CoreDataModel does not contain NSURL or NSData. The supported types are primitives, strings, binary Data and transformables. So, if you want to, you can subclass NSData or NSURL and then use it with CoreData by setting the type to "transformable". And after you have done this, you can then subclass NSData as you wish and use class extensions in the process, or just use a category to add the methods you require to the class.
Quote from Apple about transformable attributes:
The idea behind transformable attributes is that you access an
attribute as a non-standard type, but behind the scenes Core Data uses
an instance of NSValueTransformer to convert the attribute to and from
an instance of NSData. Core Data then stores the data instance to the
persistent store.

Architectural approach to HTTP communications and the parsing of returned JSON in iOS

Good evening guys,
My question is more of an engineering/design pattern approach than specifically technical.
I am developing an app that requires lots of interaction with a remote API returning JSON objects. The retrieval, parsing and utilisation of the data is not a problem and is working very smoothly. I am wanting to get some direction on the best design approach for this sort of scenario.
I will explain what I have so far (in pseudo code and declarations) and see if you can help:
A HTTP Fetcher class implementing the necessary NSURLConnection delegate methods. I initialise the class with the callback method selector like so for returning to the calling class on completion
#implementation HTTPFetcher{
- (id)initWithUrlRequest:(NSURLRequest *)aRequest receiver:(id)aReceiver action:(SEL)aReceiverAction
{
//set the member variables etc..
}
//all NSURLConnection delegate methods
- (void)connectionDidFinishLoading...
{
[receiver performSelector:action withObject:self];
}
}
I then have a Singleton HTTPController class for calling the HTTPFetcher:
- (void)postWithRequestString:(NSString *)aRequestString
{
[urlRequest setHTTPBody:[aRequestString dataUsingEncoding:NSUTF8StringEncoding]];
fetcher = [[HTTPFetcher alloc]initWithUrlRequest:urlRequest receiver:self action:#selector(receivedDataFromService:)];
[fetcher start];
}
- (void)receivedDataFromService:(HTTPFetcher *)aFetcher{
//handle the received data and split the parent object into an NSMutableDictionary
}
Now this approach works fantastically well for the app I have especially given the separate entities that I have to model (I will basically have a Singleton HTTPController for each entity).
My issue is where to handle the custom parsing of the JSON. Currently, I am doing the parsing the in ViewController where the data is required but this is too close to the source and needs to be abstracted out further but I am unsure how.
Should I include the methods to facilitate the parsing within the Singleton classes or should I create further controllers for parsing actions?
I look forward to hearing from you
Thanks
I would recommend you build on an existing JSON parsing library, in particular John Engelhart's JSONKit, considering it's arguably the highest performance JSON parsing library out there for iOS. Saves you implementing custom JSON parsing at all, but especially saves you implementing code which turns out to be too slow for your needs and then you will need to iteratively refine it until it gets fast enough for you to use.
For HTTP requests, I know you've implemented the behaviour already, but you might want to investigate ASIHTTPRequest or AFNetworking as general purpose networking libraries which have a reputation for being quite robust. Note AFNetworking uses the above JSONKit library for JSON parsing.
The way ASIHTTPRequest (the library I use in my projects) works is by using a delegate object implementing the protocol ASIHTTPRequestDelegate, which you assign after creating a request with a URL. There's a global network queue which is just an NSOperationQueue, and that handles asynchronous or multiple concurrent active requests.
You can setDelegate: for the object to start checking whether your delegate has implemented any of the methods at different points, such as didReceiveData: or requestDidFinish: by default, but you can also set a custom selector path to check by using the methods for individual operations (setDidFinishSelector:#selector(downloadComplete:)).
What you could do when, for example, the didReceiveData: callback happens, is pass the newly received data into a buffer stored in a wrapper class for an ASIJSONRequest (or use AFNetworking, which already encapsulates this). When the buffer is such that there is a complete JSON object in there which can be parsed correctly, then you call out to JSONKit to do the grunt work and then maybe send another callback yourself to an ASIJSONRequestDelegate for didReceiveData:, but now the data is in a format which is readable by the rest of your application.
Another method of using ASIHTTPRequest is with blocks. There is support for setting a completion block for a request, a block that is called when data is received, etc. For this design pattern you don't even need a wrapper class, just set the code block up to do the parsing itself and return any new data parsed to its desired destination.
One possibility would be for the View or view controller to ask a Model object for any state that it needs (including stuff from a remote server). The Model object would be told when there was any new data from the server, and it could then call any required data munging routines required to update its internal state (converting plists or json into a more canonical dictionary format, for instance).

How to store an image in Core Data?

Just a guess: I make an attribute and make it's type "binary". But in the end, how would I use that? I guess there is an NSData behind the scenes? So that attribute actually takes an NSData?
This question has been asked a number of times and the answer is a bit more complex.
When it comes to binary data you should determine how to store it based on the expected size of data you are going to be working with. The rule is:
Less than 100K;store as a binary property in your main table
Less than 1M; store as a binary property in a ancillary table to avoid over fetching
Greater than 1M; store on disk and store its file path in the Core Data table.
In addition, when you are storing images, it is recommended to store them in a standard format such as JPG or PNG. By using the transformable property type you can actually have your subclass give the appearance of accessing the UIImage class when the actual store is a PNG representation. I go into this in detail in the bog post on Cocoa Is My Girlfriend.
Update
The reason behind storing > 1M binary data on disk is because of the cache. The NSPersistentStoreCoordinator will keep a cache of data so that when your app asks for the "next" object it doesn't need to go back out to disk. This cache works really well. However it is small, very small on iOS. If you pull in a big piece of binary data you can easily blow out that entire cache and your entire app suffers greatly.
That's correct, use binary which is represented as a NSdata object, then u can use uiimages imageWithData class method in order to retrieve your images.
Marcus's answer works, but it's not the easy way - and, AFAICT, it's not how Apple "intends" you to do it. In theory, you should be able to just mark the image as "Transformable", and CD would do everything automatically.
That doesn't work because UIImage is "missing" the implementation of NSCoding - Apple's serialization system - and so the default transformer from CD has no idea how to save an image.
The alternative, which requires a little code, but is still simpler than Marcus's approach ... allegedly you run into Apple's bugs. Apple's CoreData implementation (allegedly) has been broken since 2008 - the custom transformers are ignored for any data-store of type "Binary".
Going back to "CD would do everything automatically, if only UIImage were setup right" ... IMHO, UIImage ought to be serializable, even if the "Default" serialization isn't ideal for all cases. If you fix that, then lo and behold ... CoreData starts saving/laoding UIImage instances with zero coding from you.
Here's code to make your UIImage instances all serializable. All you have to do is import this category / copy paste this category into the classes where you want the "upgraded" UIImage version.
#implementation UIImage (MyCategory)
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeDataObject:UIImagePNGRepresentation(self)];
}
- (id)initWithCoder:(NSCoder *)decoder
{
NSData *data = [[decoder decodeDataObject] retain];
return [[UIImage alloc] initWithData:data];
}
#end