Fixing “Unsupported iOS SDK Mismatch” in Solar2d

If you are trying to build your Solar2d app for iOS and see a message like this:

iOS SDK Mismatch

then you have a version of Xcode (and therefore the iOS SDK) installed that doesn’t match what the Solar2d build tools are expecting.

  • If the version that you have installed is LOWER than the target version you should upgrade Xcode
  • If the version that you have installed is HIGHER than the target version then you may be OK by simply clicking “Yes” and proceeding with the build

If Solar2d will not build after clicking “Yes”, then your last option is downloading the version of Xcode that comes with the target iOS SDK version that you require. Start by determining what version of Xcode comes with the iOS SDK that you need by reading here: https://developer.apple.com/support/xcode/ For example, to install iOS SDK 17.0 you would want to download Xcode version 15.0.x (where x is the highest number you find in the downloads list below, in this case 15.0.1).

After you know what major and minor version of XCode you need, log in with your developer account to the download page: https://developer.apple.com/download/all/

Scroll down to or search for the version that you need and click to download it. This will place a large xip file in your downloads folder. When the download is complete, find the xip file and double-click to expand it. This will take a few minutes. The expanded file will simply be named “Xcode”, rename it to something like “Xcode-15.0.1” that corresponds to the version you downloaded. Move this file into your Applications folder.

Activate this version of Xcode by opening a terminal window and running a command like this:

sudo xcode-select -switch /Applications/Xcode-15.0.1.app

Now launch the version of Xcode that you downloaded by finding it in the Applications folder. When it starts up you will probably need to download and install the iOS SDK – make sure that it matches the version you were looking for.

After the iOS SDK is installed, restart the Solar2d launcher and then open your app. The iOS build dialog should proceed without a warning.

To later switch back to the version of Xcode that is installed from the Mac App Store, you can run this command:

sudo xcode-select -switch /Applications/Xcode.app

My Crazy Meme App Idea That Worked

On Thursday, November 24th, 2022 I read a post by a woman on Twitter that went like this: “Have you heard about dabloons? My daughter is on Tiktok and apparently it’s the latest trend.” I had not heard about dabloons. I also did not have a Tiktok account, although I had watched many funny videos on the platform. I searched online “what is a dabloon?” and found very few explanations, but eventually pieced together that it was something like a virtual currency. Except you effectively couldn’t buy it, trade it, or spend it. It was a meme currency, and you earned it only by watching Tiktok videos.

The question I then asked myself wasn’t “why?” because if it was like any other meme there is no sane answer to that, only more questions. No, the question I was wondering was “how?” How were people keeping track of their dabloon transactions and balances? The surprising answer was that they were, by and large, using the notes app on their phone. This sounded tiresome and less than optimal to me, but maybe I just wasn’t getting it yet.

I am a software developer. I’ve been programming computers and coercing them to perform novel functions since 1983. I’ve also been creating mobile apps for a number of years, since my first app Just Clean Jokes came out in 2009. I have a decent catalog of published apps, and enough experience to believe I could develop a better way for tracking dabloons. But had someone already beaten me to the punch? With mobile apps — and more importantly trending memes — timing can be everything.

I searched the Apple App Store for the keyword “dabloons” and came up with no results. Zero. Absolutely nothing existed yet. I was stunned — this has never, ever happened to me in the last 13 years, searching on a trending keyword and finding nothing. Searching the Google Play store yielded the same strangely blank results.

My daughters have used Tiktok more than I have, so I asked both of them if they had heard of dabloons? No, neither of them had. So together we searched Tiktok and viewed a few of the trending videos. If we were keeping track, we had already earned some of the meme currency. This started to feel like an opportunity.

I came up with a plan, and ran it past my wife: “I’d like to spend all my free time in the next 3 to 4 days developing a mobile app to ride a Tiktok trend.” She asked what the trend was, and I did my best to explain it, but in the end she just said “I don’t really understand, but have fun!”

Years of developing software have taught me the project management rule of three: “within scope, within budget or on time: pick two”. It was slightly worse than that because if I gave myself the goal of having an app developed and launched within four days I was effectively trying to set both time and budget, since I was only one person and my budget was measured in development hours. So my scope was going to have to be very, very limited. What could I launch in only four days? Would it suck? Would people even download it? If people did download it, would they hate it and review bomb it down into oblivion? I had to force myself to throw out all of my worries and just forge ahead.

