Category Archives: coding

Adventures in app writing and publishing

Adding Codeable to @Published

Codable is a kind of magic.

@Published certainly behaves like magic.

But if you try to add a @Published property to your codeable, then XCode complains

This one had me frustrated for many hours. The issue is that the encoder gets an instance of Published<Type> to encode. As far as I know there isn’t a proper way to get the actual value.
(normally, your code will call the getter on wrappedValue, but I’m not aware of a way to call that directly with Published)

In the end, the only solution I found was to Mirror the struct and extract the value from the ‘value’ Mirror.Child

This has been pretty typical of my SwiftUI experience. There is lots of magical functionality that falls out for minimal effort in a magical way.

But then you try to push the edges, act like a programmer and extend things – and you find that the basic API seems designed to frustrate you. Why would the Published struct not have a simple way to access the wrapped variable?

Anyway, for now at least – we can hack around the limitations

import Foundation
import SwiftUI

extension Published:Encodable where Value:Decodable {

    public func encode(to encoder: Encoder) throws {

        let mirror = Mirror(reflecting: self)
        if let valueChild = mirror.children.first(where: { (child) -> Bool in
            child.label == "value"
        }) {
            if let value = valueChild.value as? Encodable {
                do {
                    try value.encode(to: encoder)
                    return
                } catch let error {
                    assertionFailure("Failed encoding: \(self) - \(error)")
                }
            }
            else {
                assertionFailure("Decodable Value not decodable. Odd \(self)")
            }
        }
        else {
            assertionFailure("Mirror Mirror on the wall - why no value y'all : \(self)")
        }
    }
}

extension Published:Decodable where Value:Decodable {
    public init(from decoder: Decoder) throws {
        self = Published(initialValue:try Value(from:decoder))
    }
}

Apple: You recompiled. Let’s make that crash now.

tl;dr – UIApplication.shared.idleTimerDisabled now causes an undocumented crash if you compile with the iOS 13 SDK and use it from a background thread.

Photo by Zach Savinar on Unsplash

VLC Streamer is one of my oldest apps. It has been going strong since 2008!

It’s a pretty complex app with a bunch of different code paths and edge cases. It’s mostly written in Objective-C.

I uploaded a new build on Jun 15, 2019 to deal with some details around new device screens. No problems there.

I uploaded again on November 20th. There were two relevant changes.

  1. I updated the base SDK to 11.0 (from 9.0)
  2. I compiled with the latest XCode using the 13.0 SDK

I woke up on the 22nd to get loads of crash reports. One of my code paths calls this from a background thread:

[[UIApplication sharedApplication] setIdleTimerDisabled:YES]; 

This now causes a crash.

Three things of note here for me

  1. Nothing in the documentation says you can’t call idleTimerDisabled from a background thread.
  2. No code has changed. I just recompiled and resubmitted. The old build worked, the new build crashes.
  3. Why is this even an issue? If Apple really need this flag to be changed on the main thread – couldn’t they dispatch it to main in the implementation?

Of course – better testing would have caught this, but how many of us really have proper UI testing in all of our code paths…

Update:

Building a test app with iOS 9 as the target still gives the crash.

Presumably this means that the change has come from using XCode 11.2.1 and the iOS 13 SDK.

Overreaction vs Sanity. Google Play vs Apple Store.

In the last week or so, both Apple and Google informed me that one of my apps was in a small way breaching store guidelines.

Photo by Jorge Fernandez on Unsplash

I’ll paraphrase both messages here:

One of your apps hasn’t been updated in years. It doesn’t meet current guidelines. If you don’t update it – we’ll remove it in 30 days.

Apple

and

One of your apps breaks the metadata guidelines. It has a quote in the app description.

We have removed it from the store.

Google

Both complaints are probably valid. The iOS app hasn’t been updated since 2015 and hasn’t been optimised for the latest devices. I’ll update it this month.

The Android app did have the following quote in the store description

VLC Remote is the latest Android app to earn my favor, and it’s a beauty

Androidnica

The quote has been there for at least 5 years. I’m not entirely sure whether it should count as a ‘user testimonial’ (banned) or a ‘third party review’ (I think those are ok). But either way – I’m happy to remove it.

Removing my app from the store just seems like a Massive Overreaction when they could simply have emailed me to request a change.

What does a red dot mean Apple?

Every now and again – Apple demands my attention with a red dot on the System Preferences in the dock

