[iOS7] Woes Unto Me, For I Am Undone

While I’m waiting for a full restore of my iPhone that will hopefully get me to a usable state again, there’s a couple of things you should know about iOS programming that has subtly changed.

First off: I like the new interface. I like the new OS. So this is not a mindless rant.

[UIKit]

UIKit has never been threadsafe. Ever. This is why we got used to performSelectorOnMainThread back in the pre-block days, then dispatch_async. The non-threadsafety of UIKit is a total mystery to me, seeing that I can change text in labels, images, and the like, mark the views as dirty, and then the main GUI should take care of the updates however long afterwards. That being said, Android does the same, so there must be a technical reason I’m not seeing. However, Android doesn’t even let me change anything. It’s an immediate exception at runtime. UIKit, on the other hand, lets me do pretty much anything I want, then crashes with a cryptic message, when there is one. After digging and step-by-step debugging, I usually find that what I assumed runs on the main thread, does not, in fact.

But that’s the kicker, right here. Up till iOS6, some conventions (or habits to be more accurate) led me to believe that this callback or notification would always be called on the main thread, thus not forcing me to use a very performance-expensive dispatch block. And then… it changes. The app starts behaving erratically, when it doesn’t outright crash. And debugging that erratic behavior is difficult, because it’s basically a race-condition. It could work just fine on an iPhone 4 but not on a 5, and vice-versa.

For a new codebase, I guess the problem doesn’t arise as much, but for us developers who work on older projects, sifting through thousands of lines of code to figure out which one causes that is time consuming, to say the least.

Case in point : navigation controllers. They used to be somewhat thread-safe, pushing and popping being stacked and executed one after the other. Of course, every now and again, you would have to tweak a little bit, make sure some essential data was loaded before pushing the next one, but nothing illogical.

In iOS 7, it’s totally asynchronous and unstacked. And unless you setup a delegate to make sure you detect the end of the animation, you can end up in very dark places. As soon as you have a more complex navigation than simple tree walking, something that worked reasonably well till now becomes very hard to maintain.

Let’s say I have a custom navigation bar that reflects some general data (ie not just a title with the name of the current screen you’re at). Now lets imagine some scenario where when you change something in one of the leaves of your navigation tree it requires a change in another leaf, where the user has to input something. You can say, for example, that you have a weather app, and in one leaf you have the general area of interesting data (wind, hygrometry, whatever), and in the other the units you want to use. Changing from one to another requires a change of units. So, naturally, you want to pop the view controller and push the units one. Except now, you can’t do it too fast. You have to wait for each animation to complete before taking the next action. And you really shouldn’t try to change the title while the animation is running either. (yes I know you could set the navigation hierarchy directly, but my rebuttal is the same as the one given afterwards)

And I hear someone say “well it’s easy, you have the two delegate methods for the navigation controller, so just use it”. Yeaaaaaaaaaah. Weeeeeeeell… The nav controller delegate has to be known at a higher level than the views you will push and pop, right? So basically, the app delegate would be the nav delegate. So the app delegate has to know about the underlying structure and the navigation paths of the whole application? Sorry, I do object oriented programming, I have no intention of having all my view controllers as instance variables of my delegate with a huge switch every time there is pop animation to determine whether I should wait for it to finish or not.

What would be a good alternative then?

  • If I had a notification I could subscribe to, that’d be swell. And that’s what I implemented at the higher level. But it’s a hack.
  • There could be a lock, too. [UINavigationController waitForAnimationToEnd] for instance.
  • Or a bool telling me whether the nav controller is in transition or not.
  • Hell, even an exception would be better than just putting the app in an uncertain state. At the very least I could break on it to find which one of my thousands of lines should be scrutinized.