From the outset I made a promise that there would be no crunch on this project. Yes, I would work hard and dedicate myself to the task, but when it was time for bed I would go to sleep. No all-nighters. No working past my normal bed time to finish “just one more thing”, because I know that always ends up being ten more things. Since it was already just past 6:00 pm on the 24th, and bed time for me was normally 1:00 am, the first evening would only yield about 7 hours of development. But first I needed a name; I needed to stake my claim in both app stores. I tried the short and obvious “Dabloons”, but someone had already claimed that app name in the Apple App Store. I assume that meant someone had the same idea as me and I was already racing against an invisible competitor. My second idea “Dabloon Tracker” was available, and I didn’t have time to ponder whether that was optimized for marketing, so I just went with it.

One benefit of having a catalog of mobile apps already developed meant that I had a good amount of code that I could repurpose, on a platform that not only I was familiar with but is also appropriate and fast (Solar2d). I wasn’t starting totally from scratch. I copied the scrolling list code from my app Just Clean Jokes and made that the home screen. I copied the database code from my game Hexium, which included a nice facility to easily push database updates. I put the two of these together and by the time I was done the first night I had a “todo list” style app that could be added to and edited, with the list items saved in the database. Bed time came too early that first night; it didn’t seem like I had enough done.

Development day two. I had the start of an app but it was plain. Boring. Uninspired. Over breakfast I wondered what I could add that would make it look more appealing. I decided on pixellated icons, one for each row in the list, and the icon could be chosen by the user to correspond with the list item. As I’m a programmer, not an artist, and because time was very limited I would have to buy these icons. Over that lunch hour I found an icon pack that looked like it would fit and bought it. Sadly that first pack ended up being a waste as the icons were all on a sheet together instead of individual files. I bought another, and thankfully found that the second pack would work. Development was interrupted again, this time for supper.

After supper I got back to work, giving the user a way to select an icon for each item and incorporating the icons into the list view. They looked good! I then added a function to keep a running total of all the list amounts and show the user their current dabloon balance at the top of the home screen. The home screen and edit screen were essentially done at this point.

I had learned a lesson at some point in my life that we should keep expectations reasonable but prepare for all outcomes. The first outcome of “nobody downloads this app” was easy, I didn’t have anything else to do. But what if the app was a hit? How would I recoup my investment of development time, and the loss of spending time with my family, to make this gamble pay off?

I knew that I could choose to put ads into Dabloon Tracker, however I have recently been very disappointed at the amount of ads in mobile games that I play myself, and I knew I didn’t want my users to have that as their experience. So ads were out. But in-app purchases could be OK if they were done right and felt fair. I could easily buy more icon packs and then mix and match them into themes and sell them as themed icon packs. This opened up a number of new questions: what should they be priced at? How many icons should come in a pack? What icons should come unlocked for free? How many packs should be in the shop in total? There are no right answers here, just educated guesses. I decided on shipping the first version of the app with 90 icons unlocked and then 5 more icon packs of about 72 icons each for a price of $1.99 USD per pack.

Development on the second night paused as I went over to a friends house for a game night that we had planned weeks ago. To honor my “no crunch” promise to myself, there wasn’t even a chance that I would back out of games night. That wouldn’t be fair to me or to my friends. After about 3.5 hours of gaming and visiting, I got a ride home from my friend Glenn. Of course I explained my dabloon tracker app idea to him, and he actually understood it better than I did, since he realized that the whole thing was a thinly veiled method of attracting more people to search for and watch trending videos on Tiktok. I finished off the second night by adding the code in for the in-app store.

The third development day was a Saturday and I would have most of the day to work on the app, but my wife and I did have a dinner and dance to attend that night, so my time was once again bounded. I worked hard to get everything tested, along with creating multiple sizes of the Dabloon Tracker app icon for the app store, but it just couldn’t be completed before it was time to leave for the dinner. At dinner I once again explained the dabloon tracker app to the people at our table, and most of them got a good laugh out of it. This wasn’t my strangest app idea, but it was certainly up there. After dinner and dancing, and helping clean up after the dance, I finally got the app submitted to both Apple and Google app stores. Now the waiting began.

I spent that Sunday doing anything but development, since it was out of my hands. I was hoping that I would enjoy some miraculous speedy review, but otherwise it was a normal day. The app did actually get approved by Apple very late Sunday night (or early Monday morning) but I wasn’t awake to see it. Monday morning when I woke up it was like Christmas: my present had arrived and my app was live on the Apple App Store! If only people could find it… I searched for “dabloon” and sadly the app was not yet in the massive search index. But thankfully, neither were any other apps – the search results were still empty! Some hours later I searched and found my app. All systems were go! Come on Tiktok masses, come find my app!

