Our BlogTips, Tricks, and Thoughts from Cerebral Gardens

App Store 2.0

AppStore2Heade_20200825-065356_1

I've been saying for years that we need a new App Store. With the Epic/Apple battle being played out in public, I figured I'd describe my current vision for a new App Store model that better serves users today. The obvious answer is just to move iOS to a macOS like system but it's just as obvious Apple isn't willing to do that. My proposal is a compromise that I believe offers a fair direction forward for all parties involved.

First, let me describe the assumptions I'm working with. I'm sure I'll miss something, hopefully nothing that would drastically affect my proposal though. Of course, message me if you think I need to include something I haven't and I'll add updates.

Some Assumptions/Assertions:

  1. It's impossible to create an environment that is both useful and 100% safe. Just being alive puts you in danger. This is normal. The goal isn't to be 100% safe, it's to find the point at which users are reasonably safe while still being functional. Anyone that tells you the current situation is 100% safe is lying to you. Arguably, it's also a disservice to tell people they have no risk, or even create a situation where there is no risk.

    Example 1: Even though viruses/malware are rare on macOS, computer experts should never say "get a Mac, they don't get viruses," since then users would think they're safe to just download anything anyone tells them to.

    Example 2: Outside of tech, take the case of playgrounds. In "the old days," as a kid, you could go to the playground and slice your hand on a rusty part, or fall 20 feet from a climbing cage. New regulations made playgrounds ultra safe, all plastic bits, no sharp edges, nothing high enough that you could fall and break a bone. Not only does this create a false sense of security since you can still fall and break something from ground level, it also takes away the opportunity for kids to learn how to evaluate risk.

    Since things can never be 100% safe, it's important for people to also consider the possible outcomes of doing something. Ensure they consider what can happen if they grant an app access to their photos. Think about why a dialog is asking for their password. Teach them to manually save documents as they're working, even with auto-save, sometimes things go wrong. Teach them to make backups on a regular basis, "just in case". Funny story: I wrote the first draft of this post on my iPad in a beta version of an app. When I came to proofread the next day, the beta had expired and the app was no longer in TestFlight. I'd been copying it into Notes every hour or so "just in case" and well, case happened.

  2. The App Store itself does not add to the security of iOS devices. Security is provided by various technical means such as user permissions, sandboxing, certificates, kill switches, etc. Some would include App Review in there, but that system is fallible, as we've learned.

  3. Different things require different levels of security. For example, personal information such as your name and government identifying numbers require the highest level of security. Photos, contents of emails, contacts etc. require a high level of security, but not quite as high. Payment details (credit card numbers) would be next in line requiring a medium level of security. The reasoning for this is that if someone compromises your government ID, they could cause all kinds of irreparable damage, but if someone gets your credit card number, it's mostly just an inconvenience since fraud happens so often the credit card companies have systems in place to reverse the damage. After all you're giving your credit card number to pretty much every company you buy stuff from online. Not to mention your physical card broadcasting the info to anyone with an RFID reader in their pocket.

  4. Apple isn't the only company you can trust with your credit card number.

  5. Everyone involved in the app ecosystem wants a fair system. Companies or individuals that want to leech off the work of others without contributing something back are not worth the time to consider.

  6. No company should be able to dictate if another company can sell their product/service. That's a job for governments.

  7. Apple is not a government.

  8. Apple builds iOS and the API used by developers for themselves. This isn't something they've built as a service to developers and for which they need to be compensated. They built iOS and its API's before they ever considered an App Store and allowing third party developers to build apps for the platform. The concept of the App Store was originally implemented by jailbreak users in the iPhoneOS 1.0 days called Installer.app (Wikipedia).

  9. Apple means well, but might be too over-protective for their own good.

My Personal Notes

Second, let me state that my personal opinion is that Apple's fee of 30% is not a problem in itself. The problem with the 30% fee is forcing it on developers and not allowing them a choice of service provider. Apple seems to truly believe they're offering value for that money, so opening things up gives them an opportunity to prove it.

More than that, my biggest complaint with Apple is the power they have to decide if another company should be allowed to provide their product/service. They are able to block any app that competes with them (now or in the future), is innovative in any way Apple hadn't considered, or that goes against their values. Apple shouldn't be allowed to project their values onto their customers. If their customers want porn apps, so long as they're legal, they should be able to buy and install them. If customers want to run an app that devours their battery, they should be allowed to do so. It's important to realize that Apple saying "Company X can't sell Y" is the same as saying "Customer Z can't buy Y even if they understand the implications".

The Proposal

With all those points out of the way, here's my proposal for a new App Store model that aims to solve most of these problems.

Apple keeps App Review in place with some changes. Apps are graded into quality tiers:

  1. rejected: illegal — this one will need to handle various jurisdictions
  2. rejected: malware — attempts to circumvent device security etc
  3. accepted: excluded from App Store — low quality/goes against Apple's values/competes with Apple/whatever else
  4. accepted: allowed in App Store — high enough quality to be promoted in the App Store

The key difference being that Apple accepts anything that isn't illegal or a valid security issue, but not every accepted app gets listed in the App Store. An app that has been accepted, but excluded from the store can be installed by a user that has a direct link provided by Apple upon approval. Side note: this gives Apple a great opportunity to optimize the App Store since they can remove the millions of junk/neglected apps and only present the best apps to users.

Next, Apple allows alternate store fronts, I'll call these Store Front(s) as a generic term to differentiate from Apple's App Store. These are apps that act as alternate stores users can use to find and install apps. They can include search, categories, editorials, or none of these, it's up to that store runner and how they think they can best serve their users. Store Fronts can list apps that are included, or excluded from the App Store. When a user installs an app from a Store Front, it uses Apple's API to install the app from Apple's servers.

Note, so far, all of this is possible with today's tech already in iOS. Store Front would be just like TestFlight, installing apps securely from outside of the App Store.

Handling payments in Store Front would be something new. While I assert above that Apple isn't the only company it's safe to give your credit card info to, let's stick to exclusively using Apple's payment system in this first step forward. When a user installs a paid app, it still triggers Apple's payment system, same as now, and calls back into the Store Front app with a success or fail response if the purchase (and install) was successful. When the app is installed (paid or free) from a Store Front, the receipt records which Store Front was used in order to handle commissions for the initial sale, plus any future IAPs.

So how is the money split in this new system?
  • 3% Payment Provider (always Apple in this first phase)
  • 7% Apple (covers platform/review/distribution costs)
  • 0-20% App Store or Store Front

The 0-20% for the Store Front is variable and is set in a new section of App Store Connect. App owners will have to authorize whether a specific Store Front is authorized to sell their app(s) and for what % range. A Store Front can use the commission % to compete with other Store Fronts. A range can be set for each store to allow for deals like a featured listing earning the store 10% while a standard listing nets 5% or something similar. Apple should also implement a range and earn a higher percentage for featured listings over a search result. Of course, an app owner can also elect not to have their app listed in the App Store if they choose.

Regardless of which Store Front makes the sale, Apple will process the payment and will split the proceeds from the sale according to the agreed %'s. Apple pays out commissions to the Store Fronts similarly to how they currently pay developers.

So what does all this accomplish?

It solves what I feel is the biggest anti-trust issue with Apple where they can prevent new innovative ideas from being explored.

It maintains all current security measures including user permissions, sandboxing, certificates, and a kill switch (including the problems associated with that).

It enables Apple to continue to earn 30% of sales they facilitate through the App Store.

Customers can still make purchases easily with a single Apple ID.

It allows third parties to create new innovative/curated Store Fronts and earn a commission for sales they facilitate, while still paying Apple a fair cut.

It allows developers to self-promote their apps and save on their commission costs, dropping it from 30% to 10% when their marketing creates the sale. This in turn can revitalize the decimated App Review sites since developers might actually be able to afford to buy online ads and sponsorships again.

What doesn't it do?

It doesn't solve the issue of free apps being able to use all the same development and distribution tools that Apple provides without contributing to those costs. For that, I'd like to see a per user, or per download (perhaps based on file size) cost that is paid by the developer of the free app. If it's $0.25/user for example, that should be a bearable cost (part of the marketing budget) for that company. But this needs to be explored in a whole other post.

It doesn't solve the issue of allowing alternate payment systems. As I stated earlier, this is a first stage. By separating out the payment provider and Apple platform commission %'s, I've opened the door to allow other payment systems later. The hard part is going to be managing the split of the proceeds if a different payment system is used. I also believe that if the payment % is dropped to 3% as I've done here, there's less of a reason to want to use an alternate payment system anyway. Except for the next point...

It doesn't solve the issue of developers not knowing who their customers are. Which an alternate payment system could help with. But if a developer really wants to know who their customer is, they can just ask in the app via an account system. If the user consents, they can supply their info. That feels like a fair way to handle it. Forcing a user to disclose their real identity just isn't cool in today's world.

Bonus notes:

  1. One implementation detail to note: when a user buys an app from a Store Front, it would still show up in their normal 'Purchase History' where they can reinstall just as they can do now. It would list the name of the original Store Front, but they wouldn't need to go back into that app to reinstall since it would be possible that Store Front has closed.

  2. I've written this with Apple in mind, but I believe the same system can and should be implemented by others in the industry, including the game console makers.

  3. While Apple's % take will drop in some cases by implementing this system, I believe they'll actually make more money in the long term. Their devices will become even more powerful as new innovative apps are released for them. Fewer developers will be pushed toward making web and/or Android apps, or pushing customers to make their purchases outside of their apps.

  4. I wonder if Apple feels, even if they want to reduce their fees, they have to fight this battle in court and be forced to make any changes in order to avoid being sued for breach of fiduciary responsibility to their shareholders? IANAL!

  5. Everyone always cites the 30% number. But it's actually higher than that in a lot of cases. On top of the 30%, developers need to pay $100 USD annually for their developer account. They must buy Mac hardware because Apple's rules state all apps must be built on Apple branded hardware. But the biggest hit here are Search Ads. Developers often have to bid on their own app name and pay Apple extra $ just so their app comes up first in the search results when someone specifically searches for it. When Search Ads were first launched, I tried them out and all it did was drive my 30% fee up to 90+%.

  6. I can't wait to see some of the really cool innovative apps that will come out. Even simple things like a third party phone dialer could lead to new ways of doing old things.

