Thursday, September 23, 2010

Dealloc

Last week there was a bit of a Twitter in-fight in the iOS community over the "right" way to release your instance variables in dealloc. I think Rob actually started it, to be honest, but I probably shouldn't be bringing that up.

Basically, several developers were claiming that there's never a reason to set an instance variable to nil in dealloc, while others were arguing that you should always do so.

To me, there didn't seem to be a clear and compelling winner between the two approaches. I've used both in my career. However, since we're in the process of trying to decide which approach to use in the next edition of Beginning iPhone Development, I reached out to Apple's Developer Tools Evangelist, Michael Jurewitz, to see if there was an official or recommended approach to handling instance variables in dealloc.

Other than the fact that you should never, ever use mutators in dealloc (or init, for that matter), Apple does not have an official recommendation on the subject.

However, Michael and Matt Drance of Bookhouse Software and a former Evangelist himself, had discussed this issue extensively last week. They kindly shared their conclusions with me and said it was okay for me to turn it into a blog post. So, here it is. Hopefully, I've captured everything correctly.

The Two Major Approachs

Just to make sure we're all on the same page, let's look at the two approaches that made up the two different sides of the argument last week.

Just Release

The more traditional approach is to simply release your instance variables and leave them pointing to the released (and potentially deallocated) object, like so:
- (void)dealloc
{
[Sneezy release];
[Sleepy release];
[Dopey release];
[Doc release];
[Happy release];
[Bashful release];
[Grumpy release];
[super dealloc];
}