Going the multi-app, multi-threaded, asynchronous, route, is fine by me. But we have to have the tools to do it properly. Even viewDidAppear is not a guarantee that the animation is done and changing the title or pushing a new view controller on the stack won’t give us a nice Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.. What did I do wrong that was perfectly fine up until 3 weeks ago? No idea. No exception. No information. Just “hey don’t do that”. Totally Kafkaian as far as I’m concerned, since I just hit on the back button. But now, after this point, any and all navigation related code will crash. And I’ll spend a few hours or even days to figure out why.

[Networking]

For the same reasons, networking (low level networking mostly, but with stuff creeping up sometimes higher) has changed. I can have network connectivity, tested, working fine in Safari, and a socket that connects to nothing while not timing out. Why? How?

Because that, too, has been moved to a new asynchronous mechanism that offers very little help. My NSURLConnection might appear to be doing nothing, but be active enough so that the system as a whole doesn’t deem it a timeout. And since I have no way of peering into its current state, apart from waiting patiently at the delegate points, the net result is that the app looks as if it’s stuck. Which it is, but not UI stuck. Can’t get out of that mode any other way than kill/restart for now.

I wish I had dug enough to give you more information about it, on par with the above UIKit problem, but the truth is it’s been 2 weeks and I still haven’t had the time to figure it out properly. And I have very little hair left. And it’s all grey.

[Conclusion]

I am for all the new changes. I like them, think they make sense, and overall improve and expand the possibilities for us developers. But without insider information as to how the thing works under the hood, we have very little ways of tackling these kinds of bugs. And all of them feel… hacky.

By all means, change the APIs, improve them, etc etc, but also give us the tools to do our jobs properly. Everybody wins if the users are happy.

And I wish I didn’t have to kill Mail so often, which seems to be stuck on the same problems I have. I guess that’s a sign that I’m not alone in my struggle.

  

[CoreData] Honey, I Shrunk The Integers

Back in the blissful days of iOS4, the size you assigned to ints in you model was blissfully ignored: in the SQLite backend, there are only two sizes anyway – 32bits or 64bits. So, even if you had Integer16 fields in your model, they would be represented as Integer32 internally anyway.

Obviously, that’s a bug: the underlying way to store the data shouldn’t have any impact on the way you use your model. However, since using a Integer16 or an Integer32 in the model didn’t have any impact, a more insidious family of bugs was introduced. The “I don’t care what I said in the model, it obviously works” kind of bug.

Fast forward to iOS5. The mapping model (the class that acts as a converter between the underlying storage and the CoreData stack) now respects the sizes that were set in the model. And the insidious bugs emerge.

A bit of binary folklore for these people who believe an integer is an integer no matter what:

Data in a computer is stored in bits (0-1 value) grouped together in bytes (8 bits). A single byte can have 256 distinct values (usually [0 – 255] or [-128 – 127]). Then it’s power-of-two storage capacities: 2 bytes, 4 bytes, 8 bytes, etc…

Traditionally, 2 bytes is called a half-word, and 4 bytes is called a word. So you’ll know what it is if it seeps in the discourse somehow.

2 bytes can take 65636 values ([0 – 65 635] or [-32 768 – 32 767]), 4 bytes can go much higher ([0 – 4 294 967 295] or [-2 147 483 648 – 2 147 483 647]). If you were playing with computers in the mid-to-late nineties, you must have seen your graphic cards offering “256 colors” or “thousands of colors” or “millions of colors”. It came from the fact that one pixel was represented either in 8, 16 or 32 bits.

Now, the peculiar way bits work is that they are given a value modulo their maximum width. On one bit, this is given by the fact that:

  • 0 + 1 = 1
  • 1 + 1 = 0

It “loops” when it reaches the highest possible value, and goes to the lowest possible value. With an unsigned byte, 255 + 1 = 0, with a signed byte, 127 + 1 = -128. This looping thing is called modulo. That’s math. That’s fact. That’s cool.

Anyway, so, in the old days of iOS4, the CoreData stack could assign a value greater than the theoretical maximum for a field, and live peacefully with it. Not only that, but you could read it back from storage as well. You could, in effect, have Integer16 (see above for min/max values) that would believe as an Integer32 would (ditto).

