Thursday, January 08, 2009

iTimeZone 1.2 Feature Complete

iTimeZone 1.1 was released on Tuesday October 21, 2008. Work was started quickly on iTimeZone 1.2 with the intention of turning it around very quickly. The only planned feature for 1.2 was Change Top City. A couple of software updates to 1.1 (1.1.1 and 1.1.2) slowed down 1.2 a little, but the plan really came apart due to completely unforeseen events. Here's what's changed:

Change Top City
When iTimeZone 1.0 was designed, the intention was never for the top city (the one that the time wheels show the date and time for) to change in the app. If the user wanted to change the top city, they would change the Date & Time city in the iPhone/iPod touch Settings app and iTimeZone would match it. This will be talked about more in an upcoming iTimeZone 1.1 post-mortem, but during 1.1 development it became obvious through internal usage and user feedback (thanks all) that changing the top city had to be in the app.

In iTimeZone 1.2 users can double-tap any city in the list to make it the top city, but there's a catch. The time wheels (which is a UIDatePicker control) don't always calculate correctly the date and time after changing the timeZone property, which iTimeZone has to do when changing the top city. This is one of the unforeseen events I mentioned,and a bug has been filed with Apple (#6452008). Hopefully Apple fixes this issue in a near-term iPhone OS release, or I might have to investigate alternative ways to implement the time wheels. In the meantime, I have chosen to handle this condition by alerting the user once per iTimeZone session when the top city changes about the issue as shown in this screenshot:

 

UI Tweaks

 1.11.2Changes in 1.2
Main with Cities
  • Country Flags. A popular request, makes it easier to quickly ID a city.
  • Bold Fonts in the City List. Makes it all easier to read and pop.
  • Weekday on City Date. Whenever a date is shown in the city list, it now had the short day of the week
  • Shrunk the city cell height. Made it just a tad smaller so the top line of the fourth row is visible.
  • Faster Scrolling. Doing all drawing manually instead of letting controls lay themselves out on screen.
Add City
  • Back Navigation. Removed the city's time zone so that the screen title was better positioned
  • Automatically put focus in the search box and loaded the keyboard
Add City - Search Results
  • See Full State/Region and Country. Each cell has two lines, state/region and country can be seen without abbreviation, while emphasizing city name
  • Country Flag. Top aligned with the city name
  • Faster Scrolling. All drawing done manually instead of laying controls out by themselves
Main Edit
  • Custom Drawing in Edit. Removing the day/night icon to focus user's attention on the delete and move icons.
City Detail
  • State/Region Added. For completeness
Settings are in the Settings App
  • Always Show Dates. If you don't like seeing Today, Tomorrow, Yesterday when the top city's date is today's date, then this is for you. The default as with past releases is OFF.

What's Next
iTimeZone 1.2 has been testing and is in App Store review, which hopefully won't take too long.

Monday, January 05, 2009

UITableView's numberOfRowsInSection behavior change from 2.0 to 2.2

Decided with iTimeZone 1.2 to upgrade and build against iPhone SDK 2.2. It seems like UIDatePicker is just *slightly* more reliable when configuring its timeZone property after it had been initialized. However, this resulted in an unintended side effect of The app crashing when removing all rows in the main city list table. It took me a couple hours to understand what was going on, but if you see the following error in console:

*** Assertion failure in -[CityTableView _endCellAnimations], /SourceCache/UIKit/UIKit-747.36.52/UITableView.m:679
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update must be equal to the number of rows contained in that section before the update, plus or minus the number of rows added or removed from that section.'

The cause, at least for me, is a mismatch in the expected return value of tableView:numberOfRowsInSection: that your code (most likely a UIViewController or UITableViewController instance) implements as part of the UITableViewDataSource protocol.

If your app is built against iPhone SDK 2.0 or 2.1, the correct behavior is to return 1 in tableView:numberOfRowsInSection: when you have no actual content and provide an empty cell in tableView:cellForRowAtIndexPath:. This does not cause tableView:deleteRowsAtIndexPaths to throw an exception when removing the last row.

If your app is built against iPhone SDK 2.2, the correct behavior is to return 0 in tableView:numberOfRowsInSection: when you have no actual content. tableView:cellForRowAtIndexPath: is never called. When your code calls tableView:deleteRowsAtIndexPaths as part of a row delete operation, everything works fine instead of crashing. An alternative correct behavior, if a UITableCell does something special (e.g. add a row, load more rows) is to always add 1 to the actual count of the items in the, most likely, NSMutableArray and return that in tableView:numberOfRowsInSection:, taken correct conditional action in tableView:cellForRowAtIndexPath: for putting your special cell at the top or bottom of the UITableView.

Hopefully you have come upon this article or figured this out on your own before submitting the latest release of your app to the App Store and got rejected for having a crasher in a main use case, just like, uh, someone I know did because no testers found the issue and you didn't rerun all use cases yourself after building against a new SDK version ;-)

Thursday, December 18, 2008

Tip: Install SmartSleep for OS X

Brent Simmons, maker of the excellent NewNewsWire, linked to Mike D's Virtualization Blog for manually configured how Intel Macs go to sleep, specifically changed the default (suspend to RAM + hibernate to disk) to the old PowerPC based Macs style (suspend to RAM), aka Fast Sleep. Brent then linked to SmartSleep, which is a preference pane that puts a GUI around configured the sleep options that were previously only available in Terminal, and implements SmartSleep. This setting configures OS X to use suspend to RAM when the battery has a strong charge, and turns on hibernate when it drops below a threshold. Sweet!

If you are positive you don't want to use hibernate, you can disable it and then get some disk space back (file size matches much RAM you have installed back).

  1. Close System Preferences
  2. Open Terminal
  3. Type cd /var/vm
  4. Type sudo rm sleepimage

Thursday, December 11, 2008

Friday, November 28, 2008

iTimeZone is on sale for Black Friday

iTimeZone has been discounted to $0.99 to commemorate Black Friday in the US in the App Store in iTunes. Extremely curious if I can "make it up in volume", the 50% reduction in sales price and revenue.

Tuesday, November 11, 2008

iTimeZone on iTunes App Store Front Page

As I usually do, I logon to to the iTunes Store on Tuesday and check to see what new content is available. I happened to do this last night after midnight right before I went to bed. Imagine my utter shock to see iTimeZone in the NEW section on top of the App Store home page. Here is a screenshot for posterity's sake:


click for larger view

Real estate is even more precious on the App Store app on the iPhone and iPod touch, but iTimeZone also managed to make it to the top of the list. Here are two images, one the default loaded, the the second slightly scrolled to show the iTimeZone row completely:

Why Now, What's Changed?
I have no idea why iTimeZone has been granted pretty prominent placement in the NEW section on the App Store home page now. iTimeZone 1.0 was released when the App Store opened, iTimeZone 1.1 was released on October 21, 2008, and iTimeZone 1.1.1 (small bug fixes) was released on October 30, 2008. I have not been contacted by Apple, but if anyone from Apple is reading this and you can comment, I would love to hear why (dave dot murdock at mac dot com). It is entirely possible this is an accident, but I think that unlikely. Each of those tiles on the any of the iTunes Store home pages (Movies, TV Shows, App Store) is premium real estate and clearly under editorial control. I think iTimeZone's promotional position on the App Store will last a week and I am hopeful it will accelerate sales. Thanks Apple!

Friday, October 24, 2008

Using the built-in OS X Grammar Checker with Mail.app

I am no spelling whiz or grammar genius, so I rely heavily on software tools like Microsoft Word and OS X's built-in system wide spelling and grammar checking. Word I believe was the first to implement spelling and grammar checking as you type, for me it was a huge innovation. OS X's built-in spell check nearly anywhere you type text, especially on the Web in Safari, was one of those extremely nice surprises coming from Windows years ago. It is one of those "why isn't this in Windows" features.

Mac OS X 10.5 Leopard added English Grammar checking as you type. When I saw this on the Leopard Feature List (pictured left), I was extremely excited about having a system wide grammar checker. I was really looking forward to having this functionality in Mail.app, problem was, I am embarrassed to admit I couldn't figure out how to use it. But I figured it out now, here's what I am talking about.

Turn it on
Grammar check is supposed to be on by default as you type, but it seems to depend on the app. If you want to make sure it's on, you have to right click in a box you enter text, then click on Spelling and Grammar, then finally make sure Check Grammar With Spelling is checked.

Make Mistakes, Get Suggestions
Here is a sample sentence that the OS X Grammar checker flags:

I had always expected that you should right-click on any of the words with the green underline to see grammar suggestions, just like in Microsoft Word. In Mail.app, this isn't what happens. When you right-click in Mail, you see the menu on the left without suggestions. When Leopard first shipped, I thought this surely must be just a bug and kinda forgot about it. Then today something unexpected happened. I hovered over the grammar text by accident, which you then get to see a tooltip like this:

So you can use the tooltip, but I also found that their is a floating Spelling and Grammar tool window:

But What If Mail.app Is Broken in 10.5.0 - 10.5.5
Seeing the tooltip I thought Mail.app's behavior must be by design, but after playing around some more, I don't think that is the case. I use MarsEdit to write my blog posts, and since it's a Mac OS X app, the place where I type my content is the same OS X text area that does automatic spell checking. I didn't remember ever seeing grammar checking while typing, so I went and turned that on and typed my sample sentence:
Bob said better to be feared than respected, with regards to investing in stocks.
Sure enough, with regards to was highlighted as having faulty grammar. I right-click, and there are suggestions on the right-click menu as pictured on the left! Tough to draw a conclusion, Mail could intentionally (by design) or unintentionally (bug) not be showing grammar suggestions on the right-click menu. I filed a bug on it, #6319745 in RadarWeb (and I hope Radar, but I don't have access to the actual app).

iTimeZone 1.1 Released to the App Store

On Tuesday October 21, 2008, iTimeZone 1.1 was released to iTunes App Store. This was actually a shock, since I had been reading from other iPhone developers that approval times where somewhere between 1-2 weeks. iTimeZone 1.1 was approved in 33 hours! This was such a surprise, that I hadn't done anything on the Tangerine Element site to document the release. I was scrambling around on Wednesday in my spare time to get it done, and it took me a bit of discovery to use the Apple approved App Store logo (seen left) instead of the App Store iPhone icon I was using since launch.

As for what new or different in iTimeZone 1.1, you can visit the product page or other posts on the blog:

Thursday, October 16, 2008

Video game saving is broken, the content is the gamers, and how to fix it

Save Game Info I have never designed a video game, but I have been playing games for a long time. Being a software developer, I have some strong opinions on software design, and game design in particular lately. Specifically, I think the way saving works in games has to fundamentally change. Developers have to rethink how their save systems work, and consumers deserve clear upfront information about how they can use any games' save system. Why is this important? Because knowing upfront the information about the save system can directly affect whether you enjoy the game, whether you can finish it, and whether you can access all the content. The handful of recent titles that drove me to think this through were Metroid Prime 3: Corruption on the Wii, Rock Band 2 on the Xbox 360, Half-Life 2 on the Xbox 360, and Star Wars: The Force Unleashed on the Wii.

What is a saved game?
Wikipedia defines a saved game as:

A saved game is a piece of digitally stored information about the progress of a player in a computer or video game. This saved game can be reloaded later, so the player can continue where he or she had stopped. Players usually save games to prevent the loss of progress in the game (as might happen after a game over unless the game features permadeath, in which the save file is permanently deleted), especially when interrupting or ending a game session.

While that is a perfectly serviceable definition, a saved game is really a piece of the gamers' life. A saved game then allows the gamer to retain a portion of their life that they have chosen to share with you game designer by proxy through the game. Notice I didn't say "your game" to game designers. That's because once you release the product to the public, it is now a shared creation, because the game is nothing without an audience. But a saved game is also more than that, it is the gamer's key to the content which they have already bought! Video games have been using digital rights management (DRM) for years, and no one has been talking about it. Not only to protect a game disc from being copyable, but nearly all games disallow access to all their content unless the gamer has played through the game nearly exactly as the game designer said the gamer should. If the game doesn't allow gamers to save their progress at will and provide access to all content once bought, the game is broken because you game designer aren't valuing the gamers' time, and thus their life. Sorry if that sounds too dramatic, and I know it's only entertainment, but I can't think of another way to spin it.

A word about the kind of games
I am really only concerned with home video games where the gamer is involved with either a single-player or co-op experience. Traditional multiplayer or MMOs aren't addressed with this post because as a shared public experience, there is intentionally and intrinsically, the decision by each individual gamer to participate in that kind of experience. That said, I do over one piece of advice in the solution section for MMOs.

Time is all we have
Time is the most precious commodity any person has. When you engage with any form of entertainment, you want to know if it's worth your time. To make that judgement, you seek out reviews and opinions from people you trust. If you decide that something is worth your time, video games, as far as I can tell, are the only popular mass entertainment form where the consumer isn't in control of the content and the time required to finish the product is open ended. There is no information at the time of purchase, not even an estimate, about how much time it's going to take to finish the game. Movies have running times and the viewer can skip chapters or fast forward and rewind. Songs have length, fast forward and rewind. Books have pages that the reader can randomly turn to. Even sports have a match time limit or set number of points. Concerts have expected length. Games, nothing. You literally have no idea what you are getting yourself into. How can we be 30+ years into home video game systems and still not have such a basic piece of information, duration, easily communicated to consumers? How can gamers still not be allowed to choose what parts of the game to play or not?

What's wrong with save game design?
Simply, the gamer is not in control of the experience. It seems game designers have gone to great lengths to not allow users to have control over the game since there are actually numerous different kinds of save systems employed in modern video games. I am not presuming this is an exhaustive list, but in my experience:

  • Save Anytime. User can choose to save whenever they want, and that exact moment in the game is exactly where they return if they load that save game. There are actually a couple sub-variants on this.
    • Limited to a fixed number. Either through design or technical reasons (i.e. available memory)
    • Unlimited. User can save as much as they like up to the limits of storage
  • Checkpoints. At fixed points in the game, user progress is automatically saved. There are also sub-variants on this kind of save game system.
    • Checkpoints can only be returned to within a level. Once you hit a checkpoint, if the character dies between checkpoints, you return to the previously saved checkpoint. If you turn off the game, you return to the beginning of the level unless the user can also choose to save wherever they want. Another anti-feature to this system is that the current checkpoint replaces the last checkpoint. This is bad because if something is broken in your current checkpoint (e.g. You are stuck on the side of a cliff you can't climb *cough* Halo 3 Highway level) then you can't step back a checkpoint, you have to restart the whole level.
    • Checkpoints can be returned to on game load. Nicer, since intra-game sessions are not snapped to level.
  • Saving embedded in the game world. This is perhaps the worst save system because the game designer doles out the required resource with extreme scarcity. Worse, it makes no sense that a save mechanic is embedded in the game. It would be like you could only pause a movie at certain scenes in movies on DVD, which of course sounds insane.
    • Tokens. If you have ever played a Resident Evil game with its typewriter ribbons, you know what I am talking about.
    • In Game Save Stations. In Metroid Prime 3: Corruption the only chance for a user to save is at certain places in the game (sorry, can't remember the name)
Why are these save systems, excluding Save Anytime - Unlimited, in use today? Here are the reasons I have come up with:
  • Lack of confidence. I can't help but think game designers, perhaps due to the immaturity of the form, in someway fear that players won't want to play their game unless they are in some way artificially induced to progress.
  • Inability to save as a dramatic device. Game designers rely on the inability of the gamer to save progress to create tension. This is bad idea. The tension should come from the story.
  • Game challenge tied to limited save availability. I can hear the argument coming. If you give gamers the ability to save anywhere, what's left of the game? It is way past time to do away with this outdated thinking. For example, in Star Wars: The Force Unleashed, if I could save anywhere, it certainly wouldn't have prevented me from playing through the game. About the only thing it would have done is cut down my artificially inflated play time by about 15 minutes.
  • Masochism. I don't know if gamers or game designers, or both, have enjoyed this abusive relationship, perhaps so, but I for one have had enough. I have stopped playing games that don't respect my time. The reason why Half-Life 2 triggered my thoughts on this is because it's one of the better ones in recent memory allowing you to save whenever. It automatically saves at certain times, but you are limited to a certain number of chosen saves. Also, you don't really get enough information about the save on the load screen.

Solution: Saving is entirely at user discretion and should be a platform feature
This is the way saves should work:

  • Gamers should be able to save anytime, anywhere, as many times as they want. Game designers can continue to structure their games as a series of challenges, but it is up to the gamer to decide if they want to play through the challenges as designed.
  • Saving can be disabled during challenges, but they can't last longer than 15 minutes. A challenge is an uninterruptible task, liking fighting a boss. But at whatever difficulty level you are playing at, those encounters can't last longer than a set period of time. Based on my experience, 15 minutes is the mark. Any more than that, and it's no longer a fun challenge, it becomes a job.
  • Games should have the equivalent of chapter selection on Movies.The user should be able to randomly access any part of the game they want, even without having played through the game. Again, it's up to the user to decide if they want to play the whole game, or if they just want to show their friends a part of the game without local save files.
  • All game controllers should have a Save button. Seriously, 2008, and there is not a uniform way to trigger either a save game screen, or ideally a "Quick Save" option. Also, why not normalize some buttons with DVD+DVR controls? The "start" button on the Xbox 360 should just be labeled a play/pause button. The back button could easily be replaced with a Quick Save button.
  • Game system should standardize the UI for save games. No developer should have to spend time writing their own save systems. It would be like having to write the code for DVD player pause/play functionality every time you release a movie, insane.
  • Save games should be backed up by the platform (e.g. Xbox Live, Playstation Network) and synched to your other consoles No reason I shouldn't be able to start playing a game on the console under my big screen TV, make progress on it at a friends house, and then finish it at home again.
  • Access to game content in multiplayer modes should not be tied to progress/completion of single player content. I can't tell you how many now I buy a new game to play the multiplayer parts first, only to find it's crippled because I haven't played the single player part. Mario Kart Wii, Star Wars: The Force Unleashed Wii, Rock Band 2, the list goes on and on. And sorry, unlock codes are lame, a relic of a bygone era. If the industry wants to grow up, it has to stop doing this. Also, unlocking content is not a re
  • Save game files should contain information about the game at the point in time, so the gamer understands where in the game they are. Actual thumbnails from the game when you save are a must. More detailed information too, like any meters you can see on screen, you should be able to see the data about that in the save.
  • Games need to auto-reload to last save on launch. What happens when you watch a movie and turn off your DVD player, or watch a video in iTunes, then stop it and start playing again? It resumes from where you left off. All games should default to this! If this isn't the 90% case when playing through a single player game, I don't know what is. But this load also needs to be cancelable on the chance that its wrong. The current way most games work is just dumb. On Xbox 360, you have to:
    1. Start the game
    2. Click a button to get past the obligatory landing screen after the parade of logos
    3. Select Load Game and press button
    4. Select where you save game is stored
    5. Figure out which save game you want to load
    6. Wait for load
    Loading the last user saved or system saved game should be the default in all games until the user quits the game in a different mode (e.g. multiplayer), which it should then launch into.
  • Stop showing most publisher/studio/tech logos on launch. Take a cue from the iPhone, when the user or game saves progress, take a full screen snapshot of where in the game the player is. When the game is reloaded at launch, show that screenshot with a progress meter if its a long load, or just get the game running and make the content animated from that screenshot frame. You can even composite unobtrusive logos in a letterbox like fashion over top of the saved game screenshot. Animated logos with surround sound are only cute once, then despised every time after through the appearance of blocking the gamer from getting back into the actual game. I don't need to see the Havok logo anymore in a game, I get it. The equivalent would be like having to see that the filmmakers used Panavision or Kodak before the movie even starts, silly. Doesn't everyone hate having to sit through the FBI warning? That's one of the sweet things about movies from iTunes, no FBI warnings, trailers, or multiple studio banners, one on the film and one for the home video division. If you have to pay a few more dollars to get the engine credit to the end credits, please do so.
  • For MMOs with challenges (e.g. Raids/Dungeons) dependent on a group, use game telemetry to provide probable time estimates. I have done my fair share of raiding in World of WarCraft. I was never able to do a lot of raiding because I never had a good idea how much time any raid or dungeon run was going to take. WoW collects a tremendous amount of data about the game, which I call telemetry, and it should be used to provide gamers estimates based on past raiding/dungeon parties similar to your own.

Wrap-up
I am sure I have missed something, or there are holes in some of my arguments. This post was already long enough, covering every possible corner case would have made it ridiculously longer. I am more than happy to answer any comments, or if you want to send me an email you can through innerexception AT mac.com

Wednesday, October 15, 2008

iPhone app version and build numbers

I think every iPhone developer should know how to assign version and build numbers to their app. If you have come from another platform, like I have, the Apple way is the same but different. I am standing on the shoulders of Chris Hansen for a lot of this post. His post on Xcode: Build numbers and versioning with agvtool was the starting place for me on a lot of this information. Chris' post is nearly 3 years old now, Apple has moved some stuff around and it isn't iPhone specific. Plus, there are no screenshots :-)

Marketing or Build Version?
Apple's iPhone application templates create an Info.plist in the Resources group for you that is configured like this:

Highlighted is the Bundle version with a default value of 1.0. This was exactly how iTimeZone 1.0 was configured. For iTimeZone 1.1, I wanted to be able to show both the marketing version (Version 1.1) and the build number (Build 14), and toggle between them in the app, since I was going to distribute the app to beta testers. So i thought Bundle version was the marketing version because the templates fill it out with 1.0, but that is not the whole truth. Since the iPhone app templates don't really do anything with Bundle version, you have to look at a Cocoa OS X sample application to see the right behavior.

When you only have Bundle version, in a Cocoa OS X app you see a built-in About box that looks like this:

When you add a new key to Info.plist called Bundle version string, short, and flip your 1.0 into that key, and then put something more traditionally resembling a build number (14) back into Bundle version, the Cocoa OS X built-in About box looks like this:

So which keys you should use on the iPhone are pretty clear, Bundle version is really your build number key, and Bundle version string, short is really your marketing version. Now lets build something.

Build It
Whenever you hit Command-B in Xcode, you are creating a build. As a developer you are building all day long. But a developer build and a build you intend for distribution are two different things. When a build is distributed, you need to assign it a unique identifier so you can ask people what version they are on or for bug reporting purposes at a minimum. This is usually referred to as the build number. I am not talking about continuous integration here, only the manual process, but if you wanted to setup continuous integration, you just need to automate what I talking about.

Apple has a tool, called agvtool, to increment build numbers, the Bundle version in your Info.plist, as well as make that number a variable in your code. The tool is called agvtool for Apple-generic versioning tool for Xcode projects. In order for it to be usable, you have to change a few build settings in the Versioning section for All Configurations. Here is the screenshot of iTimeZone 1.1:

The key setting is Versioning System. This must be set to apple-generic. I don't know of any other possible setting except None. This does not mean that Xcode now automatically increments a build number for you, you have to take specific action to get the build number incremented. Now that those are set, you have to close your project, open Terminal, and do the following to increment your build number:
/Developer/usr/bin/agvtool next-version -all

This was one of differences with Chris Hansen's post, Apple moved the location of the tool. Also, I encourage you to hit man agvtool for all the options, like automatic check-ins to source control on running agvtool, but I haven't set those up yet. Now you have pretty much everything you need to use the build number system, but you need to be able to use this information in your iPhone app.

Using Build and Version Numbers In Your iPhone App
Unfortunately, there really isn't at the time of this writing a defined pattern for displaying version numbers or build numbers in iPhone applications. In iTimeZone 1.1, I choose to put a dedicated About screen into the app. Not only to show version and build numbers, but a couple of links to give credit for usage of assets in the application, and double-tappable icons to the iTimeZone and Tangerine Element web sites.

When you tap on any whitespace, or directly on the version number, you see the build number. To show these properties in your app, you need to know what the real names of the keys are. If you right-click on any of the properties in the Info.plist view, click Show Raw Keys/Values to see a view like this:

I created an AboutViewController with a corresponding XIB. In the viewDidLoad method, I set the UILabel to the version number by doing this:

[NSString stringWithFormat:@"Version %@",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];

When the user taps the view, a BOOL is used to track what state the view is in. When the build number should be changed, the UILabel is set to the the other number, so for the build number:

[NSString stringWithFormat:@"Build %@",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]];

Wrap Up
So I think that's it. Let me first say, I am not saying you have to do an About screen in your app, I didn't have one in iTimeZone 1.0. That said, unless I am just not thinking outside the box enough on this, I can't see how to conduct a beta test without being able to check the build number, and there is simply no facility for doing that built-in to iTunes or the iPhone. You can probably hide this in your app elsewhere, but it probably needs to be there.