In this approach, each of the pointers will be pointing to a potentialy invalid object for a very short period of time — until the method returns — at which point the instance variable will disappear along with its owning object. In a single-threaded application (the pointer would have to be accessed by something triggered by code in either this object's implementation of dealloc or in the dealloc of one of its superclasses, there's very little chance that the instance variables will be used before they go away , which is probably what has led to the proclamations made by several that there's "no value in setting instance variables to nil" in dealloc.

In a multi-threaded environment, however, there is a very real possibility that the pointer will be accessed between the time that it is deallocated and the time that its object is done being deallocated. Generally speaking, this is only going to happen if you've got a bug elsewhere in your code, but let's face it, you may very well. Anyone who codes on the assumption that all of their code is perfect is begging for a smackdown, and Xcode's just biding its time waiting for the opportunity.

Release and nil

In the last few years, another approach to dealloc has become more common. In this approach, you release your instance variable and then immediately set them to nil before releasing the next instance variable. It's common to actually put the release and the assignment to nil on the same line separated by a comma rather than on consecutive lines separated by semicolons, though that's purely stylistic and has no affect on the way the code is compiled. Here's what our previous dealloc method might look like using this approach:

- (void)dealloc
{
[sneezy release], sneezy = nil;
[sleepy release], sleepy = nil;
[dopey release], dopey = nil;
[doc release], doc = nil;
[happy release], happy = nil;
[bashful release], bashful = nil;
[grumpy release], grumpy = nil;
[super dealloc];
}

In this case, if some piece of code accesses a pointer between the time that dealloc begins and the object is actually deallocated, it will almost certainly fail gracefully because sending messages to nil is perfectly okay in Objective-C. However, you're doing a tiny bit of extra work by assigning nil to a bunch of pointers that are going to go away momentarily, and you're creating a little bit of extra typing for yourself in every class.

The Showdown

So, here's the real truth of the matter: The vast majority of the time, it's not going to make any noticeable difference whatsoever. If you're not accessing instance variables that have been released, there's simply not going to be any difference in the behavior between the two approaches. If you are, however, then the question is: what do you want to happen when your code does that bad thing?

In the first approach, your application will usually crash with an EXC_BAD_ACCESS, though you could also end up with any manner of odd behavior (which we call a "heisenbug") if the released object is deallocated and its memory is then reused for another object owned by your application. In those cases, you may get a selector not recognized exception when the message is sent to the deallocated object, or you may simply get unexpected behavior from the same method being called on the wrong object.

In the other approach, your application will quietly send a message to nil and go about its merry way, generally without a crash or any other immediately identifiable problem.

The former approach is actually good when you're developing, debugging, and doing unit testing, because it makes it easier to find your problematic code. On the other hand, that approach is really, really bad in a shipping application because you really don't want to crash on your users if you can avoid it.

The latter approach, conversely, can hide bugs during development, but handles those bugs more gracefully when they happen, and you're far less likely to have your application go up in a big ball of fire in front of your users.

The Winner?

There really isn't a clear cut winner, which is probably why Apple doesn't have an official recommendation or stance. During their discussion, Matt and Michael came up with a "best of both worlds" solution, but it requires a fair bit of extra code over either of the common approaches.

If you want your application to crash when a released pointer is accessed during development and debugging, the solution is to use the traditional approach in your debug configuration. If you want your application to degrade gracefully for your users, the solution is to use the newer approach in your release and ad hoc configurations.

One somewhat pedantic implementation of this approach would be this:

- (void)dealloc
{
#if DEBUG
[Sneezy release];
[Sleepy release];
[Dopey release];
[Doc release];
[Happy release];
[Bashful release];
[Grumpy release];

[super dealloc];
#else
[sneezy release], sneezy = nil;
[sleepy release], sleepy = nil;
[dopey release], dopey = nil;
[doc release], doc = nil;
[happy release], happy = nil;
[bashful release], bashful = nil;
[grumpy release], grumpy = nil;

[super dealloc];
#endif
}


That code assumes that your debug configuration has a precompiler definition of DEBUG, which you usually have to add to your Xcode project - most Xcode project templates do not provide it for you. There are several ways you can add it, but I typically just use the Preprocessor Macros setting in the project's Build Configuration:
Screen shot 2010-09-23 at 7.56.28 PM.png

Although the code above does, indeed, give us the best of both worlds - a crash during development and debugging and graceful degrading for customers - it at least doubles the amount of code we have to write in every class. We can do better than that, though. How about a little macro magic? If we add the following macro to our project's .pch file:

#if DEBUG
#define MCRelease(x) [x release]
#else
#define MCRelease(x) [x release], x = nil
#endif


We can then use that macro in dealloc, and our best-of-both-worlds code becomes much shorter and more readable:

- (void)dealloc 
{

MCRelease(sneezy);
MCRelease(sleepy);
MCRelease(dopey);
MCRelease(doc);
MCRelease(happy);
MCRelease(bashful);
MCRelease(grumpy);

[super dealloc];
}


Once you've got the macro in your project, this option is actually no more work or typing than either of the other dealloc methods.

But, you know what? If you want to keep doing it the way you've always done it, it's really fine, regardless of which way you do it. If you're consistent in your use and are aware of the tradeoffs, there's really no compelling reason to use one over the other outside of personal preference.

So, in other words, it's kind of a silly thing for us all to argue over, especially when there's already politics, religions, and sports to fill that role.

The Garbage Collection Angle

There's one last point I want to address. I've heard a few times from different people that setting an instance variable to nil in dealloc acts as a hint to the garbage collector when you're using the allowed-not-required GC option (when the required option is being used, dealloc isn't even called, finalize is). If this were true, forward compatibility would be another possible argument for preferring the newer approach to dealloc over the traditional approach.

While it is true that in Java and some other languages with garbage collection, nulling out a pointer that you're done with helps the garbage collector know that you're done with that object, so it's not altogether unlikely that Objective-C's garbage collector does the same thing, however any benefit to nil'ing out instance variables once we get garbage collection in iOS would be marginal at best. I haven't been able to find anything authoritative that supports this claim, but even if it's 100% true, it seems likely that when the owning object is deallocated a few instructions later, the garbage collector will realize that the deallocated object is done with anything it was using.

If there is a benefit in this scenario, which seems unlikely, it seems like it would be such a tiny difference that it wouldn't even make sense to factor it into your decision. That being said, I have no firm evidence one way or the other on this issue and would welcome being enlightened.

After writing this, Michael Jurewitz pinged me to confirmed that there's absolutely no benefit in terms of GC to releasing in dealloc, though it is otherwise a good idea to nil pointers you're done with under GC..

Thanks to @borkware and @eridius for pointing out some issues

Update: Don't miss Daniel Jalkut's excellent response.

Tuesday, September 21, 2010

Complaining About Success

If you want to see how little sympathy you can get from those around you, try complaining about being successful. It just doesn't tend to be a problem over which people shed tears on your behalf. But I'm actually not here to complain. I'm really thrilled that MartianCraft is growing steadily and, more importantly, that we're getting interesting work.