Interestingly enough, since this caused no obvious concern to people writing applications out there, some applications working fine on iOS4 stopped working altogether on iOS5: if you try to read the value 365232 on an Integer16, you’d get 37552. If your value had any kind of meaning, it’s busted. The most common problem with this conversion thing is the fact that a lot of people love using IDs instead of relations. You load the page with ID x and not the n-th child page.

So, your code doesn’t work anymore. Shame. I had to fix such a thing earlier, and it’s not easy to come up with a decent solution, since I couldn’t change the model (migrating would copy the truncated values, and therefore the wrong ones, over), and I didn’t have access to, or luxury to rebuild, the data used to generate the SQLite database.

The gung-ho approach is actually rather easy: fetch the real value in the SQLite database. If your program used to work then the stored value is still good, right?

So, I migrated

NSPredicate *pred = [NSPredicate predicateWithFormat:@"id = %hu", [theID intValue]];
[fetchRequest setPredicate:pred];
NSError *error = nil;
matches = [ctx executeFetchRequest:fetchRequest error:&error];

to

NSPredicate *pred = [NSPredicate predicateWithFormat:@"id = %hu", [theID intValue]];
[fetchRequest setPredicate:pred];
NSError *error = nil;
matches = [ctx executeFetchRequest:fetchRequest error:&error];
 
// in case for some reason the value was stored improperly
if([matches count] == 0 && otherWayToID.length > 0) { // here, I also had the title of the object I'm looking for
  int realID = -1;
 
  NSString *dbPath = [[[[ctx.persistentStoreCoordinator persistentStores] objectAtIndex:0] URL] absoluteString];
  FMDatabase* db = [FMDatabase databaseWithPath: dbPath];
  if (![db open]) {
      NSLog(@"Could not open db.");
  }
 
  FMResultSet *rs = [db executeQuery:@"select * from ZOBJECTS where ZTITLE = ?", otherWayToID];
  while ([rs next]) {
      realID = [rs intForColumn:@"zid"];
  }
 
  [rs close];  
  [db close];
 
  if(realID >= 0) {
      pred = [NSPredicate predicateWithFormat:@"id = %u",[identifiant intValue]];
      [fetchRequest setPredicate:pred];
      error = nil;
      matches = [lapps.managedObjectContext executeFetchRequest:fetchRequest error:&error];
  }
}

In this code, I use Gus Mueller’s excellent FMDatabase / SQLite3 wrapper
Obviously, you have to adapt the table name (z<entity> with CoreData), the column name (z<field> with CoreData), and the type of the value (I went from unsigned Integer16 to unsigned Integer32 here)

Luckily for me (it’s still a bug though, I think), CoreData will accept the predicate with the full value, because it more or less just forwards it to the underlying storage mechanism.

Hope this helps someone else!

-nz

  

[Me Too!] Xcode 3 vs Xcode 4

Amidst the flurry of trolls and meaningful comments about the rather drastic changes from Xcode 3 to 4, I’d like to sum up a few of the major discussions out there, and add a couple of my own remarks

First things first: a word of caution

This is not and should not be about simply changing habits. Yes it sucks to have to re-learn something you’ve used daily for nearly a decade, especially if it means wasting time you need to earn a living. But in the business of making software, we convince potential customers of much much worse. So if your main gripe about Xcode 4 is “dude, I used to do this like that and they are forcing me to do it differently” but the end-result is the same, please abstain from commenting.

Xcode is the only way to make iPhone/iPad/Mac applications. Yeah yeah yeah, you can have makefiles instead, in the same way I can run my car with whiskey: it works, but it borks pretty much everything and might end up blowing the whole thing apart. Unless you are Apple and know exactly the side effects of your build process, you have to use Xcode.