Addendums:

  1. 2020/08/25 11:30am: Dave Murdock suggests Store Fronts would need to go through App Review as well. And yes, agreed, they're apps and so each update would be reviewed just like other apps. Further, I envision that in order to submit a Store Front, you'd need to be approved with a new type of developer account with it's own agreements, and most likely an additional fee, similar to Enterprise Developer Accounts.

2020 WWDC Security Wish List

WWDC 2020

We're hours away from the 2020 WWDC Keynote. Over the last week there have been tonnes of conversations about Apple's policies and while I'm on the side of change, this post isn't about that.

I've been compiling this list of security features that iOS needs for probably more than 5 years now, and every year around WWDC time I plan to publish it, and never actually get to it. Today, that's changing.

Here's my list of security features I'd love to see in iOS sooner rather than later.



1. Increased user control over device locking

There are multiple features that need to be added here.

a) A system level method that allows the user to lock the device underneath the currently open app. This means, keep the current app open and accessible, but the rest of the device is locked. You can't swipe to another app, you can't go back to the home screen, you can't tap on a notification and have it switch apps, in fact, if you have content in notifications hidden while the device is locked, incoming notifications in this mode would also be hidden.

Why add this feature? Because many apps used today require the app to be open for an extended period of time, so using those apps increases the risk for the user.

Examples:

Games (Pokemon Go): You need to keep your device open while you're walking around looking for Pokemon. If someone jumps you and steals your phone, it's unlocked and they have full access, just because you were playing a game. Of course, other games require you to keep the phone on because you're actively using the screen.

Sleep Trackers: There are apps that you leave open and running next to your bed while you sleep. They listen for your movements, snoring etc, to track your sleep. Doing this however leaves your phone unlocked and exposed for hours at a time while you're asleep. Ignoring people in your home that may exploit this situation, there's always the possibility of a thief (or even law enforcement) breaking in.

Video apps (Netflix etc): Want to watch the latest movie on your phone? That will keep your device unlocked for around 2 hours. Same risks apply, you could fall asleep, have someone grab the phone out of your hands etc.

Grocery Lists: In the age of COVID when we're all wearing face masks whenever we leave our homes, unlocking your phone becomes extra tedious. It's not unusual to disable phone locking while grocery shopping so you can constantly refer to your list without entering your passcode hundreds of times.

COVID Bluetooth trackers: Since Apple has blocked background Bluetooth access, several companies are releasing COVID Contact Tracing apps that use Bluetooth, but require the app to run in the foreground. Again, a serious security risk.

Allowing the user to lock the device underneath the current app solves these problems. The user can keep using the app in question, without risking the security of the rest of the device. This can be done with a gesture each time to trigger that you want to lock the device, or it could even use a timer that just auto locks the device under any app that is in the foreground for a specified time.

b) Allow a user to specify certain apps that can be used even if the device is locked. The UI for this feature would likely add those app icons to the lock screen so you can just jump right into them, locked or not. Same cases above are solved.

c) Use the Apple Watch to automatically lock your phone. If your phone moves too far away from your watch, auto-lock it. This handles cases where someone grabs the phone from your hand, and also cases where you leave the phone at your desk when you go to the washroom or something (back in the days when people went to offices). A bonus would be if you could disable Touch/Face ID via your watch.

If I can only have one of these security improvements this year, please let it be this one.



2. Improve the 2FA used on our Apple accounts

Apple's 2FA is one of the worst available. It's only better than systems that still use SMS for the second factor.

Ideally they allow you to store the key so you can use any standard 2FA app. At a minimum, they need to fix the geo-location on alerts. Telling me someone 150 kms away is trying to log into my account when it's really me on another device right next to me is pretty pointless. At least show me the IP address that is being used, and if it happens to be the same IP as the device you're showing me the alert on, tell me that too.



3. Secure the password dialog boxes used for our Apple accounts

The system can ask you for your iTunes/iCloud credentials at any time. This can happen while you're in the settings, the App Store, or even a random third party app. And the dialog is a standard dialog that any app can present. Most users use the same email address for their Apple ID as they do to log into apps, web sites, etc. This allows for unscrupulous apps to phish the user and trick them into giving up their vital Apple account password.

It is possible for advanced users to distinguish the difference between a dialog Apple is presenting and one presented by an app (swiping up on a system dialog is disabled), but try explaining that to normal users, never mind actually expecting them to test every time they're presented with a password request.

A simple solution would be to have a uniquely customized dialog box when the system is asking for your credentials. This unique dialog would not only include the email address of the account in question, but would display a secret image or pattern that was pre-selected by the user when they created their Apple account. This would need to be added to existing accounts during their next upgrade process.

Current Dialog   >   Suggested Dialog



4. Multiple users (including a guest account) on iOS

This is a simple one, and pretty self explanatory. Often someone wants to 'just borrow your phone for a sec'. A guest account with access to non-sensitive apps would make it easier and less risky to help someone out.



5. Improvements to Touch/Face ID

Add other options to the "Require Passcode" other than 'Immediately' when using Touch or Face ID. I've been asking for this change since Touch ID debuted, mainly because when debugging in Xcode, it's really annoying having to constantly unlock the phone while you're trying to install the newest build. You'd unlock the phone, build & run, then the phone would lock before the build started and you'd have to unlock it again.

With COVID, this is an important feature for people that aren't developers. See above for the grocery list scenario. When wearing your mask, being able to enter your password only once every 5-15 minutes would be a huge benefit.

No choice!



6. Atomic app upgrades

When apps upgrade, it should be an atomic process. I've seen cases where app A is installed and working, then it upgrades via the App Store, but the network drops during the upgrade process. The app becomes unusable now. You can no longer access the data until the system completes the upgrade process.

Granted, listing this as a security fix is a bit of a stretch. But one of the times I saw it happen, it was 1Password that became unusable. I consider not having access to my passwords a security issue.



7. Medical ID

Users should be able to add photos to their Medical ID profile. This could include QR codes, scans of their hospital cards, insurance information, scans of medical history, prescriptions etc.





Acknowledgements:

My thanks to Markus Winkler at Unsplash for providing the photo used as the sample security image in the updated password dialog box.

Introducing OTAgo, an OTA app distribution system

OTAgo

Over-the-Air (OTA) app distribution is one of the methods Apple provides that allows you and your users to securely install iOS apps on devices. Other methods you've most likely seen and used are directly installing the app via Xcode on a device in your possession, TestFlight (Apple's beta distribution system), and of course via the App Store.

Each of these methods has their purpose.

  • Direct via Xcode: Debugging and initial testing
  • TestFlight: Beta testing
  • App Store: Distribution to customers

So when is the OTA method needed?

Not every app can be distributed via the App Store: In-house apps for your staff, apps that Apple may not approve, or custom apps for your business customers that need to be distributed via Apple's private B2B store.

If the app you're building can't be distributed via the App Store, you're unable to use TestFlight for beta distribution either. OTA is a great way to distribute beta versions, and/or release builds for these apps.

Should you use OTA to distribute your apps?

Most likely, no. If you can use TestFlight and the App Store, use those. If you're building enterprise apps, or have a very early build that you can't get approved for TestFlight distribution yet, then OTA may be for you.

Why use OTAgo?

Setting up an OTA distribution system isn't very difficult. When you use Xcode to build your .ipa file, it gives you an option to create a manifest.plist file that's required for OTA distribution. You can basically drop that manifest.plist and your .ipa on your web site and set up the appropriate links. However, doing it this way, doesn't give you any protection and anyone that finds the link can install your app.

You can put the link behind basic authentication using Apache's .htaccess, or similar via nginx. But since iOS 13, using basic authentication requires the user to enter their credentials 3 times each time they install a build.

See @GeekAndDad's tweet here:

You might be thinking, let's just use an obscure link no one will find, and we'll rely on security by obscurity. This of course is never a good plan, with search engines and malicious web spiders, your hidden link is unlikely to stay hidden.

On top of that, Apple has a new requirement that's coming into play in 'Spring 2020'. Due to rampant abuse of Enterprise accounts being used to distribute apps outside of the App Store, Apple is cracking down and now asking developers using an Enterprise profile how and where the app will be distributed. They're requiring developers to use a secure authentication method. This means either username/passwords or a restricted network accessible only via VPN/Intranet. See a screenshot of the current settings (note you'll only see this in your developer account if you're using an Enterprise account):

Screenshot from the Dev Portal

OTAgo handles the secure authentication for you, and it does it in a way that works around the requirement to enter a username/password 3 times. I've designed it in a way that it should be easy to set up and configure. The initial version includes a `simpleAuth` mechanism that lets you authorize users as simply as providing a list of username/password pairs.

I've also made the authentication system pluggable, so if you want or need to link into an existing authentication mechanism, you can do so by adding in your own plugin. If OTAgo proves to be useful/popular, I'll likely add some additional authentication methods, OAuth, MySQL/MariaDB etc. Of course feel free to send pull requests with additional ones. :)

You can check out the project here: https://github.com/DaveWoodCom/OTAgo. Let me know what you think. If you find it useful, please star it on GitHub!

Acknowledgements:

My thanks to Freepik at flaticon.com for providing the koala used in the OTAgo logo.

Also thanks to Paweł Czerwiński on Unsplash for the background of the banner above.

Solved: My Time Machine and Catalina Issues

Catalina

In a hurry, read the TLDR version below...

Quick summary of the problems I've been experiencing, in case you hadn't seen me ranting on Twitter...

1) Since upgrading to Catalina, I've been unable to complete a single Time Machine backup. Time Machine would continually try to backup, but after days or even weeks, it would eventually fail and I'd have to start it over.