I did think it was worth popping in here to explain my absence of late, however. I've got several partially written posts sitting in MarsEdit, including a couple for my recently-started and just-as-lonely personal blog. But time has become a scarce commodity for me, and will likely remain that way for at least the next month or two, possibly even longer. Even my OpenGL ES book for prags has suffered lately as I've been basically working double-time trying to catch up on my workload. Even the move to Florida is being affected, as we probably won't get our house on the market now until the new year.

The fact that I've been doing so much coding does mean that I've got some great ideas for blog posts, so hopefully once the dust settles, I'll have some goodies for you, but bear with me in the meantime.

Oh, and a somewhat related note. A client of MartianCraft is looking for a really mobile developer to work in New York City. They'll be happy with any mobile experience, with preference for iPhone or Android experience. Having both is ideal. This client is a very large media company and you'd basically be responsible for leading one division's mobile efforts, starting with maintaining their existing apps, but eventually eventually extending them and building new ones. It's a great opportunity for the right person. If you're in New York or willing to relocate there and have some good mobile chops, drop me an e-mail with your resume (my main e-mail is my twitter name at mac dot com or you can send them to jeff at martiancraft dot com) and I'll put it in front of the hiring manager.

Thursday, September 16, 2010

Global Symbolic Breakpoints in Xcode 4

It wasn't obvious to me how to set a global symbolic breakpoint in Xcode 4. I stumbled across the answer today. Because Xcode 4 is still under NDA, I can't post this here, so instead, I wrote it up on Apple's Dev Forums (Dev Center login with beta access required).

Saturday, September 11, 2010

Microsof't Funeral

There's really not much fun in bashing Microsoft any more. Although they're still firmly entrenched in the desktop space and profitable as a company, their attempts to make inroads into emerging markets have been less than stellar and in what's arguably the most important emerging market – mobile – they've gone from being a moderately strong player to an almost non-existent fringe player in just a few short years.

You may not believe this, but I'm actually rooting for Windows 7 Phone (though I still hate the name). I really want it to be good, and it has the potential to be good, and even the potential to be better than Android out of the gate. Microsoft's problem has never been lack of good engineers; their problems are almost exclusively management making it impossible for the engineers to deliver something awesome. A lot of Microsoft products have had the potential to be good, but weren't.

But, maybe this is the product that will turn the tide. And if Windows 7 Phone can get some traction, there are a lot of developers out there familiar with C#, .Net, and Visual Studio who will jump aboard their market. I've actually been hearing a lot of good things about the Windows 7 Phone SDK and tools from people. I'm not sure I'd like them - I've never really liked the high-level approach that .NET takes - but a lot of people do, so there's a large pool of potential developers if they can get the platform off the ground.

I'd really love to see the mobile space avoid the quasi-monoculture problems that the desktop space has had thanks to the dominance of Windows. Real competition is good.

So, yeah… I'm kinda rooting for Windows 7 Phone even though I know there's at pretty good chance that Microsoft Management Idiocy™ has ruined another potentially good product. I'm cautiously optimistic based on what I've been hearing from people with access to the tools.

Given that, then, I found yesterday's news of Microsoft's RTM "parade" for Windows 7 Phone very sad. Who throws a victory party before the race has even started? Hell, who even throws themselves a parade?? In the words of Wil Shipley, it's just gauche.

I think the funeral was really for Microsoft's last shred of dignity.

Thursday, September 9, 2010

App Store Review Guidelines

Today, Apple posted guidelines for the App Store review process (requires developer login). This is a huge step in the right direction. The document still contains a lot of wiggle room and vague conditions and doesn't change the fact that Apple still has total discretion and can reject your app for new, currently unstated reasons if they want to, but it is definitely good guidance for App Store developers and should reduce the number of rejections for unknown reasons. If you develop for the App Store, you should read this end-to-end. Now.

Apple also issued a press release today that states that Apple is setting up a review process for rejected apps and also says that Apple is loosening the restrictions contained in several clauses of the Developer Program License (3.31, 3.3.2, and 3.3.9). I'm hoping that these changes will allow Briefs.app on the App Store, but I haven't heard anything yet from Rob to indicate that the review status has changed.

Fingers crossed for Rob and Briefs.app and kudos to Apple for listening and making changes.