And of course that’s partly why it stings. As mentioned previously here as well as elsewhere, making an app is more akin to art than science. No one would tell a painter: “make a portrait of me, using only a 4-colors ball pen”. Some restrictions are funny, but they can also be ridiculous. By forcing us developers to adapt to Xcode instead of working out how to adapt to us, Apple is being kind of condescending. They know better than us. But that too is not the object of this post, so just leave it aside as there’s no alternative, and that can’t be a meaningful debate.

The Cardinal Rule: You Shan’t Leave

Apple has put a lot of effort into this: if you’re doing it right (for a given value of right, obviously), everything is done within Xcode: version control, file creation, code/xib/coredata editing, debugging, archiving, publishing to iTunes. There’s only one exception to this: Instruments, for profiling your app, finding leaks etc. I wouldn’t be surprised if there was a technical reason for this, probably permission-related, but I bet they are doing their best to integrate it rapidly. A lot of IDEs work the same way, and it’s not necessarily a bad thing.

I just have a thing against mono-window interfaces, I like all my important files to be open in different editors so that I can switch back and forth rapidly. Putting everything in the same window suggests every file has the same importance, when I just know it’s not the case… And I’ll be damned if I understand the new counterpart system: I used to have a small box in the corner; when I clicked on it, it switched from the .h to the .m and vice versa. Now, I have keyboard shortcuts instead, and most of the time, I get it wrong.

The mono-window allows us to see all the projects we are working on. That’s pretty cool. I’ve been using workspaces for this for a while now, and it’s nice to integrate it into Xcode: my 3 projects for this customer are together and the 2 personal ones are in another workspace, etc. As a result, the file filter at the bottom becomes really useful: I don’t have to spotlight all my disk to find where in hell I’ve done that gimmicky thing again: since my projects are grouped by customer and/or kind and/or whatever criteria I want, on a day-to-day routine, it’s a lot easier to find what I’m looking for. Swell!

The mono-window has one major drawback though: I’m not always at home on my 27″ screen to write code. And my 13″ laptop screen is way too small to use Xcode comfortably. Editing text can be done, editing a xib with its relevant .h open on the side requires a lot of juggling.

And that brings us to the major side effect of putting everything in the same window: UI editing. Everyone I know agrees it’s the best thing since sliced bread. Good bye Interface Builder! You have your interface in one editor, the relevant .h/.m files in (an)other(s) and the famous ctrl drag (aka the right click drag) works directly on the code. You’ve put a view in there and you forgot to add an outlet? Ctrl drag the view on the .h, and Xcode offers to insert the outlet in the .h (optionally as a property) and puts the standard code for unloading it properly in the .m. You can obviously connect it to an existing outlet, but many-a-time have I forgotten to put it in the .h and now I am saved! Getting used to the sidebar inspector might take a little while, but in the end it’s a success. Unless, of course, I am on my laptop. Never being able to see the whole view you’re designing just sucks.

So, apart from the fact that I don’t like mono-window for personal reasons, it’s pretty much a success on that part. However, since I’m not supposed to leave Xcode for any reason, it would make a lot of sense to have a plugin mechanism. Yes, I’ve been saying that since revision 1.0 of Project Builder. Back in the day, I wanted to use the Metrowerks compiler, which I found vastly superior to GCC (and apparently I was right, since Apple pretty much switched to LLVM, not wholly abandoning GCC, but clearly saying it’s not the best compiler out there). It took some time to figure out a way to hack together an “xml plugin” to add it to the list of available compilers. Or maybe I have a set of data I will embed in the application that I’ve got a different editor for that I’d like to integrate. Or I just want to do geeky things. Any and all professional application should have a plugin mechanism, bar none. Xcode should not be the exception. Because pros have sometimes very specific things they need to do, we need to be able to tweak Xcode to grant us that power. Otherwise, we have to get out of Xcode to get the work done.

The Way Of The Developer: It’s All About Flow