Then the bombshell. “Hey Dad, I downloaded your app… why is the store blank?”

“What? No way. Are you joking?”

My daughter was not joking. The in-app store was blank. But how could that be? It was fine in testing. And why was the app approved if I screwed up the store? After some time searching I discovered my error: I had sent the app in for review, but forgot to send the in-app purchases in for review!! This was a disaster. So I dutifully submitted a new version of the app as required and then attached the in-app purchase items for review. And then waited. And waited. I waited in silent panic for probably about 10 minutes before I decided to ask Apple for a favor: would they please grant me an expedited app review? In 13 years of publishing apps with them I had never done this, so I wasn’t sure what to expect, but only 5 minutes later they granted my request, and then 20 minutes after that my update was approved! This was amazing. The Dabloon Tracker in-app store was fully functional.

I searched in the App Store again, found my app, and verified that the updated version had rolled out. Then I noticed something strange: it said my app was #42. Number 42 where though? What was this? I would later learn that 25 000 people had already found my app and downloaded it, and were pushing it up the App Store chart in popularity! Dabloon Tracker was already the 42nd most popular free app in Canada in the Entertainment category. This was the start of better things to come. By the end of the first day of being available, Dabloon Tracker would reach number 25 in Canada and number 43 in the USA. Unfortunately it would take another full day and a half before Google finally approved my app for their app store, so I lost the first mover advantage there.

As of the time of writing this story, Dabloon Tracker has now been the 12th most popular free app in the Entertainment category in both Canada and USA for two days, ahead of other well known names like CBC, CTV, Cineplex and PlayStation. The reviews are overall positive, with just a few people leaving 1-star reviews because they didn’t like that “all the good icons costed money”. I understand where they are coming from and I have already added some cute cat icons to the free set. It’s a balancing act and I don’t want people to feel like they are required to buy icons, I want it to feel more optional like Fortnite skins.

I have many more ideas on how to improve the app, but they will have to wait until I’m back from vacation. Right now I am still enjoying the feeling of having set a goal and meeting it.

Me and my crazy app ideas.

Dabloon Tracker in the Apple App Store

Dabloon Tracker in the Google Play Store

Remember to Cancel Timers When App Is In The Background

I had been working on my latest mobile game Hexium for quite a few months, and it was time to put it into the hands of some testers.  Everything seemed to be going well, but then one of the testers said that their Android phone was giving them a warning about it consuming too much battery power when it was in the background.  I had never seen this warning myself, despite already testing the app on a few Android devices of my own.  He showed me the screen, and sure enough the warning was on screen and looking fairly scary.  As an end user, my first instinct on seeing a warning like that for a casual game would be to simply uninstall the app.  This was bad.

I looked over my code and didn’t see anything that could be causing the problem.  I went to the Corona Labs web site and found this excellent article on performance and optimization.  However after implementing all of the tips, the tester was still receiving the warning.

Then I found it: a timer.  I had a timer that was firing every second while the game was running, to check to see if the user had earned a free life.  Of course this timer didn’t need to run while the app was in the background.  So I added some code to catch the applicationSuspended event and cancel the timer, and gave the updated build to the tester.

Success!! Android no longer complained about the app using battery while in the background!

Here is a block of code that contains just enough to show you what events were caught, and how I stopped and restarted the timer:

local free_life_timer

local onSystem = function(event)
    if event.type == "applicationExit" then
        -- Cancel the free life timer
        if free_life_timer ~= nil then
            timer.cancel(free_life_timer)
        end

    elseif event.type == "applicationSuspend" then
        -- Cancel the free life timer
        if free_life_timer ~= nil then
            timer.cancel(free_life_timer)
        end

    elseif event.type == "applicationResume" then
        - Start a new free life timer
        free_life_timer = timer.performWithDelay(1000, function()
            update_free_lives()
        end, 0)
    end
end

function scene:show( event )
    local phase = event.phase

    if ( phase == "will" ) then
        -- setup a system event listener
        Runtime:addEventListener( "system", onSystem )
        
    elseif ( phase == "did" ) then
        -- Start the free life timer
        free_life_timer = timer.performWithDelay(1000, function()
            update_free_lives()
        end, 0)
	end

end


function scene:hide( event )
    local phase = event.phase

    if ( phase == "will" ) then
        -- Cancel the free life timer
        timer.cancel(free_life_timer)
        Runtime:removeEventListener( "system", onSystem )
    end
end