Sunday, August 29, 2010

Core Dump

I've decided to create a second blog at http://jeff-lamarche.blogspot.com where I'll put any posts that are not related to writing iOS software, Apple, or the mobile software industry.

My first substantive post should show up some time this evening if all goes well.

Saturday, August 28, 2010

Mea Culpa

The original version of my Briefs.app blog post contained a snarky comment about the Director of the App Review team and a link to a Wired article about his past. A friend of mine, who happens to work at One Infinite Loop, privately took me to task for including that link in my post.

It took me about two seconds, now that I'm a little further away from it and not as angry, to realize that he's 100% right. It was a dick move on my part and I'm sorry for it. I have removed the link and offer my apologies to Phillip Shoemaker. The fact is, I have no visibility into what's going on and don't know where the holdup is on Briefs.app. For all I know, Mr. Shoemaker could be valiantly fighting for Briefs.app against higher up forces in the company, so casting an ad hominem attack like that was juvenile and uncool.

My basic feelings about the matter are unchanged, however. It was uncool when Apple left Google Voice in limbo, but that was one small product from one very large company. In this case, it's the sweat equity from one individual developer who worked very, very hard, sacrificing sleep, family time, and entertainment to create something very cool. The impact is much greater and much more personal and it's hard for me not to get angry about it.

Thursday, August 26, 2010

Briefs.app

My co-worker and all-around good guy, Rob Rhyne has officially open sourced Briefs.app as of today. After three months of being dicked around by Apple's review team, he's finally given up on getting Briefs.app onto the App Store.

Throughout the ordeal, Rob has taken the whole thing with tremendous grace and has only good things to say about the people involved in the entire process. I hope he'll forgive me for not being quite so gracious.

I'm pissed on his behalf, since he won't be. Make no mistake: This sucks. This is no way to treat anybody, but especially him. Rob has bent over backwards throughout the process to be nice and work within the system and to avoid saying anything negative about the problems he's faced. Rob has kept the discourse on a level I think few of us could manage. He didn't go out and raise a stink the way many developers have when they felt slighted by the App Review team. Rob just calmly and patiently worked within the system trying to make his case and get a product he worked on for months onto the app store… while working a full time job, starting a new business, and being a parent to a toddler. Oh, and his wife works too. Rob's one of the few developers I know who spends more time sitting at a computer than me.

If Apple's review team had just come out and rejected the app, it would have sucked, and it would have been the wrong decision, but it would have been an acceptable situation. The app review team's job is to make tough decisions. Sometimes they're going to make bad decisions, and sometimes they're going to make decisions that we developers are going to disagree with.

But since making decisions is, in fact, their job and they've never actually made a decision about this particular app, it's not an acceptable nor a forgivable situation. Three fucking months Briefs.app has sat in the review queue, and in that time, the app review team has allowed other prototyping applications onto the app store: applications that do the same basic tasks that Briefs.app was created to do. Interface was approved several weeks after Briefs.app was submitted to the App Store. LiveView and Dapp were both updated just yesterday. iMockups was approved about a week ago.

But Briefs still sits in the queue and nobody can be bothered to even say what the exact holdup is or what needs to happen before a decision will get made.

I don't know what reason they could have for dragging it on like this and not making a decision, but it's not right. Apple owes Rob, and all the people who want to use Briefs, an apology.

Voices that Matter Philadelphia

Just wanted to let you know that I will be speaking at the next Voices That Matter iPhone Developer Conference in Philadelphia, PA which runs from October 16 and 17th. I was originally asked to do a talk on 4.0 Multitasking, which I will be doing. I've also recently been asked to give a second talk on OpenGL ES. I've agreed to do that presentation as well, and now I have a few days to decide on the specific topic and audience and to write up the abstract.

I was thinking of doing a "from 1.1 to 2.0" kind of talk, exploring the differences between the fixed and programmable pipeline and trying to explain some of the more daunting concepts of the programmable pipeline. The goal would be to make the jump from 1.1 to 2.0 (which Apple is pushing) less scary.

I'm not sure if that's a topic with broad enough appeal, though. Are you going to VTM? What would you like to see from an OpenGL ES talk?

Wednesday, August 25, 2010

Beta Builder