At first I was using an existing Time Machine backup from Mojave. After that failed a few times, I decided to wipe it and start from scratch. Even after starting from scratch, the Time Machine backup would still take forever and then eventually fail. I joked on Twitter... that I was going to post screenshot updates each day showing the progress, but that it would end up being a 6+ month project.

Instead, I filed a FuBAr1 (FeedBack Assistant/radar) and sent Apple some sysdiagnostic and tmdiagnostic files.

2) The second problem I was complaining about was due to my continuously filling SSD that wouldn't give up space no matter how much I deleted from it.

Of course, I was blaming APFS and Time Machine for this, since it's well known now that APFS snapshots are used to keep data available until Time Machine can back it up. I've been using tmutils to delete these snapshots when I needed to free up space, but even after deleting the snapshots, I wasn't getting my free space back2.

I received feedback from my report I'd sent to Apple (Yay, they do work!). They pointed out that the issue was due to a third party app I have installed called Disk Drill by CleverFiles (which I have as part of my Setapp subscription). The app has a "feature" called Guaranteed Recovery that is supposed to help recover files later if you accidentally delete them. It "works" by creating thousands of hard links to what appears to be every file on your system in a hidden directory (/System/Volumes/Data/.cleverfiles/). This means when you delete a file, it's not really deleted because there's a hard link effectively creating a duplicate in the hidden folder. Personally I feel this is a terrible feature since you should have proper backups of your data anyway, so when you delete a file, it should be deleted. (Note: I originally installed Disk Drill in order to attempt to recover images from an SD Card that died, and so had no idea it was doing anything wonky to my main drives).

Over the last month or so, I've been trying to free up space on my main SSD because the OS is constantly complaining that my drive is full. Because I kept getting alerts that I needed more free space, I kept moving/deleting files. Eventually I'd cleared/off-loaded over 500G of data and was still scraping by with about 30G of free space. Now that I'm aware of the issue, I've taken a look into the .cleverfiles hidden folder on my iMac and I see it has over 450G in it. Wow.