When they do – I launch the preferences app, and my eye is immediately drawn to the icon at the top right on the ‘Notifications’ icon

Of course – that red dot isn’t a notification dot. That’s an icon of a notification dot – not an actual notification dot

I’m supposed to ignore that one, and look for the second one on the ‘software update’ icon

That’s just bad UI

Update – Catalina

Apple have fixed this as of Mac OS 10.15

Easier iTunes Connect Sandbox Users with GSuite Routing

Use GSuite Routing to dynamically handle different iTunes Connect test emails.

iTunes connect testing is a pain. One of the pain points is that you have to create a bunch of iTunes Connect accounts when you want to test your purchase flows. Each of those needs a separate email address.

You used to be able to do this with the magic

  • rob+test1@hobbyistsoftware.com
  • rob+test2@hobbyistsoftware.com

And all your emails would be routed through to rob@hobbyistsoftware.com (this works for gmail, and many other email providers)

Sadly Apple disabled this capability some time in 2018 (?) – so now you need a new valid email address for every iTunes Connect sandbox user

GSuite Routing provides a neat way to restore this functionality

  1. Open your GSuite Management Console
  2. Click through to Apps > G Suite > Gmail
  3. Click on ‘Default Routing’
  4. Click ‘Add Setting’ and add something like the following

(note – the regexp is rob_.*@hobbyistsoftware.com)

This redirects all email of the format rob_something@hobbyistsoftware.com to rob@hobbyistsoftware.com

Your icon can’t look like an iMac

After about 10 updates with my new icon for Multi Monitor Wallpaper, Apple decided to reject an update because

Guideline 5.2.5 – Legal

Your app does not comply with the Guidelines for Using Apple’s Trademarks and Copyrights. Specifically, your app includes:

– Apple trademark, iMac profile/ image, imagery – or likeness – in the icon

This icon is not OK any more:

This icon is fine (for now)

Notice the narrower bezel and narrower darker stand. Apparently that makes all the difference 🙂

To be clear – the icon clearly was inspired by the iMac, though it is custom built by a third party, and not based on Apple artwork.

Anyway Multi Monitor Wallpaper has a new icon.

Active Storage: Adding a custom Analyzer for unsupported image type

When you add an attachment with Active Storage, Rails kicks off a background job to analyse the attachment. You can add your own analyzer – but the documentation is very thin on the details.

One of my apps uploads .heic image files. Unfortunately as of Rails 5; this triggers a crash in the image analyzer because imageMagick can’t yet handle this filetype.

My solution to the crash is to create a custom analyzer that handles image/heic files, and returns no metadata.

# lib/models/heic_analyzer.rb

class HeicAnalyzer < ActiveStorage::Analyzer

	def self.accept?(blob)
	  blob.content_type == "image/heic"
	end

	def metadata
	  {}
	end

end

Make sure the class is loaded

# application.rb

config.autoload_paths << "#{Rails.root}/lib/models"

then add the Heic analyzer to active storage

# config/initializers/image_analyzer.rb

Rails.application.config.active_storage.analyzers.prepend HeicAnalyzer

That’s it. Rails now handles my .heic attachment without crashing

ITunes: You’re doing it wrong. I could help – but I won’t.

I just tried to redeem a code for a Mac app through iTunes.

Apple identify that I’m using the code in the wrong place and tell me what to do (use the Mac App Store), but they could easily do better.

Better: Instead of a red error message – why not give me a link that opens the Mac App Store with the code pre-filled?

According to Stack Overflow, this link used to work:
macappstores://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/redeemLandingPage?code=NHWEL3FHRYKR

As of April 23rd 2019, that link opens the right page – but doesn’t pre-fill the code. (I have filed a bug)

Best: Why not just redeem the code for me? I’m already logged in on my mac with the right account.

Apple auto-updates Xcode, but you can’t submit with it

I woke up this morning to find that my Mac auto-updating from Xcode 10.1 to Xcode 10.2

Amongst other things, this disables Swift 3, and introduces Swift 5.

So – after updating a fork of one of the libraries I use, I updated my own code to Swift 5 and submitted to the App Store. Only to find the following:

The newly released version of XCode can’t be used to submit apps.

It turns out this is standard practice from Apple

This isn’t good enough. This kind of thing needs a checklist:

Xcode Release Checklist

  • Has it been tested
  • Can developers use it
  • Really? Can developers submit builds with it?
  • Even Mac Apps?

It’s fine to release beta versions that we can’t use.

It’s not cool if release versions don’t work.