A week or two ago, Jeffrey Sambells had a blog post about using Xcode's Enterprise distribution for ad hoc distribution. It's a great idea that a lot of developers saw had great potential, but it had several manual steps.

Since that post, Hunter Hillegas of Hanchor, LLC and developer of Vegas Mate, has created an application to automate the entire process. The app is called Beta Builder, and it's free. If you do much ad hoc distribution, you should check it out.

App Licensing

For all those people telling me that Google's App Licensing would put a definitive stop to piracy on Android and that Apple should implement something similar, all I can say is: I told you you were wrong and here's proof, and it didn't take even as long as I said it would.

I understand Google has to address piracy because it's a bit of a black eye for the platform. They need third party developers, and a lot of third party developers are gun-shy about developing for a platform with a reputation for rampant piracy. Although the iPhone has its own problems with piracy, it's on a completely different scale. The closed nature of the platform is actually an advantage for third party developers, much the way gaming consoles are. Sure, Apple's protection scheme has been compromised and any App posted to the app store can be pirated easily. But, because only people who Jailbreak their phones can actually install the hacked software, and Jailbreaking a phone can cause problems with future updates from Apple, there's built-in damage control.

It also helps that you can buy App Store apps in every country where you can legally buy an iPhone. On Android, you can only buy apps in 13 countries, meaning people in other countries either have to do without paid apps, or have to pirate. There's built-in incentive in that system for people who might be perfectly willing to pay for an app to go pirate it. Google would get more for their piracy-battling dollar by expanding the paid markets than by implementing more hare-brained licensing schemes that won't ever make a dent in piracy.

I don't envy Tim Bray's task of having to try and reassure Android developers that this crack isn't a problem. Of course it's a problem. It's a problem the media industries have been fighting with since the dawn of digital content. The RIAA, MPAA, and media companies have invested millions, maybe even billions of dollars into various schemes that have failed to make much of a dent in piracy.

For those who think this App Licensing can ever work, you (much like the media industry) fail to grasp the inherent technical flaw with any kind of DRM. With DRM, you have to provide the legal purchaser with the content and with the key to unlock that content. No matter how sophisticated the stuff in-between, no matter how complex the lock, a sufficiently technically knowledgeable person who has the content and the key to unlocking it can find a way to free the content from its protection. On Android, once you've freed the content from its DRM, you can distribute it to anybody because of the ability to sideload applications. So on most Android phones right now, once a single copy of a program has been hacked, it's just as easy to pirate as it was without App Licensing.

