Ad

Our DNA is written in Swift
Jump

MyAppSales 1.0.15 – "Holiday Fix"

The latest version of MyAppSales contains a fix for times when iTunes Connect is offline, but the ITTS reporting site is still running.

This way you still get your daily sales reports throughout the downtime for ITC which was announced to go from December 23rd until December 29th.

Another minor change is that review scraping no longer is requires for the app totals to be loaded because all reviews for all apps are scraped at the same time anyway. This is a minor startup speed benefit.

Merry Christmas and Happy Holidays!

UPDATE Dec 26th: Apple caught up on using the ITTS url for report downloading and shut this down as well, giving this message.

So, there now really IS no way to get to your daily reports anymore. We all have to wait until Dec 29th.

Double Tapping on Buttons

Grinarn asks:

“I got several buttons set up on my view and when the button gets clicked, a detailed view of that item appears.
What I need is another action method like double click or click and hold, to trigger another action.

How can I do this? I just found the events in the IB which seems only supports single touch events.”

Any view in the SDK can receive and process touch events. This gives you the ability to implement any kind of tap or gesture that you might dream up. But for everyday purposes we will find the methods provided by UIControl sufficient. UIControl inherits from UIView which means that it can do everything that views can do, but it adds the Target-Action mechanism.

For this mechanism you can attach a multitude of various events to each control by simply specifying a target (= any object instance), an action (= any selector of the target) and a constant from the following list. “Selector” is only fancy name for method signature, which consists of the method name and the names of the parameters, all with a colon behind them.

General Touch Actions

  • UIControlEventTouchDown
  • UIControlEventTouchDownRepeat
  • UIControlEventTouchDragInside
  • UIControlEventTouchDragOutside
  • UIControlEventTouchDragEnter
  • UIControlEventTouchDragExit
  • UIControlEventTouchUpInside
  • UIControlEventTouchUpOutside
  • UIControlEventTouchCancel

Specific to Editing Controls

  • UIControlEventValueChanged
  • UIControlEventEditingDidBegin
  • UIControlEventEditingChanged
  • UIControlEventEditingDidEnd
  • UIControlEventEditingDidEndOnExit

Generic Constants matching several Actions

  • UIControlEventAllTouchEvents
  • UIControlEventAllEditingEvents
  • UIControlEventApplicationReserved
  • UIControlEventSystemReserved
  • UIControlEventAllEvents

Now generally if you make a button then you would use the UIControlEventTouchUpInside event even though at first you might instinctively go for UIControlEventTouchDown. TouchUpInside is the standard as it allows the user to reconsider and move outside of the button before lifting his finger thus cancelling his action. Otherwise the button would be like a landmine where there is no way back after touching it.

Now there might be cases where you exactly WANT the action to be fired right when you touch the control. Then TouchDown is the right action. You also see a TouchDownRepeat action available, but this always comes in succession after a TouchDown. Therefore some additional trickery is necessary to be able to distinguish between single and double tapping a button.

Read more

Early Christmas, 27" i7 iMac arrives

I had ordered my 27″ i7 iMac on Black Friday and expected for it to arrive on Dec 22nd as per the information on the Apple Store website. I was happily surprised to suddenly have a UPS lady at my doorstep at 5 pm. I had never gotten a package from any package delivery service that late, that was another first.

When I inquired about that, she responded: “We start at 5 am and we don’t stop until all packages are delivered”. I like that kind of service.

My brother-in-law helped me record the unboxing on my iPhone 3GS. To get it from 15 minutes down to the maximum allowed 10 minutes for YouTube I already used iMovie on the new machine.

I got:

  • 27″ gorgeous LED-lit wide display. It’s also huge. Almost twice as wide as my 15″ MacBook Pro display.
  • 8 GB RAM, I chose the cheaper variant where all memory banks are full because I don’t think I will need more RAM for coding
  • i7 CPU, the fastest iMAC machine. It’s also the greenest because between high-power compiling it can idle more thus using less energy.

About this Mac

As a final test of happiness I installed XCode on it and timed a complete build (after clean all) of MyAppSales. I had to first export my certificates including private keys from my MacBook and import them on the iMac’s keychain. Then I also copied the provisioning profiles and installed all things via double click.

  • MacBook Pro 2.8 GHz Intel Core 2 Duo: 12 seconds
  • iMac 2.8 GHz Intel Core i7: 4 seconds

I had to double-check these results, but really the i7 compiles my biggest project 3 times as fast. You wouldn’t think that having the processor speed rated at the same value. But more cores and 2 generations later give you such a turbo-boost. “Roarr!” says the Tiger, pardon, Leopard, pardon SNOW Leopard.

Dr. Touch #008 – "Christmas in Sight"

Apple is planning an outage over Christmas, ’tis the season.

Subscribe to the Podcast in iTunes

