A memory handling system
by jegeblad
The iPhone doesn't have a lot of memory. As programmers we have grown lazy about memory because we are used to 2 GB of RAM and lots virtual memory. Of course, the iPhone doesn't have that. With Lifecards I am constantly running into memory problems. Not the retain/release sort. Mine are of the type that I run out of memory. Lifecards uses a lot of texture memory. Each card is composed of multiple layers that each consists of a bunch of textures. Each photo included takes up additional texture memory. The toolbar takes up texture memory. Text in speech bubbles uses texture memory etc. etc.
The good thing about a lot of this is that all of these textures can in principle be discarded and rebuild at will. E.g. the toolbar icons can be removed from texture memory if they are not currently visible. The main-menu ditto. etc. By removing those items that are not currently used, we can save some memory, and we will require less memory overall.
That is why I am in the middle of changing the memory system for Lifecards now.
Basically, each element that requires some memory is a subclass of a class I call MemChunk.
Each subclass must implement void free(), void generate(), and int size(). I am using C++ for this system, but I could have used Objective C. Each MemChunk also has a bool ready. If ready is false, then the data represented by MemChunk must be generated first. Otherwise, we can safely use it.
The MemChunk superclass includes a function void ensureReady(). ensureReady() must be called before any of the data in the memory chunk is accesses/used. ensureReady calls generate if ready is false and nothing if ready is true. Before calling generate ensureReady also ensures that the memory subsystem has size() bytes available. If not, the memory subsystem will go through all the memory chunks and remove the least recently used memory chunks in order to free up some memory by calling free on each of those chunks.
Now, this is all quite similar to a conventional virtual memory system where page bits of memory can be paged in and out. The difference here is that the memory I need doesn't exist in flat form on the disk. Instead the generate function will generate it on-the-fly based on much smaller data either from the disk, or in rare occasions a much smaller piece of data in memory. Examples of generation can be as simple as loading a png-file, creating a texture containing text, or a full set of complex drawing instructions. In principle it can be anything. If the data must be freed from memory it is simply deallocated -- I don't write it down in flat format as you would in a virtual memory system.
In reality 90 % of all of the memory Lifecards uses lies in this category. Even photos stay on disk unless they are used to generate textures or the finished graphics.
There is a couple of neat features. Firstly, I can control much better how much memory I use. I can set the maximum memory I want to use for this subsystem to e.g. 10 MB and if I use more than that chunks will get freed. I can also use the system to keep control over which components I am currently using. If I get a memory warning from the system I can just drop everything which should help a bit in the short-term. Finally, if I forgot to free memory of a chunk manually, it will most certainly happen automatically when I need it.
The reason I started developing the system was the amount of memory used by the toolbar icons in the last submitted version 1.2.2. Each toolbar icon is 64x64x32 bit so with 64 toolbar icons that is a whole 1 MB I use just for the toolbar icons. I use an additional 128 KB for the shiny glass effect of the toolbar. I would like to add more icons and other things that are going to require more memory. In short, I had to do something. The text of the speech bubbles actually already used a similar system to ensure that the app doesn't crash when you generate 20 bubbles and therefore would run out of texture memory.
I love keeping things simple and it would have been great not to implement this, but it is really a nice little simple system with the core running in a little less than 150 lines of code. I can certainly see myself using it for lots of other things.
Scoreloop
by jegeblad
I spent Sunday evening and all of Monday this week trying to get Scoreloop (http://www.scoreloop.com/) to work with Eyestorm. The sales numbers for Eyestorm haven't exactly been stellar, so when Scoreloop contacted me about 3 weeks ago to ask if I would like to use their high-score library I was 100% interested since I figured that it would create some additional much desired exposure.
Essentially, scoreloop is a global high-score system; You submit you score to Scoreloop's server to see how well you performed compared to the rest of the world. The main part of Scoreloop however are "challenges". The idea is that you can challenge another player and the one with the best score wins some Scoreloop-coins. Scoreloop's profit should eventually come from these coins. In order to challenge someone you must have coins. You'll have to buy coins from Scoreloop, although you get a bunch to start of with. Scoreloop will share the profit of the coins with the developers that implement Scoreloop functionality in their app. Another cool feature is status updates on Facebook which is what you would like to have when you want exposure!
I didn't have much time during those 3 weeks, but once-in-a-while I was able to play around with Scoreloop. Scoreloop gives you a list of steps required to implement Scoreloop functionality, and it is relatively simple although the documentation could have been slightly more elaborate. I had some problems including the Scoreloop bundle with xib- and jpg-files in Eyestorm, most likely because Xcode failed for some reason each time I tried. I also had some issues linking with the scoreloop library, and because I use Objective C++ I had to add an extern "C" { ... } around the #import statement. Those things ended up taking me a few hours to get past, most likely because I tried to do it late at night, and I am still a rookie when dealing with project setup in Xcode.
Once I got Scoreloop up and running though, it pretty much did what it was supposed to except a bunch of bits essential to Eyestorm. Our own high-score system is synchronization based. I.e. you play while you are offline and then synchronize high-scores once you get online. At the present time Scoreloop lacks that functionality, although when I inquired they replied that they are planning to add it.
I tried various work-arounds by saving the score myself locally, but I couldn't get it to work properly. Essentially, I couldn't tell if the score-submission was successful or not. It could easily fail either because the device wasn't connected to the Internet (OK, I could check for that) or because the user didn't yet register at Scoreloop.
I always feel bad about using other people's libraries. It isn't just that you loose control over part of your code, but also that you have one more thing you have to keep up-to-date. During my test-period Scoreloop already made an update. Another thing is size. Scoreloop's bundle and library made the compressed distribution of Eyestorm grow by 3 MB which I think is quite a lot. Especially, since I prefer keeping things under the 10 MB over-the-air-limit (which I cannot find a reference to anywhere). However, I should note that the Scoreloop guys replied extremely promptly when I sent feature requests and asked about a crash in their code (which was probably triggered by me).
In the end I decided to postpone Scoreloop implementation for now. Unfortunately, it doesn't quite work with the way Eyestorm is built. I would love to have synchronization, because I know that the guy who currently holds the highest score plays the game on airplanes without any ability to submit scores. Playing Eyestorm from start to finish could take hours (you can put it away and continue later), so in reality I also don't think it works with challenges. Finally, the Eyestorm's UI just doesn't quite work with Scoreloop, although I think I got everything worked out in the end, but it became a bit messy.
Instead I went ahead and added weekly high-scores which took me about 4 hours including a few changes to some SQL queries and little bit of extra work on the client side. It should appear on the app-store within the next couple of weeks. At the same time I also fixed some problems on iPhone OS 3.0. I'll keep an eye on Scoreloop though and see if it will eventually be able to replace our own high-score system.
Rejections, private frameworks, iPod music and Dilemmas
by jegeblad
One of the things I am asked most about by non-iPhone developers is the Apple review process. While I am pretty happy about it, it has gotten a few undeserved hits by other developers. It has also gotten a few deserved ones. I thought I would share some of my experiences.
About 7 days
When I submitted the first app, iPushFit, to Apple I was worried about being rejected. Would they not approve of the user-interface? Did something not work as intended? They accepted it around the 1st of October after approximately 7 days review-time. It was smooth as butter!
The second app I submitted was the Halloween Pumpkinizer. A small simple app that took only a few days of work. That application was accepted within 24 hours (if I remember correctly). I guess that it was because it was just a few days before Halloween and Apple gave it some special attention. So again, smooth as butter and even dead fast.
After that the review periods increased. The next app was iPushFit Lite. A lite version of my jigsaw-like puzzle game. It took about 7 days again, but for the first time I experienced a rejection; According to Apple it crashed on the very first screen. I had used a preprocesser macro LITEVERSION and I had a lot of code of the form #ifdef LITEVERSION ... #endif. In the release and debug builds of iPushFit Lite I had included that preprocessor macro and so all my tests work flawlessly. However, I had forgotten to add it in the distribution build, so the version Apple received was a mix of full-version code and lite-version resources. Of course it crashed. It took me about an hour to understand what was going on though, but I am happy that Apple tested the application, because otherwise I would have had a lot of angry customers. It would have been cool though, if I could have tested the distribution build before submitting the app, but it is code signed differently.
A revision of iPushFit Lite was submitted and again rejected after about 3 days. In iPushFit I included a TableViewController with a list of all the jigsaw puzzles you can play. In the lite version I had included the same list, but those puzzles that were only available in the full version would lead to a "Buy the full version"-screen. This is not allowed because according to apple it can confuse the user, despite the fact that "only available in full version" was written below each entry. It was a bit annoying, but not a big thing. I would have preferred that I could somehow advertise for all those extra jigsaws that you get in the full version more clearly, but Apple is looking after its customers. I fixed this, and after a few more days of review the app was finally accepted.
As I remember the subsequent apps, Turkeynizer, XmasizeMe, and NotifyMe all went through smoothly. I think there was a crash in one of the updates and Apple's test team caught it.
Before Christmas I started getting a bit overconfident about the whole review process. After all, it seemed like Apple just made a few tests here and there.
Private APIs
Before Christmass I read a blog post by Erica Sadun via Darring Fireball. Back then there was a lot of discussion about using undocumented APIs.
Erica makes a distinction between private and public undocumented APIs, and calls the private APIs "Sauron of App Store". It is not really clear to me why linking to a private framework is worse than using undocumented APIs in public ones. After all, both can break at updates. The only reason I can think of is that you are not violating the contract you have with Apple.
NotifyMe (Soon to be called Voice Alarm) is my own little speaking clock/alarm/timer. When the idea for NotifyMe was first pitched to me, one of the important elements was that the iPod music playback should be paused during speech. I searched and searched and searched and couldn't find any public API to start/stop the iPod.
Eventually, NotifyMe was submitted without the feature. I had to gain all the sound, so that you could hear it while your iPod music was playing. That greatly degraded audio quality and I got some angry reviews because of it.
When it was time to make an update I decided to dig into the undocumented APIs again. It is relatively simple to find the different API calls of the different frameworks, but I am not going to disclose the procedure here out of fear of what Apple might do. After all, I am generally quite satisfied with the agreements I have with Apple, and I don't want to step on their toes.
I discovered that among the private frameworks there is in fact a set of procedures that can be used to pause, start, and stop the iPod playback. A bit of hacking and testing and I had everything up and running nicely after a couple of hours.
Now, I no longer had to gain the audio, and the whole feeling of the app was much better. I actually only started this as an experiment to see if it was possible. I was thinking that it would either work, in which case I would have more happy customers, or Apple would discover that NotifyMe was using private APIs and reject it. Either way, I didn't really have much to lose. Well, Apple went ahead and accepted the application, and in a way that was the worst thing that could happen.
After a couple of weeks I discovered a few bugs and one of my friends suggested a whole list of changes. So I started brewing an update. Now, the application with its usage of undocumented APIs had already slipped through Apple's review process, so I guessed that it would do it again. It didn't. The second time they rejected it for its usage of undocumented private APIs and they were very specific about the framework I had used, so I am guessing that Apple is actually screening the apps more thoroughly now.
I haven't used undocumented APIs in any of the other applications, and I regret having done it in NotifyMe now, because in a way this put me in a gigantic dilemma. I wanted to submit the update, but I couldn't do it without reverting to the old horrible sound system. However, the APIs I am looking for are coming in 3.0.
So what should I do, submit an update with horrible sound now, or wait 3 months and submit the intended update for version 3.0 of the iPhone software? After more than 1 month of consideration and working on other projects, I decided to revert the sound system and resubmit. The reason was that when people start updating to 3.0 everything should still work. I know that this will disappoint a lot of customers, but hopefully it will make me sleep better at night.
So let this be a warning to anyone else who thinks that they can slip under Apple's private frameworks radar. You might actually put yourself in an ugly dilemma, so don't do it!
Network connection
Recently I have had trouble again with rejections. Lifecards got rejected because a feature on the feature-list wasn't obvious to get to, so I added a help message. Eyestorm Lite got rejected the first time because I had copy-pasted the feature list from the full version which included "Local and global highscores". I had decided to remove "global highscores" since it didn't make much sense in the lite version, but Apple read the description and rejected the app because the feature was missing. It was OK to reject the submission, but a little annoying that we had to wait another 7 days just because I had overlooked something in the description. In Apple's defense; there was no way of knowing whether the description was wrong or the feature was missing from the app.
Yesterday we got two applications rejected because they didn't " adhere to the iPhone Human Interface Guidelines". This is what Apple writes when they find something they don't like. Both applications can access the Internet, but neither actually tested to see if the internet connection was available before doing it. We knew about this, but had decided that it probably didn't really pose a big problem to users, as it should be a little bit obvious that you need an Internet connection when you wish to export to Facebook or you want to synchronize high-scores.
In both cases Apple was right. There should really be a message.
The app store police?
Generally, I have been happy with Apple's rejection system. Almost all the rejections we've had were because we could have improved one thing or another, or because something went plain wrong. I really think that the that it is a wonderful thing that they check apps before they start to sell them. It improves the quality of the applications and the whole user experience on the platform. Sometimes it can be annoying as a developer because each reject costs another 7 days of wait, but on the other hand 7 days is really not a long wait.
I know that some developers get their apps rejected because they fall right in the middle of interests; Like that famous wifi-tethering app. It is a problem for the platform, and it would be great if Apple stated clearly what can be rejected. Unfortunately, it can be hard for them to predict all the possible uses of their phone.
It feels like Apple is also becoming increasingly pedantic about features and an annoying new thing for us developers is that all applications are now being tested against iPhone firmware 3.0 beta 5 before being accepted. I guess I finally have to download that SDK and start testing with it.
Considering that Apple receives around 200 app-submissions per days, I am amazed how well they are doing. The longest wait I've had was around 10 days!
Resolving Low Resolution
by jegeblad
Lifecards appeared on the featured list last week and that made sales go up by a factor of more than 10. People have been generally extremely positive about it.
Since Lifecards 1.0 came out there have been several blog-reviews. Some of them are linked below:
- Touch Reviews http://touchreviews.wordpress.com/2009/03/05/app-review-lifecards/
- Epic iPhone Apps
http://epiciphoneapps.com/epic-app-awards/lifecards/ - House of Husar http://www.husar.us/blog/archives/4407
It is has been great to read these posts and also the reviews on the US-iTunes store. I would like say thank you very much for those positive remarks and also to some of the negative ones. It took a lot of hard work to get Lifecards to a 1.0 release and I am sure I lost quite a bit of hair in the process. Thanks also to Apple for selecting Lifecards for the featured list.
Based on the positive reviews it hurts extra to read that someone had problems with resolution and therefore decided to only give it one star on the iTunes store. This was a huge puzzle for me. OK, the resolution is limited by the iPhone display and iPhone Mail's photo limits (can only send photos in maximal 800 pixel resolution). However, I didn't really think this warranted a one star review.
On the iPhone 2.2 firmware there was a huge bug with photo-saving; When you saved a photo to the photo album (from your own app) in resolution 800x600, Mail and Photos would only display a thumbnail variant of the photo (possibly 160x100 resolution). There is an easy work-around for this. Don't use even pixel resolutions; use odd ones -- i. e. use 801x601 instead of 800x600. If you do that everything looks OK on the iPhone 2.2. It is the weirdest bug but luckily Apple got around to fixing it on iPhone 2.2.1.
Lifecards contains that work-around so everything works on 2.2 firmware. I have tested this on my own phone many times to be sure.
That made me even more puzzled about the angry resolution comments. I was hoping that someone would send me more details, but for a couple of weeks all I would see was yet another angry review. Don't expect people to have the energy to write to you if something doesn't work -- It is too easy just to post a review on the iTunes store for that.
Finally I got a breakthrough last Thursday. One guy send me a thumbnail jpeg in very low resolution, and asked me if this could really be intentional. First I thought that he had some special variant of the iPhone 2.2 software because the bug looked similar. I asked him to update his iPhone and send me some more details, but just as I emailed my reply I looked at the bottom of his message; "Sent from my iPod".
AHA! I had been so preoccupied with iPhones that I had not thought about iPods. I searched the web and discovered that the guy behind ColorSplash (no. 3 on the app-store at this time) had experienced some problems with the iPod touch. He mentioned a work-around but the short-description he gave was exactly the one I had used for the iPhone 2.2 which obviously did not work since it was already in Lifecards. I decided to run out early Friday morning and buy an iPod touch for testing. A few hours later I had a solution.
It turns out that the iPod touch doesn't like photos saved in resolutions higher than 800x600 to the Photos album, so I simply reduced the output-resolution in Lifecards to a maximum of 799 pixels in either direction and that seems to have helped. The photos still look bad in the Photos viewer, but now the correct image is emailed with Mail. These bugs are really weird and it is very frustrating as developer to run into them because there is nothing you can. The bugs are completely on Apple's side. By the way, the guy got an iTunes gift-card for helping solving the puzzle for me.
I should also note that the high-resolution photo saved by Lifecards is still on the iPod in any case and you can use the SendPhotos app to email them.
Anyway, Lifecards 1.0.1 with this workaround has now been submitted, and based on the user comments Michael and I have gotten some great ideas for Lifecards 1.1 which we are already working on.
Eyestorm submitted!
by jegeblad
Fitting quote (from the bible): "Behold, I send you forth as sheep in the middle of wolves: be you therefore wise as serpents, and harmless as doves."
Eyestorm is a game Michael and I began working on back in November. A game on the iPhone app store is likely to vanish into oblivion unless it is special, and even then it might not survive for long. Eyestorm is special to me because it is one of those rare projects were I have felt more than 90% satisfied with the end-result.
So much energy has gone into graphics, sound, music, code, level design etc., that submitting Eyestorm to Apple actually made me feel really really nervous; For how will this little baby of ours which has now grown up survive in the dangerous realm of the iTunes store.
Eyestorm was Michael's idea originally. He wanted a Jezzball/Styx clone for the iPhone. We found one (I forgot the name) and we immediately found it too ugly, too hard, and just plain boring. So knowing that there was something he wanted that did not exist Michael started working. He made several prototypes. Some of them looked a lot like the clones that are on the store now. Some were quite sophisticated. Eventually, we settled on the ball/brick strategy used in the final game.
Here is a bit of trivia:
- Started development in November 2008. Was developed in parallel with a bunch of other projects. Estimated full-time development: 6-7 man-weeks
- Initially used plain Quartz. Moved to Core animation. When that didn't run smoothly enough we moved to OpenGL. That was the only way to get 40+ fps.
- Code-name: Fireball. This was my idea. Michael called it O-fence. Originally I wanted to make a game with burning balls. We did not settle on Eyestorm until 2 weeks before submission. Until then we did not have a proper name
- It was Michael's idea to use CGTextures. We couldn't have made the game without them
- The eye-idea came in December 2008. Until then we just used balls. Michael and I never agreed on the background story (I wanted the game to start in prison). The problem was to create a suitable animating object.
- Several people asked if I could put eyelids on the eyes. For a while I thought I could
- To create the eyes, I built a custom ray-tracer one very late evening using some ingredients I had for a real-time ray-tracer built for CUDA
- The sea-monsters were there almost from the beginning
- The enemy source-code is a first-class example of object oriented programming
- The gfx, code, and first level for the UFOs were done in one single evening including the required extensions to our custom ray-tracer.
- The first time I loaded up the UFO after doing the code, animation, laser beams, and sound it worked instantly and Michael and I were laughing for minutes over its aggressiveness. This was probably the funniest moment during development. It was love at first sight. The UFO-code was only changed very little after that
- Music was a challenge. Even though we know most people are likely to use the iPod music player, we felt we needed some sort of background music. We both loaded up Garage Band several times. In the end I made a couple of tracks that we found acceptable for the game. Now I can't really see the game without them -- much less play it.
- The light effects on levels Courtyard, Subway, and Lighthouse were Michael's idea. It actually took me a full day to make them which I think is a lot for such a small detail.
- The game was 17 MB uncompressed one week before submission. I cut away 6 MB music and graphics including several musical tracks to get it to 10.6 MB uncompressed (9.8 compressed).
- The cross-hair was a late addition to the game. I added it because I saw my girlfriend and mother struggling with the game. Having my mother play the game was a good usability test. The cross-hair completely changed the feeling of the game
- Special thanks to my girlfriend who patiently played this game many many times.
- The green UFO level was added 1 1/2 weeks before submission as the last level. It was added solely to introduce UFOs at an earlier stage in the game. Before that they did not appear until the UFOes level
- Most levels took between one and two hours to make once I had an overall idea/theme
- The monorail level took 1+ full day to make due to the eye-movement which was very painful to implement
- The screen-wipe effects were inspired by the original DOOM and 5 of the moon-base levels share names with shareware DOOM levels. DOOM was one of the last games I played and I needed names of moon-base levels. Interestingly enough my girlfriend told me that the level Prison Sewers reminded her of DOOM -- That was unintentional
- The Dr. Eyequeue level was inspired by the ending of Lord of the Rings films. However, it was too complicated to make a crashing tower with the big eye dropping to the ground so I settled for an explosion instead
- Was finished almost a complete week before submission so that we had adequate time to test and remove critical bugs. On submission day we ended up making quite a few last-minute changes anyway.
- While taking screen-shots I got so immersed in the game several times, that I completely forgot why I was playing it
- Began development without deadline. I originally wanted to release it in December, but the game wasn't even close to finish even by January. This game was a classic "it will be done when it is done"-game. Originally we also only wanted 24 levels. I think the final product greatly exceeds my own expectations
07/25/09 07:45:50 pm, 