The typical work cycle of a developer usually includes the following steps:

  • Pulling the last changes from the revision server
  • Making sure everything still works after the pull (that means running and/or debugging)
  • Editing code to fix bugs, add new features, or because the customer/boss wants-it-done-dammit
  • Running/Debugging
  • Testing for regression
  • Committing your (working, hopefully) changes to the revision server
  • Publishing the result: that includes sending a beta to relevant people, putting your application out there, …

The first step is getting a lot better in Xcode 4. I still can’t figure out why Xcode doesn’t offer me automatically to pull my SVN changes from the server, sometimes, but it kind of works now. I still like to use the terminal for that step, but at least I know I don’t have to force the people I work with to do the same.

I’ll talk about running and debugging a little further down. I promise.

The edition part, provided you have a big screen and have learned to navigate around the iTunes-y interface, is pretty much covered. Of course, I’d like a plugin mechanism to download crash reports and integrate ticket-management right into xcode, but hey… Maybe someday.

For me, the first gripe starts with running/debugging. First of all, I have to select the right scheme AND destination (Remote Tickets in the Simulator, as opposed to the device). This forces me to do some heavy duty gymnastics, especially when the bug I’m working on doesn’t have the same results on the different destinations (I’m looking at YOU iPhone). How many times have my trusty co-workers heard me yell obscenities because for the tenth time today I ran the program in the wrong environment? And they still jump from their seats to this day.
The side-effect is that depending on this root choice, some actions will disappear. I’m running the simulator, everything’s a-ok and I want to push an IPA? Tough luck, I have to switch to the device environment just for that. Tell me something Xcode: why in hell would I want to archive a simulator version? Therefore if I want to archive a binary, it will automatically be for the device. Therefore the menu should always be available as there’s no alternative!

Testing for regression is also a lot harder than it should be. For Pete’s sake, I have different versions of the simulator (one for every iOS device, running every supported OS version), and I have archives of my builds. Why can’t I keep them somewhere and run them for comparison without having to use the SVN/git/whatever branches? That forces me to have at least 2 versions of the same project open at any given time during my tests.

Committing still has its quirks, but all in all it works fine in Xcode. Finally.

And the last part is so ridiculously convoluted that it deserves a chapter of its own.

Publishing An App (aka Russian Roulette)

Let’s be serious for a second. The objective and benefits of publishing an application isn’t the same for the developers and for Apple. Therefore, there can be some goodwill on both sides, but, as developers we don’t work for them and they don’t have that many incentives to work for us either. Therefore, I’ll just go over that whole certificate thing very quickly.

Apple’s business model is that if you want to distribute an application, you do it through them. Period. There can be some leniency here and there, but that’s the bottomline. To ensure that, we are issued temporary permission to run our work on development devices (ours or our beta testers’), through the use of certificates. A “provisioning profile”, or a certificate in more trivial terms, is a combination of 3 permissions:

  • You are a certified developer (aka you have paid), therefore you have a right to run your work on devices
  • The device is declared in a list of devices authorized to run stuff from you
  • The stuff you are running is (at least partly, for identification purposes) declared in a list of stuff you are doing

This mechanism protects Apple from a black market of applications that wouldn’t go through them.

There are two major types of provisioning profiles: testing/development ones and distribution ones. Development profiles can be used to test your software on devices. Therefore, you are only authorized a limited amount of test devices. Distribution ones are unlimited in devices, and can be issued for the App Store (distribution by Apple) or for Enterprise distribution (a more expensive and legally binding contract).

In my work, there are 3 typical scenarios:

  • I work on a personal project that will be released to the general public (via the AppStore)
  • I work on a project for a company that will publish the result on the AppStore
  • I work on a project for in-house deployment

In Xcode, in the project settings, you set the provisioning profile you are going to use, and at the end of a successful build, the application is signed accordingly. Then you’ve got to figure out a way to deploy it.

