cocoa-dom

cocoa bits I use, come across, like, hate, the whole shebang.

twitter | adn         rant-dom | TCM        github         rss | archive
July 5, 2014 at 8:30am
5 notes

Swift Library naming warts

This is just a collection of oddities / naming choices that I ran across while playing with Swift. This list isn’t exhaustive, nor do I have a good solution for all of them. It’s just an observational piece on things that produced a negative gut reaction.

  • the func keyword
    somehow that one really doesn’t sit well with me. I’d much more preferred it if the keyword would have called function. However It has been pointed out that other abbreviations are used as well in standard keywords. Most notably: enum, var
  • succ(), pred()
    The ForwardIndex and BidirectionalIndex use these methods to move forward and backward. I’d much rather have seen successor() and predecessor() there. Or nextIndex() and previousIndex() since these methods are tied to Indexes. Or succedingIndex() and precedingIndex(). Actually this hits a sore point in the current Swift Library quite on the mark: how general or specific should the naming of methods and global functions be? And how can this be as consistent as possible to give future code writers the right idea and direction to make these in a way they fit in the language? Something that is very established in Cocoa and imho needs to be established in Swift as soon as possible.
  • advance()
    This is used to move an index forward by a distance. So why not advanceIndex()? And also what is the inverse? I found no good one in the english language, but I’m foreign. There is no inverse because you are supposed to use advance with a negative distance to move backward. I like that the name includes the direction, so you know your moving forwards with a positive distance and backwards with a negative index. However, I don’t like the impetus of advance, suggesting to me going forward is the only way. 
    Interestingly enough it hits another point: mutability. Does advance() change the index given or not? This is not clear by the naming, but I think it should be.
  • join(a,b) and <Type>.join(b)
    These behave consistent but are confusing to me as hell. Compared to cocoa’s [NSArray componentsJoinedByString:] which is clear to me. So to join [“c”,”d”,”e”] with “-” as glue you have to do either join(“-“,[“c”,”d”,”e”]) or ”-“.join([“c”,”d”,”e”]). I think the more intuitive version would be something like [“c”,”d”,”e”].joinBy(“-“) - I also don’t get exactly why it is both a global function and a method. I would prefer the function to be defined as join(a, by: b) with the semantics reversed. Everything would be much clearer and visible to the first time reader at a glance. However, the swift library does not make use of named parameters on a function level.
  • Array.append(), Array.extend(), Array.filter(), Array.sort()
    This again hits my sore spot on mutablity. Which of these methods return a new, changed array, which do mutate the array? Without consulting the documentation or Xcode there isn’t a clear indication. It turns out sort() sorts in place while filter() returns a filtered array. We need a consistent way of expressing this in the naming.
  • contains(a,b)
    Does this test if a contains b, or if a is contained in b? You can’t be sure without looking at the signature. I would much rather prefer this to be something like does(a, contain: b). and for it’s brethren with the predicate doesAnyElementOf(a, satisfyPredicate: b). However, with trailing closures being able to lose the last parameter name and just putting the closure at the end that might look awkward. But I sure as hell don’t get why one should name basic functions in a short way, if there is a possible improvement in readability and less ambiguity by writing a longer signature.

February 5, 2014 at 4:31am
0 notes

Autolayout and Complexity

I recently dipped my toes into some autolayout on both iOS and OS X. Short term verdict: powerful, but too complex for daily use. And that’s an issue.

Remember the times of handling touches before there were UIGestureRecognizers? That is exactly the state autolayout is in. The underlying technology is great but it is in desperate need of a good abstraction layer on top of it that makes the standard use cases easy and maintainable. I’m looking forward to this year’s WWDC to see some of these aspects to be addressed.

November 20, 2013 at 7:17pm
3 notes

Blocks need considering

While I don’t agree with this post in most respects, it still alerted me to a crucial thing when using blocks: Be sure the Macros you use in them don’t reference self directly. And if they do and you need to be sure to not create a retain cycle, use either @weakify and @strongify from libextobjc or do shadow self yourself this way:

__weak __typeof__(self) weakSelf = self; 
// some api that takes blocks, when you give the block do 
^{ 
__strong __typeof__(weakSelf) self = weakSelf; }

So thanks for ranting so much to alert me to the fact that NSAssert() does reference self directly, and as such make the block retain self.

Update: ZAssert seems to be a reasonable replacement for NSAssert() in blocks which is mentioned in the comments of the Article.

November 12, 2013 at 5:38pm
0 notes

iOS 7 Game Center Sandbox

Great advice and bug report from Patrick McCarron. Ran into it several times. Disabled Game Center for debug builds because of that (and because of the annoyingly slow login dialog when switching simulators).

October 19, 2013 at 11:33am
2 notes

objc.io

If you haven’t seen it yet, you have to check it out. objc.io offers fantastic in depth articles about all things objc. 

Update: An iOS newsstand subscription is now avaiable - I highly recommend supporting this publication by purchasing it!

July 26, 2013 at 10:19am
48 notes

New thing I do in code

Ever since I found out that a GCC C extension causes a code block to return a value if you enclose it in round brackets, I’ve been using it in my code. What do you think?

self.bounds = ({
	CGRect bounds = self.bounds;
	bounds.size.height = self.currentYPosition + SHEETINSETY;
	bounds;
});

I’m also using this for frame. The advantage is that with this construct I never forget to set the frame after altering it, which I did far too often otherwise.

self.helpButton = ({	// helpbutton
  UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  [button setImage:[UIImage imageNamed:@"ParentalControlQuestionMarkButton"] 
          forState:UIControlStateNormal];
  CGRect buttonRect = innerBounds;
  buttonRect.size = [button sizeThatFits:CGSizeMake(400, 400)];
  buttonRect.origin.x = CGRectGetMaxX(innerBounds)-CGRectGetWidth(buttonRect);
  button.frame = UIEdgeInsetsInsetRect(buttonRect, UIEdgeInsetsMake(0, -10, -20, -10));
  [button addTarget:self action:@selector(helpAction:) forControlEvents:UIControlEventTouchUpInside];
  [self addSubview:button];
  button;
});

The major benefits of this one are:

  • the instance variable in which I will store the generated object is in the first line, clearly showing what the next part of the code does. Prior to this, the assignment happend at the end.
  • the stack variables declared and used don’t pollute other code in the same function/method. I can feel free to use very generic names (view,frame,rect,button) and not get into conflict with other parts.

Update: And it all works with CLANG due to the great design policy of CLANG to support most of the GCC extensions to maximise compatibility. 

January 30, 2013 at 4:32am
1 note

NSHipster

If you have anything to do with Cocoa: Go ahead and go to NSHipster now, if you haven’t done so. I can’t stress enough how good this site is in revealing great Stuff about Objective-C and the Cocoa and UIKit frameworks. Just so well done.

September 4, 2012 at 4:00pm
1 note

Turn Based Gaming - Game Center Style Part #1

Some of you may have noticed that we released Lost Cities in the end of August. Apart from being our first card game (we did Carcassonne prior to that), it is also our first time to use the Turn Based API that Game Center provides starting with iOS 5.0. This sadly has proven a very challenging thing to do, and I want to part with some of the major gripes we encountered in this multipart series.

Game Center’s idea of Push Notifications

Push notifications are something iOS users are pretty familiar with now. They also made the hop to the desktop in Mountain Lion. Every app can send push notifications to the user in appropriate moments, if the user gives their permission. Users can even customize them on a per app basis and set if they want them to appear as alerts, banners and/or in the lock screen, if they have sound or not or they should badge the App Icon.