First thing, add /System/Volumes/Data/.cleverfiles/ to the Time Machine exception list. This should fix the first issue with Time Machine taking forever to backup. Though, if you are using an existing Time Machine backup, it can still take a long long time for the next backup. My MBP has been working on it's first backup since I added the exception for 2 days still, and it's completed 150G of 177G. I created a brand new backup on my iMac and it completed it's first 650G backup in a day, so I'd recommend just wiping your old Time Machine backup and starting fresh (unless there's something you really need in the old backup, but then, why not restore it first, then wipe etc). Wait until after you read the rest of this post before staring a new backup though.

Next, I went into Disk Drill to disable to the Guaranteed Recovery feature. I don't need it, and think it's a flawed idea. While looking at how to disable it, I see it has a setting for how big the "Guaranteed Recovery Storage" should be. It's set to 8GB by default. Recall my .cleverfiles stats it has >450G. A lot more than 8G. That limit is supposed to be the limit of files hard linked but deleted. But it appears there's a bug in the app because it hasn't been trimming down to the 8G limit (perhaps there's a Catalina issue Disk Drill is hitting that prevents the trimming, and that's why this only started getting really out of hand after I upgraded). I didn't bother digging into this since I'm disabling the feature anyway. After disabling the feature, and "resetting" the storage, I expected the .cleverfiles folder to be removed or at least cleared. It was neither. Perhaps another issue due to Catalina. (Note that Disk Drill does have Full Disk Access permissions on my machine).

I went back into Disk Drill, and it had re-enabled Guaranteed Recovery on each of the drives I had just disabled it on. Another bug? Not sure, no time to dig into it, so instead, I uninstalled the app from my machines. Note that you can't just delete the app, you need to follow the full uninstall instructions here (modified slightly if you have the Setapp version): https://www.cleverfiles.com/help/how-to-uninstall-disk-drill/

I then manually deleted the /System/Volumes/Data/.cleverfiles/ folder.

One issue is that I can't remove a folder: /Library/StagedExtensions/Library/Application Support/CleverFiles due to it being read-only in Catalina. I supposed I could turn off SIP and delete it then, but I try not to do things normal users won't/can't do in order to keep my system consistent for testing.

After deleting .cleverfiles, finishing a full Time Machine backup, and ensuring there are no remaining APFS snapshots, I now have 593G free! Subsequent Time Machine backups are completing quickly again, and I expect my whole machine will start behaving better again (time will tell).

My thanks go out to Apple staff who responded to my report and helped restore (some of) my sanity! Hopefully the developers at CleverFiles can fix the issues they have in Disk Drill.

TLDR: It wasn't Apple's fault. A third party app, Disk Drill by CleverFiles was causing the issue. It was also causing the issue that prevented hard drive space freeing up as I deleted files. The apps not so clever feature was creating hidden hard links to basically every file on the system so you could recover it later in the odd chance you accidentally deleted a file.↩︎

Footnotes:

1: I'm still trying to make this nickname for the new Feedback Assistant reports stick, but I'm probably the only one. 🤪↩︎

2: I had used Daisy Disk to try and locate the missing disk space, but it didn't help find them. It did have a huge chunk (4-500G) marked as hidden, but didn't break down the location.↩︎

Why did I reserve a Tesla Cybertruck?

CyberTruck

Abbey Jackson messaged me on Twitter:

I started to reply with a tweet, then figured it would take a few tweets so started a thread, then realized this needed to be a full blog post...

First, note I'm not a truck guy. I'm an SUV guy. But I appreciate how useful a truck can be when needed (I've borrowed my bro-in-law's F150 on more than one occasion). The Cybertruck looks like it will feel more like an SUV than a truck, and actually, I could see it shipping with an SUV option at some point.

Stainless steel is awesome. I've always wanted a DeLorean even thought about electrifying one as a side project at some point. The strong panels are a major bonus. My Bolt was dented when ice slid off my house and hit the hood during last spring's thaw. Over $1000 in damage. Also, I'm paranoid about a-holes keying Teslas. Granted there's a low probability of it happening, but I figure if it'll happen to anyone, it'll happen to me (I'm just that lucky).

The transparent steel windows look great (regardless of them failing during the demo). Making it harder for thieves to smash and grab will be a benefit since Telsas are often targets. Impressive to see a company actually use transparent metal too, it shows they're reconsidering everything, even if it's already "a solved problem".

Then of course, there's the non-tech aspects. I'll admit I've always liked the Hummer. But their gas guzzling nature of course has always prevented me from really looking at getting one. It might just be me, but drivers of Hummers and similar vehicles always seem to give the impression they just don't care about the world, and almost take pleasure in actively hurting it. Almost like they're shouting: "Look at me, obnoxious as hell, f*king up the planet!". Driving the Cybertruck feels like it'll be similar to me shouting: "Look at me, obnoxious as you, but helping the planet!"

I'm always trying to convince others to go electric when purchasing their next new vehicle. But everyone always has an excuse about why they have to stay with gas, and driving the Cybertruck will be proof that most of their "reasonable" excuses just aren't valid anymore (and I'll be able to take them for a ride to demo why):

"I drive to Florida and back once a year so I need 2400 km range (cause I drive for 24 hours without stopping to eat or pee)"
  • — The tri-motor version will have 800 km range! That means you can drive to Florida (from Ontario) only needing to stop twice at a supercharger, that's less than the number of times you'd have to stop for gas.

"I have to use my vehicle for work, so it needs to be rugged, tough and able to carry all my stuff"
  • — Payload 3500 lbs! Tows 14,000 lbs! My Cybertruck will crush your puny work truck. 'nuf said.

"Electric vehicles cost too much (because I suck at math and can't add up the deferred costs of gas, maintenance, or factor in the environment damage we'll have to pay for later)"
  • — $40-70K USD! With nearly zero maintenance costs, really low fuel costs, probably cheaper insurance costs, and no damage to the climate to pay for later1.

"I'm special and shouldn't have to consider the damage I do to the world, someone else can clean up after me, gas is just easier because I already understand it"
  • — Ok Boomer. There's no convincing these people with logic and facts, I'm trying not to waste too much time and energy on them anymore.

"Electric vehicles are bad for the environment because they just move the emissions from the car to the power plant"
  • — Same with these people. In Ontario, our electrical grid is very green (about 85% emission free), and even if you live in an area with coal plants it's still better for the environment to drive electric because your total emissions will be reduced and will continue to decrease as those plants are decommissioned.

"I have kids and need a mini-van for car seats, dogs and hockey bags"
  • — Model X solves this one already, but everyone always balks at the price tag. Cybertruck will be a decent answer with a great price, but I know some of my family and friends will still swear they need a Mini-Van option. Amazed no one is making an EV mini-van yet, huge hole in the market that needs to be filled (Chrysler Portal maybe).

While I don't "need" a truck, and I'd prefer an SUV, I already have a Bolt (considered getting a second but I think I'd rather diversify), and while I'd love a Model X, I'm an indie2 app developer in a market where people don't want to pay for apps so I can't justify the cost of an X. The pricing on the Cybertruck is amazing. The dual motor Cybertruck is just over half the cost of the lowest end X. $49,900 vs $84,990 (USD for consistency).

Elon tweeted that a pressurized version of Cybertruck will be used on Mars. I’m not sure if people think he was serious or joking, but I can see it as a real possibility. How cool would it be to drive a Martian truck?

Now, will I actually buy a Cybertruck in late 2021 when they go into production? Which likely means it won't really be available until 2022 (in Canada). That's 2-3 years from now. I actually need a second EV soon since my wife's ICE car is nearing death. We're stretching it out as long as possible, waiting for either the Model Y or the Rivian R1S to ship (to Canada of course). Who knows what my situation will be in 2-3 years when the Cybertruck ships. Maybe one of my apps will take off and I can buy an X. Maybe I'll be working at Apple, Tesla or Rivian (call me 😁). Or maybe I'll be completely happy with whatever I buy in 2020. If stars align and it makes sense, then yes, I'll buy a Cybertruck once it's available. If it was shipping today, it would be on my driveway already. It's not, so the reservation will have to do. The fully refundable deposit is only $150. Compared to the $1000 deposit for the Model 3, this one is chump change. Worst case, it's an interest free loan I'm giving Telsa for a couple of years. Best case, it gets me to the front of the line when they do ship.

Update: just had an interesting interaction that might expose Elon’s hidden genius with Cybertruck. I had a conversation with a gas driver today, someone who has always resisted electrics. I showed them the Cybertruck which they hated the look of, but then they said: "did you see the electric Mustang just announced, I’d rather get that...". Suddenly the question became which electric vehicle to get, not electric vs gas. That’s a serious win!

Footnotes:

1: At some point, we're going to have to pay to remove CO2 from the air. This is a cost that will have to be paid by our governments, and everyone just ignores it and treats it as not their problem. This is especially funny (annoying) because I ran a disguised poll on Twitter asking if someone would pay extra if they caused the damage or just let the everyone else pay, and 92% said they'd pay the extra. But they never do in real life.↩︎

2: I'm also working full time for a big company again, but I'm still indie at heart. ↩︎

My WWDC 2019 Wish List

WWDC 2019


Everyone seems to have their own list of things they want to see at WWDC, so I figured I should throw mine down on virtual paper too. I'll keep it short for you and mostly just include things that aren't on everyone else's lists. [Note: You may notice this is almost exactly like my list from last year, maybe I'll get more of my wishes granted this time around.]

App Stores:

  • ✅ (Partial, tvOS is the exception, of course.) they all get the 2017 update, adding curation etc.
  • (macOS only) allows more powerful (read non-sandboxed) apps back in the store.
  • commission rate change: 5% for apps sold via a deep link, 15% for apps sold via search/browse, 30% for apps sold via curation stories/app lists/features.
  • ability for devs to merge SKUs, i.e., combine X and X Lite into one app. Any user that had downloaded either now gets the merged version and the receipt lets the dev know which one(s) the user originally downloaded. (Odds increase for this one this year, since it could help with Marzipan apps as well.)
  • ability for users to browse all stores on any device, make a purchase, and have the app installed on a different device. I should be able to browse the tvOS AppStore on my Mac, buy a tvOS app and have it install on the family room Apple TV.
  • new badges on every app that indicate features/warnings, such as: age rating, whether or not the app is sandboxed, has passed an accessibility audit, if there's a complimentary macOS/iOS/watchOS/tvOS app, is on your wish list (which they need to bring back), etc. (Hat tip for the accessibility audit idea from Marco Arment on Under the Radar) (Bonus points if there are icons that indicate third party analytics and/or similar frameworks embedded).
AppStore screenshot showing 1Password with new App Badges

iOS:

  • ability to set default apps for email, web, calendar etc.
  • add app shortcuts to Control Center. (Not quite as desired anymore since I work on Launch Center Pro, you should use that instead! 🤪)
  • better control of audio, routing and setting different volumes (ring vs media etc). Rumours suggest something is coming here, hopefully not just a UI change with the same limited functionality.
  • landscape support for Face ID. (Works for iPad Pro now, should work on iPhone too).
  • ✅ multiple faces for Face ID.
  • bring the iPad keyboard to iPhone (the swipe down on a key for the alternate version feature).
  • more granular selection of contacts to allow calls from when in Do Not Disturb mode.
  • multi-user support (for iPhone and iPad).
  • ability to block calls for anyone not in your contact list.
  • fix auto-capitalization. There are a few issues with the way iOS auto-capitalizes letters when typing. First: when iOS determines you need a capital next, you can’t change its mind, for example, let’s say you type “Hi. The dog...”, then realize the period was meant to be a comma, so you use your finger to move the cursor there and change it, then move the cursor to between the T and h, backspace to correct the capital T to a lowercase t. But no, iOS makes it a capital T again, based on the original decision, not based on the current text. Second, and even worse, if using a hardware keyboard, when it decides you need a capital next, there is no way to type a lowercase letter. Tapping the hardware Shift doesn’t undo the pre-pressed software shift. Typing Shift-Letter gives you uppercase, caps lock gives you uppercase. You have to type the letter you want twice, and then delete the first one. (It’s possible this is a bug with the Logitech keyboard I have. Update: Angelo Cammalleri reports this happens with Apple’s keyboard as well).
  • remove the stranglehold on apps, either allow distribution outside of the App Store, or at least stop rejecting apps that Apple doesn’t like. I prefer the walled garden over the Wild West of Android, but perhaps make the walls lower for legit businesses/apps, and higher for the scam apps. If Apple can’t tell the difference, let us crowd-source problem apps.

macOS:

  • the ability to lock the dock to one screen. Having it randomly fly around all my other screens has driven me nuts for years, especially when I go to click an icon on the dock and then the dock runs to a different screen so I can't click it.
  • ✅ HomeKit support
  • fix spaces: when an app has a window in a space and the app crashes, put the window back in the space when it reopens. Safari is the worst for this, I’ll have 20 windows across multiple spaces, it’ll crash, and every window moves to the current space.
  • when booting up, remember which display is where in the arrangement. This has gotten better, but occasionally it still randomly flips displays around on boot up.

tvOS:

  • a built-in web browser.
  • enable UIWebView/WKWebView in tvOS apps.
  • multi-user support.
  • for the love of all that is holy, give tvOS some reason to continue to exist.

watchOS:

  • complications that can update more frequently (1 minute intervals). Even if this requires user permission to update that often.
  • custom watch faces.
  • always on display.

Xcode:

  • plug-in system, at least restoring functionality that was lost in Xcode 8. I'd even be happy with just a way to restore colour to the console logs.

HomeKit:

  • when using automation to turn on a light, be able to turn it off after x number of hours without a second automation. Right now this feature exists, but is limited to 60 minutes. I have several lights that I turn on at sunset, and off at sunrise. They all require 2 automation tasks. Being able to say turn off in 8 hours, would simplify things.
  • display more than just ‘light’ when listing devices in the automation section.

Mac Mini:

  • ✅ (Partial) updated Mac Mini's. Maybe even a Mac Mini Pro with Coffee Lake CPUs, Dual 10 GigE ports, USB-A and C/Thunderbolt 3 ports. Up to 128 GB RAM, 4 TB SSDs. Able to drive 3 5K displays. (We got an update, nearly the exact specs I requested!)

MacBook Pro:

  • ✅ (Partial) updated, with a fixed keyboard design. Coffee Lake CPUs, Up to 64 GB RAM, 4 TB SSDs.
  • option to include the Touch Bar and the standard function keys. I feel most of the hate with the Touch Bar was not with the bar itself, but the removal of the function keys (especially the escape key). I’d buy a MBP that included both.

iPad Pro:

  • ✅ Face ID.

I really wish I could be in San Jose for WWDC this year. It's been a while since I’ve been out with my fellow developers, so you’ll have to have a beer for me. Stay safe, have fun, and hopefully I'll see you next year!


If you've found this article interesting, please subscribe to the RSS feed and follow me on Twitter, Micro.blog and/or Mastodon

It would be awesome if you'd download our newest app All the Rings. It's free and we really think you'll like it!

If you see any errors, want to suggest an improvement, or have any other comments, please let me know.

Copyright

© 2018 Dave Wood

My WWDC 2018 Wish List

WWDC 2018


Everyone seems to have their own list of things they want to see at WWDC, so I figured I should throw mine down on virtual paper too. I'll keep it short for you and mostly just include things that aren't on everyone else's lists.

AppStores:

  • they all get the 2017 update, adding curation etc.
  • (macOS only) allows more powerful (read non-sandboxed) apps back in the store.
  • commission rate change: 5% for apps sold via a deep link, 15% for apps sold via search/browse, 30% for apps sold via curation stories/app lists/features.
  • ability for devs to merge SKUs, i.e., combine X and X Lite into one app. Any user that had downloaded either now gets the merged version and the receipt lets the dev know which one(s) the user originally downloaded.
  • ability for users to browse all stores on any device, make a purchase, and have the app installed on a different device. I should be able to browse the tvOS AppStore on my Mac, buy a tvOS app and have it install on the family room Apple TV.
  • new badges on every app that indicate features/warnings, such as: age rating, whether or not the app is sandboxed, has passed an accessibility audit, if there's a complimentary macOS/iOS/watchOS/tvOS app, is on your wish list (which they need to bring back), etc. (Hat tip for the accessibility audit idea from Marco Arment on Under the Radar)
AppStore screenshot showing 1Password with new App Badges

iOS:

  • ability to set default apps for email, web, calendar etc.
  • add app shortcuts to Control Center.
  • better control of audio, routing and setting different volumes (ring vs media etc).
  • landscape support for Face ID.
  • multiple faces for Face ID.
  • bring the iPad keyboard to iPhone (the swipe down on a key for the alternate version feature).
  • more granular selection of contacts to allow calls from when in Do Not Disturb mode.
  • multi-user support (for iPhone and iPad).

macOS:

  • the ability to lock the dock to one screen. Having it randomly fly around all my other screens has driven me nuts for years, especially when I go to click an icon on the dock and then the dock runs to a different screen so I can't click it.
  • HomeKit support

tvOS:

  • a built-in web browser.
  • enable UIWebView/WKWebView in tvOS apps.
  • multi-user support.

watchOS:

  • complications that can update more frequently (1 minute intervals). Even if this requires user permission to update that often.
  • custom watch faces.
  • always on display.

Xcode:

  • plug-in system, at least restoring functionality that was lost in Xcode 8. I'd even be happy with just a way to restore colour to the console logs.

HomeKit:

  • when using automation to turn on a light, be able to turn it off after x number of hours without a second automation. Right now this feature exists, but is limited to 60 minutes. I have several lights that I turn on at sunset, and off at sunrise. They all require 2 automation tasks. Being able to say turn off in 8 hours, would simplify things.

Mac Mini:

  • updated Mac Mini's. Maybe even a Mac Mini Pro with Coffee Lake CPUs, Dual 10 GigE ports, USB-A and C/Thunderbolt 3 ports. Up to 128 GB RAM, 4 TB SSDs. Able to drive 3 5K displays.

MacBook Pro:

  • updated, with a fixed keyboard design. Coffee Lake CPUs, Up to 64 GB RAM, 4 TB SSDs.
  • option to include the Touch Bar and the standard function keys. I feel most of the hate with the Touch Bar was not with the bar itself, but the removal of the function keys (especially the escape key). I’d buy a MBP that included both.

iPad Pro:

  • Face ID.

I really wish I could be in San Jose for WWDC this year. It's been a while since I’ve been out with my fellow developers, so you’ll have to have a beer for me. Stay safe, have fun, and hopefully I'll see you next year!


If you've found this article interesting, please subscribe to the RSS feed and follow me on Twitter and/or Micro.blog

It would be awesome if you'd download our newest app All the Rings. It's free and we really think you'll like it!

If you see any errors, want to suggest an improvement, or have any other comments, please let me know.

Copyright

© 2018 Dave Wood

Mix and Match Swift 3 & Swift 4 Libraries with CocoaPods

With Xcode 9, it’s possible to mix and match Swift 3 and 4 libraries together. In the build settings for the project you set the version of Swift to use by configuring the SWIFT_VERSION setting. You’re able to override the project setting at the target level, thereby building some targets with Swift 3, and others with Swift 4.

If you’re using CocoaPods as your dependancy manager, there’s an issue when mixing and matching.

As you know, when using CocoaPods, you end up with an Xcode workspace that contains your main project, and a Pods project. Whenever you run pod install or pod update, CocoaPods will set the SWIFT_VERSION for all targets to be whatever your main project is set to, or it will fallback to Swift 3 if the main project doesn’t have the SWIFT_VERSION specified.

This means Xcode will try and compile all targets with the same version of Swift, regardless of what version of Swift is actually needed. There’s no built-in way for you to specify the version of Swift to use for each pod you’re including. There is a way for the pod maintainer to specify the version needed in the podspec (they need to set pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0' } see XCGLogger.podspec for an example), but I’ve found it’s rare at this time for it to be set (hopefully this post will help change that).

Even if the pod sets the version of Swift to use, we run into a problem when Xcode resolves the setting. Xcode will prefer the target’s direct setting over the podspec’s suggestion, and since pod update always sets a direct setting on the target, the pod spec’s suggestion is never used (not surprising it’s rarely set).

The solution is to add a post_install script to the end of your podfile:

Let's examine this script.

It’s a post_install script so CocoaPods will execute the script after is has updated all of the included pods and updated the project file.

The script starts by looping through the build configurations of the Pod project and sets the default Swift version to 4.0 (lines 2-5).

Then it loops through all of the project’s targets (lines 7-19). It checks the target name against a known list of targets (line 8) and sets each of the configurations for matching targets to Swift 3 (lines 10-12). If the target isn’t in the known list, the script unsets the Swift version (lines 15-17), which will allow the pod to set the version itself using the pod_target_xcconfig setting we noted above. If the pod doesn’t set the version, Xcode will use the default Swift version we set at the start.

You will need to tweak the script for your project, specifically to set your default Swift version, and then to add the targets that require a different version on line 8.


If you’ve found this article helpful, please subscribe to the RSS feed and follow me on Twitter

It would be awesome if you’d download our new app All the Rings. It’s free and we really think you’ll like it!

If you see any errors, want to suggest an improvement, or have any other comments, please let me know.

Proposed Affiliate/Developer Commission Changes

Big news from Apple today:

Starting on May 1st 2017, commissions for all app and in-app content will be reduced from 7% to 2.5% globally.
Screenshot via @drbarnard on Twitter

Apple is reducing commissions paid to sites who promote our apps by 64%! That's a huge cut and they're only giving everyone involved 7 days notice.

The app economy has been tanking for the last few years. Apple must know this by now, even though they tout how great it is (maybe it is for a few big companies such as Niantic Labs, Nintendo etc). Personally I think this change in commission rate must be part of something larger, aimed to help rejuvenate the ailing app economy.

Back in June, 2016, Apple announced the first change in the percentage developers pay Apple, dropping the 30% rate to 15% but only for those using subscriptions, and only after a customer has been a subscriber for at least a year. For the most part, this would only have helped a few developers so far, since only a limited number of developers were even allowed to use subscriptions until the June changes. Those would be the Netflix/HBO type apps that are worth billions and don't need the extra help.

What I'd like to see at WWDC this year, is for Apple to announce they are finally reducing the 30% rate we pay to something more reasonable. Let's say 15%?

Assuming that's the plan, how about this as a proposed alternative? Instead of dropping the rate to 15% across the board, Apple could drop the rate to 15% for apps installed organically, and 20% for apps installed through an affiliate link. That extra 5% could then be paid to the affiliate who earned the sale. As a developer, I'm fine with that since it only helps those who help me.

I feel this would be a win-win for all involved. Developers get a much needed drop in their commission rate. Promotion sites such as iMore, touchArcade, etc will get a small bump instead of a drastic cut in their earnings, and Apple gets to keep the new 2.5% affiliate commission rate. I know dropping the developer rate isn't ideal for Apple, but it would make a big difference for the people who help keep their devices in demand.

What do you think about this proposal? Please @ me on Twitter and let me know your thoughts.

It’s Time to Transition from the App Store to the App Mall

With the recent announcement of some App Store changes, and WWDC just days away, I figured I’d better write about an idea I had before it’s too late. I’ll keep this much shorter than the version that’s been floating around in my head.

I would suggest that Apple release their grip on the App Store, and start allowing other stores on iOS/tvOS which would, essentially create an App Mall. Open it up so that anyone can create a store. These will be distinct apps developed like any other third party app, clearly branded to avoid user confusion with Apple’s App Store. I envision stores created by brands you already know: TouchArcade, 148Apps, AppShopper, Google, Microsoft, Panic, OmniFocus, RelayFM (for sponsored apps) etc; as well as new ones that will appear.

These stores would be akin to radio stations. If a person likes Rock and Roll, they tune in to a Rock and Roll station. If they prefer Jazz, they listen to a Jazz station. Every once in a while you listen to something different. We’ll have stores that focus on pro apps, stores for games, a store for writers, developers, parents etc. Users will come to know and trust the curators of their favourite stores. This plan delegates some of the curation of apps out to the community where it can be handled much better (just because of sheer numbers). It doesn’t take away from Apple’s App Store curation, rather it enables a method to better group apps and aid with app discovery. Instead of trying to fit 2+ million apps into 25 categories, there will be another layer on top to help sort.

One huge side effect to this plan is that Apple would have more control over it’s own App Store. They will be able to delist a tonne of bad apps, and stop adding new bad apps by raising the criteria that allows apps to be listed in the official App Store. If an app is ugly as sin, riddled with spelling errors, etc, they can refuse to include it in their store, just as Saks Fifth Avenue can refuse to stock substandard products. Right now, Apple has a set of rules, and if your app follows those rules it should be allowed in the store. Ugly apps should never be featured anyway, but they still come up and clutter the search results, they still show up in the “Customers Also Bought” section. With my new plan, those apps won’t show up at all. It’s my belief that Apple has to generally accept any app that follows their rules, or else they’ll start to run afoul of anti-competition laws. Since there is no other way to sell apps to users with iOS/tvOS devices than through the Apple App Store, if they reject apps based on religious beliefs, politics, bad UI, etc, they are preventing other companies from operating, and could get into trouble.

It would sort of be like Panasonic selling a radio, and then saying no Justin Bieber songs can be played on them. How long would it be before Panasonic was dragged into court by the Department of Justice? So my point here is that because Apple would be allowing developers to list their apps in other stores, they’ll be free to be more selective in their own store.

None of this affects app review, signing, pricing or privacy BTW. All apps would still go through review (though it would be more for weeding out malware or buggy software). Apps would still be signed by the developers and installed from Apple’s servers. Just as the TestFlight app can install apps that aren’t in the App Store, third party stores would also be able to use an API to trigger app installs (securely of course, apps wouldn’t be able to install other apps without the user’s explicit permission). The price of an app would be the same, and the payment would still be handled by Apple. So privacy is preserved as Apple would still be the only one to know who the customer is. Apple could still take their 30% (or now 15% in some cases, hopefully more cases soon). The third party store developer would be compensated via the already existing affiliate program. Or depending on the store, they may charge the developer for a listing, just as grocery stores charge food producers for the valuable space on the end of the aisle.

The goal of this idea is to help with app discovery. By opening up the App Store in this way, it empowers the developers in our community to help solve this major problem that’s really hurting the platform, without compromising the security or privacy of the platform that users have come to expect.

Copyright

© dave

The First Essential Swift 3rd Party Library to Include in Your Project

As we all scramble to learn this fantastic new language Apple gifted to us at WWDC 2014, we're coming across new ways of doing things, either because the new way is better, or because the old way is no longer possible.

One of the main features that Swift has taken away, is the C preprocessor. That's what enabled #define's to work. A common #define used is for debug logging, to include useful info with every line.

#define DLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__])

This lets us go from this:

NSLog(@"Simple Message");
2014-06-08 05:38:54.649 TestApp[35062:60b] Simple Message

to this:

DLog(@"Simple Message");
2014-06-08 05:38:54.649 TestApp[35062:60b] -[TSTAppDelegate application:didFinishLaunchingWithOptions:](0x10961f2e0) Simple Message

We now get a lot more info in our log messages and can see where in our code the log message came from without having to type it in for every line.

In Swift, we lost this functionality. The reason this was traditionally done with a #define, and not a function or class, is so that we can use the __PRETTY_FUNCTION__, __FUNCTION__, __FILE__, and __LINE__ macros. The preprocessor replaces those macros with their actual values. For example, __FILE__ will always contain the filename where the macro is placed for example. If you were to use them in a logging function, the macros would always contain the information of the logging function, not the calling function, rendering them useless.

This looked like it was going to be a major inconvenience in Swift so I filed a radar about it: http://openradar.appspot.com/17170702. After playing with Swift for a while, I've discovered a solution. I've built a library you can use in your projects.

Introducing XCGLogger

XCGLogger is my first open sourced third party library, that I think will be essential to add to your project.

The source can be found on GitHub here: https://github.com/DaveWoodCom/XCGLogger, with basic instructions on how to use it.

At a glance, it will change your logs from this:

Simple message

to this:

2014-06-09 06:44:43.600 [Debug] [AppDelegate.swift:40] application(_:didFinishLaunchingWithOptions:): Simple message

By writing code like this:

log.debug("Simple message")

instead of this:

println("Simple message")

A few things to note:

  1. Swift is brand new and in a state of flux, so it — and any libraries using it — will be subject to frequent changes.
  2. This is my first released library, so please let me know what you think and please share any ideas for improvement.
  3. There is a bug in Xcode 6 when using __FUNCTION__. XCGLogger uses a workaround for now, but will remove that once the bug is fixed. See: http://openradar.appspot.com/17219684
  4. This library is intended for use in Swift projects. There's no Objective-C compatibility included in it at this time. If it's a requested feature, it can be added.

Since Swift is brand new, there are a lot of different ways to accomplish the same thing. Over time, some best practices and patterns will emerge. I've used what I think are good practices and patterns in this library and hopefully they'll be helpful for developers as we work to establish what's best. For example, how to store static tokens for dispatch_once calls, shared instances, and global variables etc.

How does this work when I said earlier that using __FUNCTION__ and its friends in a function only gives you the information in the function instead of where it's called? Well, that's the secret sauce I discovered last week. If you set the default value of a parameter in a function to one of the macros, the compiler fills in that value at the calling location giving us the effect we need. Giving us access to the correct macro values inside the function that's called.

If you find this library helpful, you'll definitely find these other tools helpful:

Watchdog - monitors Xcode® and automatically cleans up stale cache files

Slender - cleans up Xcode projects, removing duplicate and/or unused assets

Briefs - powerful app prototyping, lets you and your clients try before you build

Follow me on Twitter @DaveWoodX

Copyright

© sabby

Every iOS and Mac Developer Needs a Watchdog

Today, Cerebral Gardens introduces Watchdog for Xcode. Watchdog is a helpful utility for iOS and Mac OS X developers that monitors Xcode cache files (DerivedData) and cleans out stale files before they interfere with your builds.

If you’ve been building apps in Xcode for a while, you will see the value in Watchdog instantly as you are familiar with the weird errors that can happen with Xcode. If you’re new to using Xcode, you may not have run into these issues yet, but eventually you will and that’s when Watchdog will save immense time and frustration.

A Watchdog user will no longer see these weird issues:

  • Old images that you've replaced, still showing up in your app.
  • The DerivedData folder growing continuously over time, often taking up 10+ gigabytes of space.
  • Constants/Defines not updating in the app after you've changed them in the code.
  • Localization file changes not being seen.
  • Phantom breakpoints and/or breakpoints stopping on the wrong line.
  • Xcode refusing to run a build on your device, only reporting something obscure like: "Error launching remote program: No such file or directory"

Sometimes the cause is related to your version control system updating files without Xcode noticing. Sometimes it’s random. Regardless, the result is the same: a bad build, time wasted, a frustrated developer, or even worse, an annoyed customer.

These errors can be mind numbing. Let Watchdog be your guard against these errors so you never again have your time wasted.

Watchdog gives you truly clean builds, saves time, and your sanity. It guards, protects and, most importantly, prevents.

Download Watchdog for Xcode

Copyright

© sabby

App.net – Get In Early

I'm sure by now most of you have heard of App.Net. A new microblogging type service that aims to be much more. At first glance it looks a lot like Twitter, but it is much better in a few fundamental ways.

The first thing you'll notice is that you have 256 characters per 'post' instead of Twitter's 140. It is very liberating. Think in terms of 140 characters, and then actually spell out all of the words, use punctuation and clearly articulate your point.

Ok, who's kidding? That's actually the second thing you'll notice. First you'll notice that you have to pay for an account! WHHHHHAAAAAAATTTT? Pay? Money? For a social network? Aren't they all free? - No, social networks such as Twitter, Facebook, Linked In, and pretty much most of the others (excluding Ping1) are not free. You don't pay with money, but you pay with time, attention, and the loss of personal privacy. With most social networks, you are not the customer, you are the product being sold2. With App.Net, you are the customer, and so you're the one paying with money. A normal member account is $50 per year. Which, is arguably cheap compared to Twitter. WHHHHHAAAAAAATTTT?

Let's take a quick look using some rough guesstimates comparing just 1 metric that you use to pay for your Twitter account: Time. Twitter is now starting to sell a lot of ads (promoted tweets) on its network. They've even started to push into selling a high volume of small accounts to users such as you and me, not just to the big guys. They've been giving away $100 starter accounts to promote their ad service. So, here's where we start to guess some numbers, we'll attempt to guess low in order to give Twitter the benefit of the doubt.

  • Assumption 1: on an average day, you see 15 tweets that are ads (promoted tweets, spam tweets, etc).
  • Assumption 2: it takes you 2 seconds to read/recognize and discard/move on.
  • Assumption 3: your time is worth more than $20/hour.

This means, over the course of a year, you're wasting 365 days * 15 tweets * 2 seconds = 10950 seconds = 182.5 minutes = 3 hours. For a monetary cost of over $60/year. And I know my time is worth a lot more than $20/hour.

This example ignores the cost you pay with your lack of privacy; Twitter and Facebook both track you as you move from site to site on the Internet, even when you're logged out!3

Two other benefits of App.Net that I won't get into detail on are: conversation views — much improved as you can see the whole thing, instead of having it forked into uselessness. It's a platform, not just a Twitter clone. Other apps and services that aren't possible with Twitter are being built right now on top of App.Net.

So, I encourage you to get on board with app.net, own your content, be the customer, and if you act pretty fast, get the name you always wanted....

Follow me on App.Net as @davewood and @cerebralgardens.

Footnotes:

1. No one uses Ping so there's no cost in time. (back)

2. Original quote via Gruber (back)

3. Original research (back)

Copyright

© sabby

WWDC 2013: Ideas for Expansion

WWDC 2012 sold out in less than 2 hours, a record that had been easily predicted by many. Tickets went on sale at about 8:30 Eastern time and were sold out before most people on the west coast had even woken up. The demand for tickets was obviously extremely high, and the supply was limited to about 5000. How can Apple solve this for next year?

Of course, the first question is, does Apple even want to solve this. I believe they do. They have information they want to put into the hands (and heads) of developers, as many developers as possible. That's why they release the videos shortly after the event. That's why they've had the free Tech Talks in various cities. So yes, Apple does want to get the information out to many developers as they can. So it's to their advantage to increase the supply of tickets for WWDC.

One idea that often comes up when this topic is discussed is to have two events. Perhaps keep the first one in San Francisco and have another a couple of weeks later somewhere in Europe. The SF one would have the main Keynote for press, just like the present; the Europe one would skip the keynote but have all of the same sessions. This would temper the expectations of the press, who may be disappointed if the event were held months later without additional product announcements. The SF one would still be called WWDC, but would now actually be 'Western World Developers Conference', the Europe one would be EWDC, 'Eastern World Developers Conference'. The downside of this scenario for Apple is the increased cost, not just the direct costs of hosting an event in Europe, but the time involved in tying up Apple's engineers for an additional week (or two including travel/prep etc).

My personal suggestion is to keep it as one event, but increase attendance to about 10,000. The most common argument against this idea is that Apple likes to have a roughly 1:5 engineer to developer ratio, and they don't have enough engineers available to maintain that ratio if they double the developer attendance. I've only been to one WWDC, so this could be inexperience talking, but to me, the number of engineers there was almost irrelevant. Each session had 1-5 engineers on stage, but it didn't matter whether there were 500, 1000, or 2500 people in the audience, only the size of the room affected how many people could attend. Where the number of engineers matters is in the labs (which are more like Q&A sessions than labs). So I have an idea to increase the usefulness of the labs for everyone, while at the same time increasing the efficiency so that the same number of engineers can support 10,000 attendees.

The idea is to have attendees submit the questions that they intend to ask an engineer in a lab, to a special WWDC lab email address (along with their project source if applicable). These questions will be prescreened by Apple engineers (or even interns) way before WWDC. Some questions will be simple enough that an email response will be enough to answer them. For the rest, an appointment at WWDC can be scheduled with an engineer that can actually answer the question for the developer. In a lot of cases there will be duplicate questions that can be answered in a group session. This plan will reduce the need to have such a high ratio of engineers to developers while increasing the value of engineer and developer meetings. No more lining up to talk to an engineer that doesn't know any more about the problem you're trying to solve than you.

The only other logistic is how to fit an extra 5000 people ino the sessions. My plan there would be to expand to Moscone North and/or South, and make all of the rooms bigger. Same number of sessions, same number of engineers, just a larger audience. I heard developers this year talk about how long the lines were to get into each session, and that they weren't that long in previous years. But the consensus seems to be that Apple was just way better organized this year than in previous years and that the lines just looked longer than a massive mob of unorganized people. My point here is that Apple did a great job of moving the 5000 attendees around this year. If they increase the time between sessions a little, it should be possible to move 10,000 people around the 3 buildings efficiently.

Whatever the plan, I can't wait to attend WWDC 2013.

If you enjoy reading my blog, please follow me on Twitter, and/or like Cerebral Gardens on Facebook.

What a Week! WWDC 2012 Edition

This was my first WWDC, but it certainly won't be my last. It was a great experience and I'm going to try and share some of the things I learned over the last week. Nothing that's covered by the NDA of course.

1) It was great to finally meet some of the big wigs in the community. Drinking beer with Jeff LaMarche and the other MartianCraft guys. Hanging out with the Empirical Development guys that I've been working with for most of the last year was awesome. Getting to pitch Party Doodles to Eli Hodapp (of Touch Arcade) and Victor Agreda, Jr of (TUAW) in person was amazing. I'm sure it helped that Apple basically used Party Doodles as an example of how to do an AirPlay game correctly.

2) Probably the biggest shock to my system was the amount of walking involved. As someone who normally sits at a desk for 12+ hours a day, it was a major change to walk back and forth from my hotel 2 or 3 times each day. Why 2 or 3 times you ask, depending on whether or not I took my laptop to the sessions and wanted to drop it off at my hotel before dinner/socializing etc, or based on meetings with various people I had scheduled between sessions.

3) In most cases, you do not need to take your laptop with you to the sessions or labs. I had a completely incorrect assumption of what the labs were. Labs should be considered more like Q&A sessions with Apple engineers. They are not planned tutorials or anything scripted. They're just a chance to ask a question, sometimes with someone who may have helped build the system you have a question about. The only time having your laptop with you is probably if you need to show an engineer your code during your Q&A (lab) session.

4) For the labs, my own experience was pretty dismal in this regard. I had a few questions to ask about various topics, and each time, the engineer(s) I was talking to had no more information to provide on the issues. That being said, I heard of some people that had much more successful visits to the labs.