With software, finding the balance between making it inconvenient to pirate (because you can't make it impossible) without overly inconveniencing your customers is hard. It's easier on a closed platform. That's not to say there aren't downsides to a closed platform — of course there are — but this is one of the clear advantages for third party developers. Truth be told, you simply can not stop the dedicated pirate, but a closed platform does deter the bulk of the pirates who can be diverted into paying customers by making it inconvenient. Best of all, it does it without affecting the legal purchasers, unlike most DRM.

When it comes down to it, the most effective way to stop piracy is to make it easy and convenient for as many people to buy content legally as is possible and to price it fairly. This is something Google clearly doesn't get, or else you'd be able to buy paid apps everywhere you can buy an Android phone.

Tuesday, August 24, 2010

UIImage-Blur

Many moons ago, I wrote convolution kernel for Cocoa. It had convenience class functions to do many different types of filters, including blurring, embossing, outlining, edge detection, horizontal shift, LaPlacian, soften, high pass, etc. Now, this was before Core Image and long before the switch to Intel. I don't remember exactly when I first wrote it, but I'm guessing it was around 2001 or 2002. The method that actually applied the filter to an image used AltiVec if it was available, and if it wasn't, it did a brute force filter on the CPU.

Of course, once the switch to Intel happened, the AltiVec code was no longer was helpful, and then Apple came out with Core Image which includesda convolution kernel and all of the filter settings I had created and more. So, I stopped maintaining the code.

Then, when the iPhone came out and didn't have Accelerate or Core Image, I saw a potential use for the old source code. I had a project early on where I needed to be able to blur an image. So, I blew the dust off the old code. I didn't convert the entire convolution kernel – I didn't want to go through the effort if it wasn't going to work — so I created a blur category on UIImage. And it didn't work.

Pressed for time, I found another solution because I was uncertain the processor on the original iPhone would be able to apply a convolution kernel fast enough for my purposes, but I included the broken code when I released the source code for replicating the Urban Spoon effect. Today, I received an e-mail from a reader who figured out my rather boneheaded mistake. The convolution filter was fine, I just specified the wrong CGImage when specifying my provider while converting the byte data back to a CGImage.

Now, since I wrote that code, Apple has come out with the Accelerate framework for iOS, so it could definitely be made faster. It's unlikely that I will be able to spend the time converting it to use Accelerate unless I need a convolution kernel for my own client work; I've got too much on my plate right now to tackle it. If anyone's interested in doing the full convolution kernel port, you can check out the source code to Crimson FX. It's an old Cocoa project which may not work anymore, but it has, I believe, the last version of the convolution kernel before I gave up maintaining it. It shouldn't be hard to port the entire convolution kernel to iOS in the same manner. Once you get to the underlying byte data, the process is exactly 100% the same (even if byte order is different), and the code to convert to and from the byte data is this UIImage-Blur category.

So, without further ado, I've created a small scaffold project to hold the unaccelerated UIImage-Blur category. Have fun with it and let me know if you use it in anything interesting. If you improve it and would like to share your improvements, let me know, and I'll post it here.

You can find the source code right here. Here's a screenshot of the test scaffold with the original image and after several blurs have been applied. The image is from the Library of Congress Prints and Photographs Collection. Plattsburgh is where I grew up, so this public domain image struck my fancy. I don't know why, but the Army Base was spelled Plattsburg without the ending 'h' even though the city has always been Plattsburgh with the ending 'h'.

Screen shot 2010-08-24 at 10.18.58 AM.png

Thanks to Anthony Gonsalves for finding my error!

Thursday, August 19, 2010

Transparent Grouped Tableviews

I ran into a problem today with a transparent grouped table view. On the table view, I set opaque to NO and set the background color to clearColor so that an image behind the table would show through:
Screen shot 2010-08-19 at 1.55.43 PM.png
I can't show you the actual app I was working on because I'm under NDA, but I've created a sample project that shows the problem using a really ugly background image. Notice the black around the corners of the group sections:
Screen shot 2010-08-19 at 1.54.21 PM.png

It took me a while to figure out why this was happening, but with a little crowd-source debugging thanks to Twitter, I've narrowed the culprit down. Even though you can clearly see in the first picture that the table view's background color has been set to clearColor in Interface Builder, that setting is either not being respected, or is being changed somewhere. Now, I'm using stock elements here - no custom views at all - so I know I'm not changing it.

My original fix for this was rather involved, but after a process of elimination where I kept commenting out bits of the fix and and seeing what impact it had, I was able to remove all of the code of my fix except for this line of code:

- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.backgroundColor = [UIColor clearColor];
}


Turns out all the other parts were red herrings leftover from my various attempts to fix the problem. So, if you're having this problem, just manually re-set the table view's background color to clearColor and you should be good.

(and yes, I'm going to file a bug report now)

Core Data Starting Data

Yesterday, I tweeted asking for advice about the best way to provide a starter set of data in a Core Data-based application. The app I'm working on had started with just a small set of starter data, so the first time the app was run, I simply pulled in that starter data from a plist in the background and created managed objects based on the contents and all was good. The user never noticed it and the load was complete before they needed to do anything with the data.

Then the data set got quite a bit larger and that solution became too slow. So, I asked the Twitterverse for opinions about the best way to provide a large amount of starting data in a Core Data app. What I was hoping to find out was whether you could include a persistent store in your application bundle and use that instead of creating new persistent store the first time the app was launched.

The answer came back from lots and lots of people that you could, indeed, copy an existing persistent store from your app bundle. You could even create the persistent store using a Mac Cocoa Core Data application as long as it used the same xcdatamodel file as your iPhone app.

Before I go on, I want to thank all the people who responded with suggestions and advice. A special thanks to Dan Pasco from the excellent dev shop Black Pixel who gave very substantive assistance. With the help of the iOS dev community, it took me about 15 minutes to get this running in one of my current apps. Several people have asked for the details over Twitter. 140 characters isn't going to cut it for this, but here's what I did.

First, I created a new Mac / Cocoa project in Xcode. I used the regular Cocoa Application template, making sure to use Core Data. Several people also suggested that you could use a Document-Based Cocoa Application using Core DAta which would allow you to save the persistent store anywhere you wanted. I create the Xcode project in a subfolder of my main project folder and I added the data model file and all the managed object classes from my main project to the new project using project-relative references, making sure NOT to copy the files into the new project folder - I want to use the same files as my original project so any changes made in one are reflected in the other.

If my starting data was changing frequently, I'd probably make this new project a dependency of my main project and add a copy files build phase that would copy the persistent store into the main project's Resources folder, but my data isn't changing very often, so I'm just doing it manually. You definitely can automate the task within Xcode, and I heard from several people who have done so.

In the new Cocoa project, the first thing to do is modify the persistentStoreCoordinator method of the app delegate so it uses a persistent store with the same name as your iOS app. This is the line of code you need to modify:

NSURL *url = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"My_App_Name.sqlite"]];