In the first case, it gets easier with Xcode 4 than Xcode 3: Publishing an application to the AppStore is ridiculously easy nowadays. Just build and archive, Xcode makes sure all the minimum requirements (icon size and so on and so forth) are met, and pushes the new version of your application straight to Apple for evaluation. Since my own test devices are included in pretty much every profile I have on my computer, testing for myself is dead easy, I don’t even have to change the settings: any certificate will do. At distribution time, a popup asks me to confirm the distribution profile I want to use anyway, so why would I do anything about it? Congratulations on a job well done.

Where it gets hairier, is when you have beta testers. To test your software, they need to have given you the ID of their test devices, you need to have included them in a specific profile through a web interface that’s not the best website I’ve seen out there, and you need to make sure to sign your application with this profile and none other. Problem number one: every time you need to add a new beta tester to the list, you need to go through the whole process of creating a new certificate, identical in everything to the previous one but for the list of authorized devices. Then, in the project settings, you need to make sure you’ve selected the new one (which has the same name as the old one, and doesn’t replace it). This is currently one of the major sources of my daily cursing spree (see below). Once all the beta testing has taken place, you’re home free, the rest is a lot easier.

One step more in that direction: working for a different company. They have their own developer identity, on which you might have only limited access. In that scenario, they have to manage the list of test devices, and grant you the right through a delegation mechanism to sign the application through their account. Basically, nothing changes, except the fact you can’t manage anything anymore: when they update the certificates, profiles and the rest… well if they don’t tell you, you’re screwed. Let’s assume for a second they gave you access and are extremely good at communicating that sort of information (that’s a big assumption).

When it’s not your own project, the pressure changes focus: you are not in charge of the timetable anymore. This means that any error that delays the time they get the application installed on their test devices can be a source for major conflicts. And, obviously, if they called in a contract developer, it generally means that they either don’t have the expertise or they don’t want to. Distribution of beta builds, when it could be done via email for your own beta testers becomes a little less diplomatic.

As many of my fellow developers, I have set up a server that distributes my work for me (kind of like the AppStore, in less pretty): On the test device, they click on a link, and it installs the application automatically. When you have one or two projects, you can do this all manually, it’s no big deal. Editing plists, HTML, and making sure the right profile has been selected is not that complicated. When you have ten projects and generate one or two builds for two of these projects each day, it becomes troublesome very fast. Mistakes are made, panic ensues, grief follows not far behind, hence the daily cursing spree (told you).

In Xcode 3, it was rather easy (when you know how, obviously) to automate that process: build the application, sign it with the right profile and certificate, push it onto the server, and add the relevant info to the web page. It can even optionally send an email to the right people for you. I fiddled around with my own build scripts, and with Hudson.

These build scripts (whatever the flavor) need a few things in order to work:

  • access to the code (obviously)
  • understand the structure of how Xcode generates the application (especially where in relation to the source code)
  • access to the provisioning profiles (more tricky if the server is not a Mac OS X machine, and that leaves a huge security hole as the process needs to access files in your home directory. Oh and to Xcode’s internal shenanigans, a profile doesn’t have a name, it has a fricking 60+ characters identifier, another source of potential mayhem)
  • access to your keychain (for signing, even bigger security holes as the process either need to be you or to have your personal password)

Fiddling around for a reasonable amount of time will get you set up. Lo and behold, when I commit something to my SVN, the server grabs the source, checks it, compiles it, signs the app, pushes it onto a web server, and sends an email to my customer. Perfect.

Xcode 4 on the other hand has changed all that: from outside of Xcode, you can’t know where the build has been put. It’s in the preferences of the application itself, it has nothing to do with the project anymore. So there’s an extra layer you need to manually add to your projects: putting the build result in a location that is always the same. Basically, you need to emulate Xcode 3’s behavior in Xcode 4. That’s typically a “new feature” that forgets most developers out there don’t work for themselves. And I read here and there that I’m not alone pleading for a return to a more project oriented structure: I have currently on my disk close to 4GB of builds that I know nothing about. Xcode 4 decides to put a bunch of things here and there, and if I trash the project, or revert to an earlier version, or change its name, or whatever project management thing we do on a weekly basis, these files will stay there till the end of times (or till I find the time to guess which is which and trash what I don’t need).