My script (aka “Show Notes”) after the fold below.

Read more

Reverse-Engineering the Apple CalendarView

Now that I am able to work on my apps full time I finally get to fulfilling my user’s wishes. Amongst those, for iWoman, was a Calendar view. I did not want to use another person’s code and deconstructing something that Apple made proved to be extremely instructive.

Here a video demonstration of my result so far. Please disregard the incorrect labelling at the top. At least this proves that this is not fake. 🙂

The whole thing consists of three classes, a CalendarViewController which takes care of the general management of views, a CalendarDayView which derives it’s capabilities of responding to target/actions from UIButton and a CalendarHeadView which is everything you see at the top.

What makes it look special and professional is that you have multiple gradients at work. Besides of the obvious one at the bottom, there are black and gray gradients shading most of the labels. The regular gray background I custom draw in drawRect, but the blue selected box was too complicated so I opted to use a UIImage for that.

For most of the calendar calculations I needed an NSCalendar, so I made a helper class with category extensions to NSCalendar to give me things like first day of next month, first day of current month, number of days and so on. I found that I could quite reduce my memory footprint by reusing things. Like instantiating the NSCalendar just once for the whole view controller.

By far the trickiest business was to properly set up the grid of CalendarDayViews on an invisible view where I enabled cropping of children so that stuff could appear smoothly from below the gradient at the bottom. The animation itself is trivial, but it took some time to figure out that I would have to overlay the bottom or top line with alpha 0 with another set of days when going forward or backward and then animate it to 1 so that the selected day would move from being a gray rectangle (if you tap on a day that belongs to another month) to being blue.

For quickly switching between months you can tap and hold one of the arrows. For this I did not need any animations, so I pimped the method to set up my sheet with an animated: BOOL parameter. For regular switching this would be YES, for fast-switching it would be NO. Et voila!

That all worked nicely until I started to use it on device. I was asked about the performance on device, especially for fast-scrolling. I found that I could tremendously improve this by reusing the UIViews I am creating one for each day. Before my optimization all subviews to my sheet where removed from superview when they left the visible area. Since the sheet was the only view retaining them this also lead to their deallocation. Then I simply introduced an NSMutableSet to hold onto removed Views and also reuse CalendarDayViews from there. This speed things up quite a bit. Reuse if you can as much as you can!

Next things to do on the CalendarView control is to add a table view below it, so that I can query a data source for table view sections and rows pertaining to the currently selected date. I plan on using this in version 2 of iWoman, the first major update to my best selling app. I owe this to my customers since I did not get to update this app in over a year.

CalendarView will be the first of many things in my “Dr. Touch Parts Store” where you can obtain high quality components to use in your own projects for a small donation. The concept is that this small amount of money pays for the time I need to continue to improve it, support it and help you with implementing it.

Adding Last Build Date & Time

If you need or want the date and/or time the last time your app was built then there are two handy macros you can use. Consider the following example:

char *date = __DATE__;  // e.g. 'Dec 15 2009'
char *time = __TIME__;  // e.g. '15:25:56'
 
NSLog(@"Build date: %s", date);
NSLog(@"Build time: %s", time);

I tested it with GCC 4.0, GCC 4.2 and even the new LLVM GCC 4.2. It worked fine with all three compilers.

Those are precompiler macros which work by getting the precompiler to replace them with the current date and time when the file in which they are located in gets built. Bear in mind that generally building is incremental and therefore these will only get updated if there really IS a new build happening. That’s a drawback if you forget it, but you can or should always do a build – clean before you build a release or distribution version to make certain all got updated.

Since those macros date back to C times they get replaced with C-style strings. That’s a pointer to a char array with a binary zero at the end to terminate the string. To convert them to obj-C NSStrings is simple by means of the %s formatter or by using one of the more complicated initializers of NSString.

char *date = __DATE__;
char *time = __TIME__;
 
NSString *myDate = [NSString stringWithCString:date encoding:NSASCIIStringEncoding];
NSString *myTime = [NSString stringWithFormat:@"%s", time];

Finally if you are really so much “pro” that the build time of your app matters, then you will probably also ask if there is a way to force building of certain files to forego the incremental building. Sure you can. All you need to do is to add an extra script to your target to set the last modified time of the file using these macros to the future.

touch -t 2012310000 "${PROJECT_DIR}/Classes/CalendarAppDelegate.m"

Touching Script

It’s not enough to simply set the modified time once because then the next modification sets it back to the current date. Thus the need for this simple unobtrusive script, in this example I am setting the modified time for CalendarAppDelegate.m to Dec 31st 2020 which is sufficiently far away so that this blog article will work for the next 11 years. 🙂

Waiting for Daily Reports