5) The actual sessions where amazing. Some covered brand new information about iOS 6 or Mountain Lion, while others covered older information that you might have missed. Sometimes you see something presented that's been available for a while that you just hadn't seen and you think "this will save me hours". When the session videos are released, make sure you watch as many as you can. Even if you think you already know about a topic. There are always extra little tips that are invaluable.

6) When you attend a session in person, please use some common decency and follow these four rules:

  1. When sitting down, move to the center of the row, don't 'end cap' the row by sitting in the first seat. Most sessions fill the entire room and when everyone has to fill in rows by jumping over a person sitting in the first seat, it's pretty annoying.
  2. Wait until the speaker has finished talking before running out to the next session. We all have to get to the next session at the same time, give the speaker the respect they deserve by letting them finish.
  3. Do not use a MiFi device. They jam the provided WiFi and in some cases prevented even the presenters from being able to demonstrate what they had planned.
  4. Take your trash with you. If you bring in a drink, lunch etc, just take the garbage with you when you leave and drop it in the garbage bin or recycling etc. I think they teach this in kindergarten but it appears some people missed that day.

7) Related to #2 above, the choice of hotel is important. The closer the better (or at least the less walking you have to do). But there are other issues. I only have experience with the one I stayed at this year, Parc 55 Wyndham, but I'm pretty sure I won't be staying there again next year. The room was nice, clean etc, most of the staff were nice and helpful. My issues with the hotel were

  1. the network is awful. Wifi or wired, it wasn't strong enough to keep iChat connections alive. And they charge $15/day ($50/week).
  2. the included breakfast only includes pastries, you can add eggs and bacon for $25!
  3. the elevators are extremely slow, taking up to 10 minutes to go up and down.
  4. the TVs are locked down and prevent you from adding your own input, no connecting Apple TV or your laptop for example. That made testing some changes to Party Doodles impossible.