Wrapping Up, Because It’s Long Enough Already

I like a lot of things in Xcode 4 (workspaces, better source control management, UI designing features, and a lot of small features that made me smile the first time I discovered them). Some choices made are time consuming for me (why in hell can’t I lookup a symbol in the documentation without copy/paste???) but not that big of a deal.

To me, the only thing that is really troublesome is that most of the changes between 3 and 4 were made to keep you inside of Xcode. Good bye external scripting, good bye standard automation (recreating the same scheme over and over and over again with just 2 variables changed isn’t the same as having ONE script that adapts to the project), good bye workarounds for certificate/profiles management… And there is still no way to add plugins to integrate into Xcode what I need to do every day.

Xcode 4 is meant for the solo developer that will publish directly to the AppStore, with maybe a couple of beta tests thrown in. Every other type of developer out there will have to find a way to make it work for themselves, on their own. And we will, because we are, after all, in the same trade. That extra effort is not something I really care for, but I accept it.

Feel free to comment.

  

iPad’s Initial Orientation Issues

Last week, I had to make an iPad application with 3 viewcontrollers’ views in the window.

It’s an unofficial convention that you should have only ONE view controller at the root level, and then all the other controllers should be descendants of this one. Then the orientation, transformations and the rest is passed along the view hierarchy through this root controller.

For a reason best left aside, I had to have 3 “root” view controllers, that would animate in and out, replace each other on occasion etc. I felt it would be easier to code and maintain having them be installed in the window at the same time.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
  [self.window addSubview:vc1.view];
  [self.window addSubview:vc2.view];
  [self.window addSubview:vc3.view];
 
  // ...
}

Now, the twist is that my application can only run in landscape mode (with the relevant information in the plist set).

First launch, the views are correctly created and put on screen, but 2 of them are displayed in portrait mode, while the first one is indeed in landscape. The xibs are all set in landscape, the sizes are correct, but the display is not.

After some digging around, I setup the shouldAutorotate delegate methods, and check upon display the orientation the view controllers think they are in.

Surprise, it does indeed work, the orientation is set correctly, and the views are resized to landscape mode. But still displayed with the wrong orientation. The UI is setup in portrait mode, then stretched in width and shrunk in height.

At this point in time, I guess I could have considered to put all my view controllers as descendant of the root (first) one, and let the rotation/stretching be handled by it. Or created a blank root one in which the other three would fit.

After some more digging, helped hugely by Dam, it turns out all the information and notifications but the transformation matrix are passed along all the root view controllers. Therefore it’s just a matter of forcing it. For those of you having the same problem, it’s as easy as:

vc2.view.transform = vc1.view.transform;
vc3.view.transform = vc1.view.transform;

Bug? Feature? Up to you to decide.

  

Of NSURL

While making RemoteTickets, I had to depend heavily on NSURL. For some reason, it didn’t work with Dam’s RT system.

After a couple hours of debugging, here’s the thing: NSURL doesn’t work as expected.

URLWithString:relativeToURL:
Creates and returns an NSURL object initialized with a base URL and a relative string.

+ (id)URLWithString:(NSString *)URLString relativeToURL:(NSURL *)baseURL

Parameters

* URLString
The string with which to initialize the NSURL object. May not be nil. Must conform to RFC 2396. URLString is interpreted relative to baseURL.

* baseURL
The base URL for the NSURL object.

Return Value
An NSURL object initialized with URLString and baseURL. If URLString was malformed, returns nil.

Discussion
This method expects URLString to contain any necessary percent escape codes.

Availability
Available in iOS 2.0 and later.

Seems to me it means I’m building an URL based on concatenation. Well, that’s not the case:

NSURL *baseURL = [NSURL URLWithString:@"http://www.apple.com/macosx"];
NSURL *compositeURL = [NSURL URLWithString:@"/lion" relativeToURL:baseURL];