All right, these are nice and understandable, already a little bit complicated – but okay. And if you are using the Game Center Turn Based API you get push notifications too. Isn’t that nice? Or so it seems. The Game Center Push Notifications sadly have some strangeness to them:

  • They aren’t yours – meaning they do not belong to the app sending it. They appear with the Game Center Icon and your App’s Name. The push notifications appear in Quotes to discern them from other Game Center Notifications. If you can search the web for undocumented features you can change the built in fanfare to your app’s sound. That’s it. (rdar://10177685)

  • They are all or nothing – your users can’t set the notification settings for your app, they have to set the one’s for game center. You read right. You can’t turn them on or off for specific games, or even discern them from the Game Center’s friend requests. You want a Sound for this game notification? Then you also have to live with the friend request fanfare. You have a game you play only on your iPhone and not on your iPad? Too bad for you, can’t tell the iPad not to show the notifications. (rdar://10177685)

  • Invitations to games appear during your game - yes that too. If you are invited to a new game, while already in that game’s app, you get the invite notification banner shown inside your app. Also: there is no sane way of showing the invite text to you other than show in notification center, as we the app devs don’t have access to it. We don’t even get a notification.

  • No extra payload and early access for you - with normal push notifications you, as an app, can add a little bit of extra payload to that notification which you get instantly when your app is moved into foreground or started. This way you can prepare the correct UI for the Notification the user swiped upon while you sneakily load the data you need in addition that in the background to give the fastes experience. Not so with Game Center. With Game Center you aren’t told at all that the user swiped on a notification to start your app. The earliest you can guess that the user swiped a notification is in the turn based delegate callback, which only fires after the Game Center Greeting Banner (you know the one) is shown in your app, which can be up to 30 seconds in bad networking conditions. And which also fires if you start the app and a turn is received during that time. So we more or less have to guess if the user swiped a notification instead of knowing. (rdar://10177254)

  • They aren’t localizable – yes you read right – there is no sane way of localizing the Notifications. There isn’t even a proper way to set the notification text directly. (rdar://9581651)

    You can either chose to not set a custom text in which case the recipient(s) of the push notification get a message of “Your Turn” localized in their iOS Language. But then you have no additional information about the game in progress, no opponents, no turn order, no score, no whatsoever.

    Or the participant that made the last move may set a text to be sent instead. This is the text that will be displayed in quotes in the notification. E.g. we chose to set “20 cards against Monkeydom”. This is the Status text of the game. Which poses a problem if you use the standard Game Center Turn based UI, as it appears in that list as status of the user that made the turn. So if you are using that you already have the issue of a strange display.

    However if you want to go the extra mile and localize the Message then the only thing you can do is the following: The participant app that makes a move needs to anticipate the recipient of the push notification (e.g. the next player), localize the notification in their language, and set the text and make the move. This needs knowledge about the locale of the recipient, which in turn you have to transport in the game data. We chose that approach, but it was a serious pain.

Although I’m sure some of the issues are adressed in iOS 6, I already know of some that aren’t. Which in turn means one more year until we can hope for improvements again.

August 1, 2012 at 9:16pm
0 notes

One of the reasons that you may want to upgrade to Xcode 4.4 is that it includes Apple LLVM compiler 4.0 that adds a number of new Objective-C Language features. Even better most of the new features are backwardly compatibile with older versions of iOS. To make is easier to understand which features are available for which tool and OS releases Apple has published a useful Objective-C Feature Availability Index.

— Great summary on useyourloaf.com about Automatic Property Synthesis With Xcode 4.4

July 5, 2012 at 3:05am
1 note

iPad Mini

Just to go on the record: If and when an iPad mini comes I predict it will have the 1024x768 @1x resolution cramped into that smaller display area. It will have slightly higher DPI than the original iPad/iPhone, but it won’t be Retina. All iPad apps will just work. All iPad apps that disregarded Human Interface Guidlines and made their buttons too small will have issues. All others will be a little bit more intricate to handle than on the iPad/iPad2, but be fine overall.

April 17, 2012 at 11:31am
0 notes

Games, iCloud and Game Center

There is a dilemma Game Developers face on iOS in terms of synced game state across multiple devices. This also applies to games that you deleted from the device once and want to continue playing some time in the future.

Apple’s recommended solution is to use the iCloud. However the iCloud storage is not tied to your Game Center account. This causes some major problems:

  • somebody logged into Game Center on a device that does not have his iCloud account simply doesn’t get his/her save games / game state (little known fact: you can log into the game center on any device using your account)
  • since the iCloud account and the Game Center account are totally independet, there is no way of telling the user about this fact gracefully, so you end up just creating a new game state for this game center id on this iCloud storage, everyone is confused
  • you essentiallly have to design your app around the fact that the user might have an iCloud account or not, and might have a Game Center account and not, and any combination of iCloud account and Game Center account, and work fine and predictable in every case. This adds very uneccessary complexity to quite a simple problem: taking care of the users progress in the best possible way.

However, there would be a simple solution to this dilemma: make Game Center provide storage that is tied to the Game Center account. That is the natural fit. Users would get what they expect. If you think so too, please file a duplicate of rdar://11263793 so we get this rather sooner than later.

Sadly, this is just one of the little details Apple did not figure out right with Game Center.

November 7, 2011 at 3:38am
0 notes

TCMMyClassWithSingleton.sharedInstance

PSA: I switched to dot syntax for Singletons now. Seems fitting, looks less cluttered. I took some time getting used to it, but now I really like it.

[NSUserDefaults.standardUserDefaults boolForKey:@"SomeKey"];
[NSNotificationCenter.defaultCenter postNotificationName…];

July 25, 2011 at 9:41am
1 note

Network Testing

If you put in network code into your apps, it is always important to test against bad network conditions. While this isn’t too trivial, it was always possible using dummynet.

However, now new in Lion is a Preference pane to use, so you don’t have any excuses anymore for not testing: Network Link Conditioner - Use it and use it heavily - real life network conditions, especially on iOS, are never as good as in your pristine office big bad ass network location.

May 30, 2011 at 7:25pm
0 notes

Could the Mac become a Post-PC device?

Today I did a little mind experiment thinking about if an ARM based MacBook Air would be viable, and what would be the pros and cons.

Pros:

  • Battery life
  • Return to in house chip development
  • iOS and Mac running on the same architectures
  • Probably a good long term bet, as ARM is here to stay and winning the performance per watt battle

Cons:

  • No Bootcamp, parallels or the such
  • No Rosetta-like intel emulation for existing apps
  • Probably less performant, although with enough Cores going on, probably not that big of an issue.

However the more interesting point to me is that Apple is here to innovate. Mac OS X has kind of seen its time and iOS in itself is a good companion, but not a replacement. If they really want to change the way the standard computer works then a mandatory transition to another hardware platform could be the way.

How could such a future look like:

  • it would be one big step further in the direction Lion is taking. But since backwards compatibility is not an issue there, as developers would have to recompile and adjust to the hardware anyways, Apple could cut with old metaphors and really innovate. E.g. away with the standard file system. Away with the unix shell. Away with the Menubar. Keep technologies and infrastructure, but only expose the Developers to the new environment.
  • redo AppKit from ground up, gone with the slapping on of technologies adapted from the iOS development, a real first class DesktopUIKit, give it the same love that the rewriting did for iOS, perfectly adapted to the new and improved interface metaphors.
  • the platform would be closed in the same way that iOS is closed. Apps only via the App Store, nothing else. There is a clear incentive for Apple to do this, 30% of every sale made.
  • the platform would include full support of iOS and iPad Apps out of the box. E.g. the platform would be comprised of a multiple of the iOS/iPad resolutions so multiple iOS apps could be used at once
  • great integration of multitouch, both via the trackpad and also directly on the screen. 

In essence Apple would create a third platform between iOS and Mac and use it as an opportunity to implement major changes in the user interaction model. It could leverage the existing Developer base in the same way, or even more so than they did with iOS.

The major flaw in that theory: This third platform really needs a good incentive for users to jump on. Something as “magical” as the iPad had. And I have no clue what that might be, but hey, that’s just me, I also thought that Apple needed something special for an iPad to make sense. And all they needed was a great execution of the basic features, reduced to the max to chime in the Post-PC era. The next step: to make the Mac make the jump into the Post-PC era as well.

So these are my 2 cents, here to stay to be laughed at in the future ;)

May 25, 2011 at 12:08pm
6 notes

0x8badf00d, Core Data and Migration

Let’s begin with the basics. In iOS there is a watchdog process - a watchdog that checks if an App takes more than about 20 seconds to start without getting back to the OS it will be killed. In the crash reports this killing is indicated by an exception code of 0x8badf00d - Ate Bad Food. Getting back to the OS more or less means that your delegate call of  application:didFinishLaunchingWithOptions: needs to return control to the runloop in time. If you don’t: you are killed. The user experience of this is that your Default.png is shown for about 20 seconds, and then the iPhone/iPad returns to the home screen.

Fun Fact: On iOS Devices that support multitasking the watchdog can be confused by pressing home and go back to the app again in intervals slightly less than 20 seconds. So if an App is showing this behavior, you might get it to work again using this stunt.

This is all good and well, the user should not have to wait for more than 20 seconds for an App to actually do something that the user can see or interact with. This is where Core Data comes in.

Core Data is a great framework that provides developers an abstraction to a local database. You do so by specifying a data model in Xcode, and provide classes for your database objects. In code you usually initialize your Core Data infrastructure very early on, because you rely on the data in the database for almost everything your app does. Usually that step takes little time - if the app is new you maybe want to copy an existing database as a first step, otherwise it just opens up the database files and returns.

However, if you want to store additional information in your database, you need to update your model. And if you do so, you need to migrate your data once to accommodate the new model. This happens in that early on initialization step. As migration goes there are two kinds of migration: Lightweight migration and manual migration. Lightweight migration is fast and can be done by just issuing SQL statements to the underlying database. Manual migration causes every object to be loaded and be updated. If you can achieve your changes with lightweight migration, especially on iOS devices you should do so. However, sometimes it is necessary to do some manual work in the migration process.

This is where the shit can hit the fan: Consider you are initializing Core Data before the end of  application:didFinishLaunchingWithOptions: because you want to use some of the existing data to prepare badges and UI state in the first viewWillAppear: methods of your top view controllers. Consider further that you are making a major update to your App and the database needs a manual migration. Now what easily can happen is that the watchdog literally bites your app in the ass.

And even better, during development you probably won’t notice. Why? Because of many reasons, I try to list many of them:

  • The watchdog is inactive if you are running in the debugger. Your App Start may take a while, but your app will always start up.
  • The migration step only happens once, unless your specifically testing for it over and over again. And it is most likely that that step is done while you are running in the debugger to check if everything goes right.
  • The data you need to test needs to be big. In most test and development scenarios you have realistic data, with a wide variety of items being in there to catch most cases, but you won’t do very big data because that slows down overall roundtrip time.
  • Your test devices most probably will be the fastest you have most of the time, again for roundtrip reasons. And faster devices migrate faster. Probably fast enough to not show the problem.

When you look at the customers that will have that problem out there you see another problem: They are probably your best customers. If they store a lot of data in your app, they use it a lot. If there is a lot of data, migration takes long. Tada! Horrible.

So now that we have established that this problem is a real turdball coming your way, how do you prevent/fix it?

Solution #1: do lightweight migration. If you can do that. This will be fast enough to not call the wrath of the Watchdog.

Solution #2: if you can’t do lightweight migration, make sure your migration step won’t be caught by the watchdog. E.g. your initialization of Core Data needs to be done after the app has returned from the application:didFinishLaunchingWithOptions: callback. A really good way of testing this is adding a simple sleep(25) in the code that actually creates your persistentStoreCoordinator. That way without having all test data, migration always takes long enough to trigger the watchdog. And again be sure to not attach the debugger, or the watchdog won’t bite.

Lessons learned:

  • Beware of the watchdog.
  • Core Data is great, but migration can really hit you quite unexpectedly.
  • If you use Core Data - put in that sleep(25) call now and test if you get bitten. If so fix it so it won’t bite you unprepared in the future when you update your model.
  • Prepare feedback for user migration. As it turns out users don’t make a difference between an app that appears frozen for a long period of time and an app that actually crashes. Even if migration will only happen once for each major model update, that moment can be crucial.