8) Since I'm Canadian and our roaming fees are insane, I wanted to pick up a local SIM card in order to be able to use data whenever I needed. I have an unlocked phone so it should have been easy. Eventually I went AT&T, $50 for unlimited voice and text, and $25 for 1G that they said wouldn't work on an iPhone and that they wouldn't refund the cost if I couldn't get it to work. After putting in the SIM card, it took all of about 30 seconds to switch the APN using the site: http://www.unlockit.co.nz/. The AT&T network has been great the whole week (Keynote excluded, but nothing was working there).

9) J.J. Abrams. Wow. He was a guest speaker for the Friday lunch session. And boy was his talk amazing. For one, he was by far the most entertaining speaker of the week, granted his content makes it easier, blowing up stuff is more exciting by itself than NSManagedObjects being accessed by the wrong NSManagedObjectContext. But his way of presenting was great, it almost felt like it was just me and J.J. in the room and he was telling me stories from his life. It was very interesting to hear how certain ideas/shows came to be due to other events in his life, in much the same way we move from app to app where the first app may inspire the idea for the second app. I'd love to go into more detail here, but it seems even this talk is covered by the NDA. J.J., if you're reading this (maybe Google Alerts brought you here), I just want to say thanks for the awesome and inspiring presentation.

10) One last point. Since it was my first WWDC, I wasn't sure when I should be here, so booked my flight for Saturday to Sunday. Getting here on Saturday worked out well, gave me some time to get to know the area and meet up with people for drinks etc. But next year I'll leave Friday night or Saturday morning. There wasn't much happening on Saturday or Sunday as most people have already left.