should give http://www.apple.com/macosx/lion, right? It gives http://www.apple.com/lion instead. The baseURL parameter is actually taken as “the base URL to take the base from”.

The following code gives the result posted afterwards. Use with caution.

    NSURL *baseURL = [NSURL URLWithString:@"http://www.apple.com/macosx/"];
 
    NSURL *relativeURL = [NSURL URLWithString:@"/lion" relativeToURL:baseURL];
    NSURL *relativeURL2 = [NSURL URLWithString:[NSString stringWithFormat:@"%@/lion", [baseURL absoluteString]]];
 
    NSURL *relativeURLLvl211 = [NSURL URLWithString:@"/new" relativeToURL:relativeURL];
    NSURL *relativeURLLvl221 = [NSURL URLWithString:@"/new" relativeToURL:relativeURL2];
 
    NSURL *relativeURLLvl212 = [NSURL URLWithString:[NSString stringWithFormat:@"%@/new", [relativeURL absoluteString]]];
    NSURL *relativeURLLvl222 = [NSURL URLWithString:[NSString stringWithFormat:@"%@/new", [relativeURL2 absoluteString]]];
 
        NSLog(@"%@", [NSString stringWithFormat:@"baseURL:\n%@ (%@)\n\nLevel 1:\nRelative:\n%@ (%@)\nAbsolute:\n%@ (%@)\n\nLevel2:\nRelative/Relative:\n%@ (%@)\nRelative/Absolute:\n%@ (%@)\nAbsolute/Relative(a):\n%@ (%@)\nAbsolute/Absolute:\n%@ (%@)\n",
                     baseURL, [baseURL absoluteString],
                     relativeURL, [relativeURL absoluteString],
                     relativeURL2, [relativeURL2 absoluteString],
                     relativeURLLvl211, [relativeURLLvl211 absoluteString],
                     relativeURLLvl221, [relativeURLLvl221 absoluteString],
                     relativeURLLvl212, [relativeURLLvl212 absoluteString],
                     relativeURLLvl222, [relativeURLLvl222 absoluteString]]);


baseURL:
http://www.apple.com/macosx/ (http://www.apple.com/macosx/)

Level 1:
Relative:
/lion -- http://www.apple.com/macosx/ (http://www.apple.com/lion)
Absolute:
http://www.apple.com/macosx//lion (http://www.apple.com/macosx//lion)

Level2:
Relative/Relative:
/new -- http://www.apple.com/lion (http://www.apple.com/new)
Relative/Absolute:
/new -- http://www.apple.com/macosx//lion (http://www.apple.com/new)
Absolute/Relative(a):
http://www.apple.com/lion/new (http://www.apple.com/lion/new)
Absolute/Absolute:
http://www.apple.com/macosx//lion/new (http://www.apple.com/macosx//lion/new)

  

RemoteTickets

This project is not secret anymore…

For years now I have been using RT (from bestpractical) to track bugs/issues/ideas with my various projects. I like the simplicity of it, and I like the fact that it’s email- and web- editable. With the huge number of things I had to track, and their somewhat urgent nature, I used the mail to keep track of it, through folders and suchlike. But the sheer number of systems and issues made that quite difficult to follow.

My two current project both involve heavy web interaction, so I figured an iPhone front end for RT would not be that difficult to make. It was and it wasn’t, but I’m starting to get results.

Right now, the application is in its last stages of development, early stage of usable beta.

Features:

  • multiple instances tracking (servers or vhosts)
  • full support in reading the tickets (with attachments, links, etc) insofar as the iPhone can handle the file types
  • searching, sorting, categories and queues are easily accessible
  • partial edition support (adding a comment, changing status, queue, due date etc…)
  • new ticket creation
  • calendar view for due dates

Known bugs:
A few crashes due to CoreData not being thread safe
A few unknowable states in the sync engine

The application will be on the AppStore next month, hopefully.

Feel free to comment or ask questions here or via email!