Make sure you add the .sqlite extension to the filename. By default, the Cocoa Application template uses an XML datastore and no file extension. The filename you enter here is used exactly, so if you want a file extension, you have to specify it.

Since the Cocoa Application project defaults to an XML-based persistent store, you also need to change the Cocoa App's store type to SQLite. That's a few lines later. Look for this line:

if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType 
configuration:nil
URL:url
options:nil
error:&error
]
){


Change NSXMLStoreType to NSSQLiteStoreType.

Optionally, you can also change the applicationSupportDirectory method to return a different location if you want to make the persistent store easier to find. By default, it's going to go in
~/Library/Application Support/[Cocoa App Name]/
which can be a bit of a drag to navigate to.

Next, you need to do your data import. This code will inherently be application-specific and will depend on you data model and the data you need to import. Here's a simple pseudo-method for parsing a tab-delimited text file to give an idea what this might look like. This method creates an NSAutoreleasePool and a context so it can be launched in a thread if you desire. You can also call it directly - it won't hurt anything.


- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:self.persistentStoreCoordinator];


NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];

char buffer[1000];
FILE* file = fopen([path UTF8String], "r");

while(fgets(buffer, 1000, file) != NULL)
{
NSString* string = [[NSString alloc] initWithUTF8String:buffer];
NSArray *parts = [string componentsSeparatedByString:@"\t"]

MyManagedObjectClass *oneObject = [self methodToCreateObjectFromArray:parts];
[string release];
}

NSLog(@"Done initial load");
fclose(file);
NSError *error;
if (![context save:&error])
NSLog(@"Error saving: %@", error);

[context release];
[pool drain];
}


You can add the delegate method applicationDidFinishLaunching: to your app delegate and put your code there. You don't even really need to worry about how long it takes - there's no watchdog process on the Mac that kills your app if it isn't responding to events after a period of time. If you prefer, you can have your data import functionality working in the background, though since the app does nothing else, there's no real benefit except the fact that it's the "right" way to code an application.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Do import or launch threads to do import

// So, do this:
[self loadInitialData];

// or this:

[self performSelectorInBackground:@selector(loadInitialData) withObject:nil];

// But not both for the same method probably
}


Now, run your app. When it's done importing, you can just copy the persistent store file into your iOS app's Xcode project in the Resources group. When you build your app, this file will automatically get copied into your application's bundle. Now, you just need to modify the app delegate of your iOS application to use this persistent store instead of creating a new, empty persistent store the first time the app is run.

To do that, you simply check for the existence of the persistent store in your application's /Documents folder, and if it's not there, you copy it from the application bundle to the the /Documents folder before creating the persistent store. In the app delegate, the persistentStoreCoordinator method should look something like this:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
{
@synchronized (self)
{
if (persistentStoreCoordinator != nil)
return persistentStoreCoordinator;

NSString *defaultStorePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"My_App_Name" ofType:@"sqlite"];
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"My_App_Name.sqlite"];

NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(@"Copied starting data to %@", storePath);
else
NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
}


NSURL *storeURL = [NSURL fileURLWithPath:storePath];

persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil
]
;

if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{

NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}


return persistentStoreCoordinator;
}

}


Et voilĂ ! If you run the app for the first time on a device, or run it on the simulator after resetting content and settings, you should start with the data that was loaded in your Cocoa application.

 
Design by Wordpress Theme | Bloggerized by Free Blogger Templates | coupon codes