A developer based in the USA would probably never have to wait for the previous day’s sales report. But the iPhone app selling business being a global operation all EMEA and even more so APAC countries have to wait for a while until they can see how they are doing. That’s why I started to collect data on when a daily sales report usually becomes available so that I might be able to see a pattern or draw any interesting conclusions from that.

You might remember that I added anonymous availability reporting to MyAppSales 1.0.10. This way whenever a new report is encountered I am able to send out a push notification and automatically post it to the MyAppSales Twitter feed. The theory was that Apple would not make daily reports available at different times around the globe, but just have a single database that treats all developers the same. After a couple of days testing this hypothesis it was proven correct. In the beginning I was often myself the first person to see a report and thus trigger the notification. But very soon it started to happen that colleagues started to consistently beat me to the punch, be it either because they where more eager to get the report or just because the number of people with newer versions of MyAppSales exploded as I added more and more compelling features.

Now after 2 months Thomas Bonnin (thanks!) suggested to do an analysis, so I created a CSV file for the daily availability times for the past 2 months and Thomas kindly created these two charts from it. Bear in mind that the detection granularity grew over time, so at the beginning of this report the actual availability times might have been actually a bit lower.

Read more

Dr. Touch #007 – "Shaken not Stirred"

More and more video streaming apps arrive on the app store. More tablet rumors and a new iPhone coming next year.

Subscribe to the Podcast in iTunes

My script (aka “Show Notes”) after the fold below.

Read more

How to make a Pull-To-Reload TableView just like Tweetie 2

When I started on Twitter, I tried out a few Twitter clients both on Mac and iPhone until I quickly settled on Tweetie. When Loren Brichter made the bold move to sell Tweetie 2 as a seperate app I also purchased it because I am convinced this guy means quality and Tweetie 2 is on the first page of my springboard.

One thing that’s cool about Tweetie 2 is the fresh paradigm to refreshing the contents of a table view. Up until now we had been looking for space to mount a reload button on, sometimes having to resort to adding an extra tool bar for just one view so that you can have enough space. Now if you have a tableview that it sorted reverse chronologically, then you have a natural urge to make new items appear at the top by pulling down the table with extra force.

Loren recognized this need and innovated the Pull-To-Reload paradigm. If you want to refresh a tableview in Tweetie 2 then you simply pull down the table far enough for an additional cell to appear at the top with the instruction “Pull down to refresh”. If you do, then at a certain point the arrow rotates and the text changes to “Release to refresh”. All accompanied by two distinct wooshing sounds and a pop once the reloading action has ceased. The Intuitiveness of this paradigm is so compelling in fact that people who use Tweetie 2 start to try to refresh ALL tableviews like this.

Might be a good case to make this the standard way from now on because it feels more logical and natural than to tap on a small button with a circular arrow on it. A user of MyAppSales requested that I add this mechanism for reloading reviews of individual apps. At first I thought this to be advanced magic, probably using forbidden techniques. But after a bit of research and lots of hints coming from my Twitter friends (thanks Thomas and Fabian) I figured it out. This article explains how I did it.

Read more

MyAppSales 1.0.14 "Go Faster Strips"

There are a couple of users of MyAppSales who have been collecting daily reports since the first feeble beginnings. Turns out that if you have upwards of 300 reports in your apps.db then the previous method of loading everything at program start has a major drawback. There is a watchdog timer which kills any app that takes more than 20 seconds to start which caused a couple of users grief because that’s how long it took to load on iPhone 2G or 3G if you had that many reports.

This release is all about startup speed. On my own iPhone 3GS I managed to get from 12 seconds down to about 2. Also I was intrigued by the request to add a Tweetie-2-like Pull-To-Refresh mechanism, so that’s in there as well.

Changes:

  • Pull To RefreshADDED: Added Pull-to-Refresh on all review table views. Just like in Tweetie 2.
  • CHANGED: If a review text or rating changes then the review will now be updated and marked as changed.
  • FIXED: Country assignments for some report regions where incorrect causing financial reports to be incorrectly rejected as duplicate. Fixed translation language for China.
  • FIXED: Changed from GET to POST for Google Translations to support extra long review texts.
  • FIXED: Bug would cause link between InAppPurchase and App to disappear upon restart
  • CHANGED: Rewrote totalling to cache and replace averaging
  • CHANGED: Numerous performance improvements
  • ADDED: Lazy loading for reviews for additional speed improvement on startup
  • ADDED: Transparent 2-stage loading of reviews to speed up opening of review page for an app
  • CHANGED: Made more UI elements opaque to speed up table view drawing
  • ADDED: Financial and Monthly Free download on Import/Export homepage

Generally if you have any issues after applying this update then please go to the settings page and tap “Empty Caches”. This removes all the cached .dat files keeping information for faster access.

Now that I am coding full time I can spend hours and hours on lots of things. I have to literally force myself not to take on too much before releasing a new version. I’ll have to start updating all my other apps, create some new ones and then there are some looming contracts.