I'd say WWDC (I'm not yet cool enough to be able to call it "dubdub") was a great success this year. I can't wait for next WWDC 2013! It'll sell out super fast again next year, so be prepared...

If you haven't already, please download my free game Party Doodles, like us on Facebook, and if you like to hear me ramble, follow me on Twitter.

Introducing Party Doodles, and the Lessons It Has to Share

Last Wednesday we released our newest game, Party Doodles. A unique picture guessing game that uses AirPlay mirroring to create a party game experience unseen before now on the App Store. It has been in the works since AirPlay mirroring was announced (yes, almost a year from concept to release).

I believe, and app reviewers (here, here, and here) seem to agree, that Party Doodles is the first of it's kind on the App Store. The tag line for the game is 'Made for iPad, designed for Apple TV' because while you can play without Apple TV, the game is really meant to be played on the Apple TV.

The easiest way to describe the game, is to compare it to Pictionary. A group of friends break into two teams, each person takes a turn drawing clues (on the iPad) while their teammates try and guess what it is (watching on the big screen TV) before time is up. If time runs out, the other team can steal the points. I added some strategy to the game by having 3 difficulty levels baked into each turn, the person doodling can pick an easy topic to win 1 point, a medium topic for 2 points, or a hard topic for 3 points. If you're behind the other team it gives you a chance to catch up, or if you're winning, a chance to get even further ahead!

The game really is a lot of fun, and I'm not just saying that cause I made it. I had people actually come over to my house and ask to play it. And it's not uncommon for players to break out into uncontrollable laughter.

Anyway, enough plugging the game, please download it and give it a try. Did I mention it's free?

On to what lessons the game has for us, as developers....

First, it's important to know that progress on the game was sometimes slow as I was working full time at a normal job when I first started working on the game. Even when I went full time indie, client projects usually take precedence and consume the majority of my time. I recently looked back at my source commits and noticed several months where I hadn't committed a single line.

A lot of things happened over the course of the development of the game. One of which was that Draw Something came out and became a massive hit. There was a definite 'Oh shit' moment at that point. But it didn't take long to come to terms with the fact that the games are significantly different. I never intended Party Doodles to have mass market appeal anyway. It's definitely for a niche market since the requirements to play properly are high; you need:

  1. an iPad
  2. an Apple TV
  3. actual friends in the same room1

The goals for Party Doodles were to create a unique experience doing something that no other app on the store was doing yet (I expect more will come along shortly), to try the freemium model out, to create a fun game that people can really enjoy playing together, and to get a little attention for the game itself since it is the first of it's kind on the App Store, not an easy feat nowadays.

I've definitely learned a lot while developing this game and plan to share a bunch of tips/tools and code over the next few blog posts. Today's tip is a short one though (since I used up so much space detailing the game in the first place).

When releasing an app with In-App purchases, make sure you test the purchase functionality with the live app from the App Store as soon as it's available. (Use a promo code to download the app after it's approved and before it's released if need be). There are some slight differences between the Production IAP system and the sandbox we can get to test with before submission. In my case, I added receipt verification as suggested by Apple (using PHP on the server, with code I'll likely share in a later post) and the verification process would fail on production requests only due to a minor difference in data sent back from Apple. That caused the first few purchases to be rejected. Not a good user experience at all! Even Apple's reviewers test the app in the sandbox and thus missed my production only bug.

Fortunately I was able to fix it fast since it was just server side code and not in-app code that needed to be changed.

Ok, lots more to come in future articles so stay tuned. In the meantime, please download the game, and like us on Facebook.

1As a french review (Google Translation) of the game pointed out, you also need a TV which they said makes my free game very expensive. I hadn't considered that someone would add the TV to the cost of my game.

Copyright

© sabby

Improving Your Image Workflow

No doubt you're excited about the new iPad being released next Friday. Me too! I've been hoping for an iPad with a Retina display since the days of the original iPad. In celebration, here are a few tips to make creating your images a little easier. These of course work for iPhone too.

{emailcloak=off}First, it goes without saying (but I'll say it anyway), you need to create the high res version of the image to start with. I use Adobe's Illustrator and Photoshop for most of my graphics. (Actually, my Wife uses Illustrator mostly, while I import her stuff into Photoshop for any pixel level tweaks and to ensure sizes are consistent etc). For the most part, that means I have images in Photoshop with one image per layer. I use one .psd file for all images of the same size in the app.

When I have a part of the app that needs multiple images, say for a menu, each menu item will be on it's own layer, if there are separate overstate images, they also get their own layer.

Even Steven

Make sure your canvas width and heights are even numbers. If you have an odd number you'll get slight cosmetic errors and off by 1 pixel effects.

Mass Export

Name the layer with the desired filename for that image. Eg: btnMenuItem1@2x etc. Make sure you include the @2x part.

Now you can use Photoshop's Export Layers as Files script to quickly create PNG's of all the images in your .psd file. Leave the File Prefix blank, and make sure you uncheck the 'Trim Layers' option which is always defaulted to on or some of your image sizes will be altered and could end up odd.

Honey, I Shrunk The Files

Next you're going to need to do some setup.

Setup Step 1: We're going to use the awesome ImageMagick tools, so you'll need to install them. The quickest way is to use MacPorts, follow the instructions here.

Setup Step 2: Copy the following script to somewhere in your path. I use ~/bin, but other options will work.

~/bin/1x (Download):

#!/bin/bash

## Copyright (C) 2012 Cerebral Gardens http://www.cerebralgardens.com/
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify,
## merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
## permit persons to whom the Software is furnished to do so, subject to the following
## conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
## PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
## OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

CONVERT=/opt/local/bin/convert
IDENTIFY=/opt/local/bin/identify

if [ $# -lt 1 ] ; then
	echo "Usage: ${0} filename@2x.ext"
	exit
fi

for ARG in "$@"; do
	FILEPATH=`dirname "${ARG}"`
	FILENAME=`basename "${ARG}"`
	FULLNAME=${FILEPATH}/${FILENAME}

	PHOTOSHOPTEST=${FILENAME#_[0-9][0-9][0-9][0-9]_}

	if [ "${PHOTOSHOPTEST}" != "${FILENAME}" ] ; then
		mv "${FULLNAME}" "${FILEPATH}/${PHOTOSHOPTEST}"
		SOURCEFILE=${PHOTOSHOPTEST}
	else
		SOURCEFILE=${FILENAME}
	fi

	IMAGENAME=`expr "${SOURCEFILE}" : '\(.*\)@2x.png'`
	EXTENSION=`expr "${SOURCEFILE}" : '.*@2x.\(png\)'`
	DESTFILE=${IMAGENAME}.${EXTENSION}
	FAILNAME=.
	BLANKS="                                                  "

	if [ "${DESTFILE}" != ${FAILNAME} ] ; then
		${CONVERT} "${FILEPATH}/${SOURCEFILE}" -resize 50% "${FILEPATH}/${DESTFILE}"

		FILENAME_LENGTH=${#IMAGENAME}
		SOURCE_WIDTH=`${IDENTIFY} -format "%W" "${FILEPATH}/${SOURCEFILE}"`
		SOURCE_HEIGHT=`${IDENTIFY} -format "%H" "${FILEPATH}/${SOURCEFILE}"`
		DEST_WIDTH=`${IDENTIFY} -format "%W" "${FILEPATH}/${DESTFILE}"`
		DEST_HEIGHT=`${IDENTIFY} -format "%H" "${FILEPATH}/${DESTFILE}"`

		echo "${IMAGENAME} - @2x : ${SOURCE_WIDTH}x${SOURCE_HEIGHT}"
		echo "${BLANKS:1:${FILENAME_LENGTH}}   @1x : ${DEST_WIDTH}x${DEST_HEIGHT}"

	else
		echo "Skipping: " "${FULLNAME}"
	fi
done

Once you're setup, and after running the export script, you'll have a series of files in the export folder with names like _0000_btnMenuItem1@2x.png, _0001_btnMenuItem2@2x.png etc. Drop to a terminal in that folder (Go2Shell works great for this). And run the script you created above for each file you've exported. You can do this one file at a time:

1x _0001_btnMenuItem2\@2x.png

or for a collection of files at once,

1x *

What this script does, is chop off the _xxxx_ part of the filename (if it's there), then create a @1x version of the image with the appropriate filename. It will skip any file that doesn’t match the filename@2x.ext format.

The script will also print out the original image dimensions along with the new image dimensions as a quick way to ensure you don't have an odd sized image. (It's ok for the @1x image to have odd dimensions).

Size Apparently Does Matter

Take a look at the sizes of your final images. Apple recommends using PNG files, but they're not always the best solution. Some images just do not compress well as PNG files and are better suited as JPG files (assuming you don't need alpha transparency). For example, an @2x image in my upcoming iPad game was over 5 mb in size as a PNG and just 305 kb as a JPG. There is no discernible difference in the way the file looks or behaves as a JPG, but 10 million users (fingers crossed) will each save 6 mb of space on their iPads (the @1x version went from 1.3 mb to 85 kb).

Get Your App To The Gym

Check out Slender in the Mac App Store. Run your project through it and it'll catch any odd size images, or extra images you're no longer referencing in your app. It can even automatically remove the extra images from your project for you (saving a backup first of course).

On a completely unrelated note, we've launched a Cerebral Gardens Facebook page. If you use Facebook, it would be swell if you'd head over there and do the like thing. Thanks!

Enumeration of NSMutableArray's Follow Up

Today I'm going to follow up on some interesting comments to my previous tip regarding enumerating NSMutableArray's.

I had suggested to make a copy of the mutable array before enumerating, in order to prevent another thread from modifying the array you're working on and causing a crash.

It was mentioned that making that copy is itself an enumeration and so we were just moving the crash point. So, I did some digging.

// array is an NSMutableArray
- (IBAction)button1TouchUp:(id)sender
{
    DebugLog(@"Populating Array");
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        for (int i = 0; i < 10000000; ++i)
        {
            [array addObject:[NSNumber numberWithInt:i]];
        }
        DebugLog(@"Done Populating Array: %d", [array count]);
    });
}

- (IBAction)button2TouchUp:(id)sender
{
    DebugLog(@"Enumerating Array");
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        DebugLog(@"Copying Array");
        NSArray *copyOfArray = [array copy];
        DebugLog(@"Done Copying Array");

        long long x = 0;
        for (NSNumber *number in copyOfArray)
        {
            x += [number intValue];
        }
        DebugLog(@"Done Enumerating Array: %d", [copyOfArray count]);
    });
}

Note: if you test this, do so in the simulator, a device will generate a memory warning and shutdown the test app.

The idea here is that by touching button1, we create a large array in a thread, and touching button 2, we enumerate that array. The array is large enough that it gives you time to ensure you touch button 2 while button 1's thread is still populating the array. If you didn't create the copy of the array before enumerating it button 2, the app will crash. Using the copy of pattern I mentioned in the last post, prevents that crash as expected.

Taking this a little further, the test code shows that when you take a copy of the mutable array, you get a copy of it at in its current state. If something is altering the array when you make the copy, you could get a copy of the array in an unusable or unexpected state. If you were adding or removing elements for example, you might have half of the elements in your copy while expecting all of them, or none of them.

If you need to ensure changes to an array are completed in full before another thread accesses it, you can use the @synchronized directive. The modified code looks like:

- (IBAction)button1TouchUp:(id)sender
{
    DebugLog(@"Populating Array");
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        @synchronized(array)
        {
            for (int i = 0; i < 10000000; ++i)
            {
                [array addObject:[NSNumber numberWithInt:i]];
            }
            DebugLog(@"Done Populating Array: %d", [array count]);
        }
    });
}

- (IBAction)button2TouchUp:(id)sender
{
    DebugLog(@"Enumerating Array");
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        @synchronized(array)
        {
            DebugLog(@"Copying Array");
            NSArray *copyOfArray = [array copy];
            DebugLog(@"Done Copying Array");

            long long x = 0;
            for (NSNumber *number in copyOfArray)
            {
                x += [number intValue];
            }
            DebugLog(@"Done Enumerating Array: %d", [copyOfArray count]);
        }
    });
}

The @synchronized directive creates a lock on the object for you.

A few points to be aware of:

  1. you need to use @synchronized around all places where you need to lock the object.
  2. the code will block until the object can be accessed, so if thread A is using the object thread B will pause until thread A is done with it, so be sure not to create a block on the main thread.
  3. if you define a property on a class as atomic, by omitting the non-atomic keyword, the synthesized accessors will include synchronization code and simply the code for you, but you still need to be aware of the blocking issues.

As always, I love reader feedback. Especially when you point out my mistakes, since that's the fastest way to learn more.

A Simple Tip to Avoid Crashes

As you may be aware, I went full time indie a couple of months ago. I've been working almost as many hours as I can stay awake for clients, and spending whatever extra time I can find on a new game that I hope will be finished and submitted to the App Store before my next blog! I can't wait to tell you guys all about it.

With all this work though, I've been slacking in the blog department. Today I start to correct that. Here's a quick tip for today that may save you from a few crashes tomorrow.

It's a common sight to see something like this in Objective-C, where you're enumerating through an array (that's mutable), and doing something to each of the objects:

// self.objects is an NSMutableArray
for (NSObject *object in self.objects)
{
    [object doSomething];
}

If your app is multithreaded, you'll get a crash if another thread adds to or removes from the array at the same time this loop is being processed. Depending on timing, you might not see this bug hit until the app is being used by users.

Instead, anytime you're going to run through an array like this, make a copy first and enumerate the copy. Now you're multithreaded safe.

NSMutableArray *copyOfObjects = [self.objects copy];
for (NSObject *object in copyOfObjects)
{
    [object doSomething];
}
[copyOfObjects release]; copyOfObjects = nil;

Here's another common pattern you'll see, when you're enumerating the array in order to remove certain elements from it. Because you know you can't remove objects from the array as you're running through it, you create another array to store the the objects you want to remove and then remove them in a second step:

NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];

// self.objects is an NSMutableArray
for (NSObject *object in self.objects)
{
    if ([object shouldBeRemoved])
    {
        [objectsToRemove addObject:object];
    }
}
[self.objects removeObjectsInArray:objectsToRemove];
[objectsToRemove release]; objectsToRemove = nil;

Using the copyOf pattern above, you can remove the objects in a single step, since you're not actually enumerating the same array anymore:

NSMutableArray *copyOfObjects = [self.objects copy];
for (NSObject *object in copyOfObjects)
{
    if ([object shouldBeRemoved])
    {
        [self.objects removeObject:object];
    }
}
[copyOfObjects release]; copyOfObjects = nil;

Now you have nice, clean, efficient, crash free code.

RIP Steve Jobs (1955-2011)

RIP Steve Jobs

Steve,

Thanks for everything you've done to improve this world.  Thanks for giving us a platform with which to build a business, a career, and a life that's truly fulfilling.

You'll be missed.

Tags:

Copyright

© sabby