Ryan Booker
About Archive Replies Photos Search Stats Also on Micro.blog
  • If you were a Twitterrific or Tweetbot subscriber, I encourage you to:

    1. Reinstall (or update) both apps.
    2. Tap “I Don’t Need a Refund”.

    Forgoing a few bucks will help two small development houses whose apps we’ve all enjoyed for many years.

    A longer explanation on Daring Fireball

    → 3:28 PM, Mar 2
  • Vale

    For a decade and half I used Twitter. Every day. Occassionaly via the website or first party apps, but for me it was always defined by third party apps.

    Hell, third party apps and the community gave us the very word tweet, let alone the blue bird logo, mentions, retweets, and hashtags!

    I started with the first version of Twitterrific on macOS and iOS, I used Birdhouse before drafts were ubiquitous, Tweetie before Twitter bought it, and of course Tweetbot.

    I’ve had both Twitterrific and Tweetbot installed on my devices for as long as they’ve existed.

    I stopped using Twitter a few months back when a grifter acquired and rapdily destroyed it. This week he cut off every significant third party app, all but scuttling several businesses, without a word or reason.

    Cowardly.

    Iconfactory have now discontinued Twitterrific and Tapbots are hard at work on Ivory1 for Mastodon.

    Good luck and thanks. I look foward to seeing and using what you’re up to next.


    1. Tapbots have released Ivory on the App Store, and have a roadmap for the future. It’s easily my favourite Mastodon app. ↩︎

    → 10:39 AM, Jan 20
  • Setting up DeviceCheck access for your iOS apps

    If you’re trying to set up DeviceCheck access, I suggest ignoring Apple’s certificate set up instructions at the start of the DeviceCheck docs. They’re garbage. So bad they’re not even wrong.

    Instead:

    1. Go to Certificates, Identifier & Profiles in the Dev Portal
    2. Go to the Keys tab
    3. Create a DeviceCheck key

    Now you can continue reading the docs.

    NB: You’ll need your Team ID from the portal, and the 10 character identifier attached to the Key you just created (which is in the portal, and the Key’s filename), for the JSON object you’ll be creating.

    → 4:57 PM, Feb 27
  • Error: Multiple commands produce…, Cocoapods & Multi Platform Podfiles

    TL;DR: Remove use_frameworks! from your Podfile.


    If you’ve arrived here you probably have a Podfile that includes multiple targets for multiple platforms that share some pods, while using the new Xcode build system.

    And you’re almost certainly ready to burn everything to the ground.

    Thankfully, I already did that, and out of the ashes a solution emerged, resplendent and angry.

    There are a couple of issues:

    1. Xcode is non deterministic with respect to this issue, and will randomly decide whether there is an issue to worry about at all. This is fucking infuriating.

    2. The new build system (randomly, see 1!) doesn't like duplicate things, whereas the legacy build system didn't care.

      When Swift was introduced, it didn’t support static libraries, so we all dutifully added use_frameworks! to our Podfiles. When you have multiple platforms sharing pods, you will have multiple copies of the pod source in your Pods/Target Support Files/ folder, and Xcode will get its knickers in a bunch. Sometimes.

      Swift now supports static libraries.

      Remove use_frameworks! and everything will work.

    → 10:13 AM, May 11
  • The Data61 FP Course is great. Tony Morris et al., have done a great job producing a fundamental course. Here’s a video series of Brian McKenna delivering the material.

    → 1:09 PM, Jan 3
  • A difference in perspective

    Recently, Erica Sadun wrote about inconsistencies in Swift’s function-like constructs, such as KeyPath and method references—a post that appears lost in the apocalypse befalling her site.

    Erica pointed out that method references return (A) -> () -> B functions that aren’t directly useable by higher order functions like map, which takes an (A) -> B function.

    // Basic example
    ["a", "b"].map { "a".uppercased() } // ["A", "B"]
    
    // Using a method reference. Hmm. Not quite what we wanted
    ["a", "b"].map(String.uppercased) // [() -> String, () -> String]
    
    // Junky alternatives
    ["a", "b"].map(String.uppercased).map { $0() }
    ["a", "b"].map { String.uppercased($0)() }
    

    She then works through a couple of solutions. A custom map style function on Sequence taking such a function and hiding the extra function application, and eventually an apply function that converts (A) -> () -> B to (A) -> B (and an accompanying operator).

    One thing that struct me was how Erica derived all this from first principles, essentially reimplementing a function called flip, a common function in functional programming circles. From my perspective I immediately saw the problem as:

    Oh, I need to flip this function and partially apply it to ()

    Unfortunately, because we’re using Swift, it’s not quite that easy.

    func flip<A, B, C>(f: (A) -> (B) -> C) -> (B) -> (A) -> C {
        return { b in { a in f(a)(b) } }
    }
    
    // Doesn't compile! For a couple of Swiftastic reasons.
    ["a", "b"].map(flip(String.uppercased)())
    

    Swift treats () parameters as a special case, not just another type, so () can not be used as a B. Let’s write a special case version:

    func flip<A, B>(_ f: @escaping (A) -> () -> B) -> () -> (A) -> B {
        return { { a in f(a)() } }
    }
    
    // This one does compile! Though you may notice it looks to have some redundancy.
    ["a", "b"].map(flip(String.uppercased)())
    

    And we can now reimplement Erica’s apply in terms of our special case of flip:

    func flap<A, B>(_ f: @escaping (A) -> () -> B) -> (A) -> B {
        return flip(f)()
    }
    
    ["a", "b"].map(flap(String.uppercased))
    

    Or just remove the intermediary special case of flip altogether, arriving at exactly Erica’s solution (operator not withstanding):

    func flap<A, B>(_ f: @escaping (A) -> () -> B) -> (A) -> B {
        return { a in f(a)() }
    }
    
    ["a", "b"].map(flap(String.uppercased))
    

    In the end, we’ve arrived at the same solution to this specific problem. However, I hope I’ve illustrated how viewing programming from a slightly different perspective let us identify the issue quickly, and build a solution out of existing smaller, composeable pieces.

    → 3:31 PM, May 4
  • How should I frame this? Selecting better video frames for photogrammetry

    Sometimes my spheres of interest overlap.

    Meeting Matt Carter at OzTek a couple of years ago, and working with Pim Bongaerts on mesophotic.org introduced me to photogrammetry, and over the past few weeks I’ve been processing some models using 4K video.

    Selecting useable still frames is tedious at best: manual selection is a non starter, and basic selection with ffmpeg often produces blurred frames.

    I solved this with framer.sh, a script for selecting better frames.

    Feel free to use and improve it. Pull requests accepted.

    → 3:48 PM, Apr 20
  • Today I discovered GNU Parallel, and I don’t know what to do with all this spare time.

    → 9:07 PM, Apr 19
  • One of the best development tips I can give you is to not leave an omega-3 capsule in your computer bag for two weeks.

    → 9:39 AM, Apr 19
  • I finally got around to moving my site from Tumblr to Wordpress. It seemed like the thing to do. Perhaps I’ll write more, and perhaps not. We’ll see…

    → 3:16 PM, Apr 15
  • How I learned to stop worrying and love the bomb

    Recently, Brandon Williams posted Algebraic Structure and Protocols, a great article describing and building semigroups, monoids and groups using Swift protocols. It’s an interesting article and I encourage you to read it (and his other articles). In fact, I’ll wait while you do…

    As with most such articles there are some simple examples, however I find the leap from ok, I see what that is, to oooooh, I see what this thing does! somewhat confounding.

    In other words, how do I spot this pattern and use the technique in my own code?

    I aired this small grievance, wondering if mconcat([1,2,3,4]) was just a fancy way to replace reduce([1,2,3,4], 0, +)1 in my code? In response, Nick Partridge said yes, but related the following real example from his work:

    I am measuring outdoor advertising views. I have many different Collectors of this metric for various types of signage with a type like: Trip -> ViewCount. e.g. A collector of views for buses seen along the trip or a collector of views for billboards. These are all independent things that I need to collate.

    This example was a penny drop moment for me. The Collector type is a monoid!

    Given a specific trip, the idea is to calculate the view counts for different types of signage and collate them in some way. It doesn’t really matter what way. Maybe we’re adding them all together for a total, maybe we’re returning a list of counts, or a dictionary of types and counts. It doesn’t matter. We’re taking a list of Collectors and a Trip to process, and returning some collation of the data.

    We can process this manually, of course. We can take our list of Collectors, pass the Trip to each, get the results, then collate them however we want.

    But if we recognise this as a monoid, we realise that a lot of that code is already written, it’s the same code that processes all our other monoids. A tiny abstraction that is common to a lot of things. We already know how to collate things. We give their type a collation operator and an identity, then mconcat them.

    In other words, we can mconcat our Collectors and produce a new composite Collector that does the collating for us. We can pass our Trip to that new Collector and obtain our result directly. For example, instead of something like:

    let collectors = [collector1, collector2]
    var result: Int
    for collector in collectors {
        result = result + collector(trip)
    }
    

    we can take a small step to this:

    let collectors = [collector1, collector2]
    collectors.map { collector in collector(trip) }.reduce(0, +)
    

    but once we’ve defined a monoid extension for Collector, we can:

    let collate = mconcat([collector1, collector2])
    collate(trip)
    

    We have abstracted away the idea of how we’re going to collate our data and instead ask for collated results directly. How to define this monoid is actually covered in Exercise 6 of Brandon’s article.

    Semigroups, monoids and groups while perhaps alien, are in fact useful and common. They’re names for patterns we encounter frequently and now recognise. Maybe you’re like me and have just realised you’re working on something monoidal right now.

    → 3:18 PM, Feb 19
  • A year already. Thanks again, Steve.

    → 11:10 AM, Oct 5
  • Gas Manager

    I’ve finally released my new gas management program for iPhone & iPod touch. Gas Manager helps you choose and blend custom gas mixes for diving. It includes partial pressure blending (with fudge factors), top off and best mix, as well as MOD, END and EAD calculations.

    Check it out!

    → 9:39 AM, Feb 12
  • gcc on Lion

    If you’re using rvm or anything else that specifically wants gcc on Lion. Install Xcode as usual, but also do this:

    1. Download Apple’s gcc: http://www.opensource.apple.com/tarballs/gcc/gcc-5666.3.tar.gz
    2. Build it http://www.opensource.apple.com/source/gcc/

    From within the source directory:

    mkdir -p build/obj build/dst build/sym
    gnumake install RC_OS=macos RC_ARCHS="i386 x86_64"
    TARGETS="i386 x86_64" SRCROOT=`pwd`
    OBJROOT=`pwd`/build/obj DSTROOT=`pwd`/build/dst
    SYMROOT=`pwd`/build/sym
    sudo ditto build/dst /
    ln -s /usr/bin/gcc-4.2 /usr/local/bin/gcc
    
    → 4:38 PM, Nov 11
  • Points of Interest added to crashcard

    Crashcard’s map view now features “Points of Interest” showing where nearby towing services, public transport, taxis, repairers, hospitals etc are. Tapping on a POI shows you the address and allows you place a call where available.

    Available now on the app store.

    → 10:22 AM, Oct 29
  • Thanks Steve.

    → 11:06 AM, Oct 6
  • Who with the Wot now?

    UPDATE: Wot was retired.

    A little Dropbox syncing, notes app I wrote for myself is now available for everyone else.

    → 7:50 PM, Dec 18
  • Safeguard with crashcard

    Finally, crashcard is available in the App Store.

    Unfortunately, car accidents do happen. CRASHCARD guides you through the collection of important information during these stressful times, granting peace of mind and simplifying insurance claims.
    → 10:00 PM, Nov 19
  • Air Depth Crash Bug

    UPDATE: Air Depth is no longer available. Check out Gas Manager on the App Store.

    UPDATE: Air Depth 1.3.1 is available. No more crashing on iOS3.x. Thanks for your patience.


    Version 1.3 of Air Depth contained a crash bug related to an iOS4 framework. I’ve already submitted 1.3.1 to fix the issue. 1.3 was approved in 12 hours, so I hope 1.3.1 is approved within a similar time frame.

    The issue manifests on iOS versions less than 4. iOS 4 is released on 21 July. However, 1.3.1 will fix the issue for all prior iOS versions if you don’t intend upgrading to iOS 4.

    Sorry for any inconvenience.

    → 9:00 PM, Jun 19
  • Followup on App Store Pricing and Developer Payments

    I received a message from Apple today, in response to an earlier post. I’ve reproduced it bellow:

    Hello

    Your assumptions on UK VAT are correct; the iTunes Store sales prices in the United Kingdom are VAT (Value Added Tax) inclusive, as are any other goods sold in the UK. This is required by law, not by Apple policy. You can refer to the government tax website www.hmrc.gov.uk for an understanding of VAT laws. Every customer in the UK expects that VAT is included in their price, so this is not unusual for them.

    To the contrary, USA customers are used to seeing sales taxes added at the time of checkout, and not included in the sales price of an item. This is how the iTunes Store reflects taxes—the same way customers are accustomed to seeing it as they would in any other store in the USA.

    This is address in the FAQs on iTunes Connect, as well as explained in detail to anyone who emails iTStax@apple.com.

    Kind regards, [redacted] iTunes Royalty Accounting

    → 4:52 PM, Dec 12
  • On App Store Pricing and Developer Payments

    UPDATE: A reply from Apple


    I had a rather long and messy discussion with Garret Murray on twitter today, after he expressed some concerns regarding apparent discrepancies in App Store developer payments. I think the confusion can be put down to two issues:

    1. Apple is less than transparent
    2. There is a minor misunderstanding with regard to sales taxes

    On Transparency

    The only official word is that developers receive 70% of App Store profits. Without any further details, many developers make the reasonable assumption that this equates to 70% of sales. After all, Apple made a big deal of covering all the costs for us. Unfortunately, a quick look at your monthly financial statements scuttles this idea.

    Tier 2 pricing is $1.99 USD in the US, and £1.19 in the UK. So, we might expect the following payments in our financial statements:

    $1.99 * 0.7 = $1.40
    £1.19 * 0.7 = £0.83
    

    What we find is a UK payment of only £0.72. Bastards.

    On Sales Taxes

    Sales taxes are applied to the price of goods and services. They are collected by the seller and remitted directly to the government, having no relationship with the seller’s income tax obligations.

    There are two ways to apply sales taxes. They can be included in the list price or applied at the register. For example, Europe and Australia include the tax, but some states in the US apply it at the point of sale. However applied, when considering profit margins and pricing the seller only cares about the base price. That is, the price not including sales taxes. How the final sale price is displayed is a semantic issue and the result of local laws.

    So What Happened?

    My educated guess is that Apple withheld sales tax (VAT of 15%) from the UK payment. A quick calculation confirms this as a definite possibility, assuming tier prices already include any applicable sales taxes. That is, the tier price in this case is 115% of the base price.

    Base Price  = Price * 100/115
                = £1.19 * 100/115
                = £1.03
    £1.03 * 0.7 = £0.72
    

    This satisfied me. However, Garrett wondered if Apple is reducing the price in other regions to absorb the sales taxes. I doubt it, but we really don’t know and that’s the biggest issue. There doesn’t appear to be any information available on exactly how tier prices were converted from USD.

    My guess? When the tiers were priced, Apple used the current exchange rate to convert $USD to £ then added 15%. Of course, without word from the mother ship, we have absolutely no way of verifying this. If Garrett’s right, the tiered pricing model is fundamentally broken; charging different relative prices in each region.

    Clear as mud

    Personally, I think Apple should release details of the pricing model and include a specific break down of developer payments in our monthly financial statements. However, I don’t expect to find a story dissimilar to what I’ve described above.

    → 5:00 PM, May 5
  • Busy Bee

    Things are busy here at the moment. Firstly, I have a new scuba diving app in progress for iPhone OS. With the back end work done and wrapped in a nice set of unit tests, I’m finishing off the interface, which requires a lot more thought than either Air Depth or Depth Gauge.

    Secondly, I’m working on a large rails app for a friend’s business, which is keeping me busy and hopefully fed for a while.

    On a marginally related note, if anyone has a line on some research or film related diving work, paid or volunteer, let me know. I’d like to do some grunt work underwater for someone.

    → 6:00 PM, Apr 16
  • Depth Gauge Now With More Air Depth

    UPDATE: Air Depth and Depth Gauge are no longer available. Check out Gas Manager on the App Store.


    I’ve submitted a new version of Depth Gauge to the App Store. Other than being recompiled for OS 2.x backward compatibility, this update brings an enhanced version of Air Depth EAD calculator. The Depth slider’s range is now tied to the MOD of the gas.

    Version 1.1 of Air Depth has also been submitted. Other than recompilation for backward compatibility, this is a cosmetic update.

    → 4:00 PM, Mar 22
  • Backward Compatibility

    UPDATE: Air Depth and Depth Gauge are no longer available. Check out Gas Manager on the App Store.


    A little research by Arstechnica revealed that a significant portion of the iPhone community—around 45%—have not yet upgraded to iPhone OS 2.2.1. In light of that, the next release of both Air Depth and Depth Gauge will be compatible with all versions of iPhone OS 2.x.

    → 11:18 PM, Mar 17
  • Finally

    UPDATE: Depth Gauge is no longer available. Check out Gas Manager on the App Store.


    Air Depth and Depth Gauge 1.0.1 have finally gone live on the App Store. The biggest change is that both now share a lot of code (in preparation for Depth Gauge 1.1), however the visible changes are:

    1. The Nitrogen slider’s visibility is controlled by a new setting: “Show Nitrogen %”.
    2. New gas icons replace the labels to the left of each slider.
    3. Localización española.

    Depth Gauge 1.1 is almost ready for publishing. I’m just waiting on one last piece of localised text and for the resurrection of my MackBook Pro. The big change for 1.1 is the inclusion of Air Depth, allowing you to use both tools from the one application. The free version of Air Depth will of course still exist and could be considered a lite version of Depth Gauge.

    → 10:00 AM, Feb 26
Page 1 of 2 Older Posts →
  • RSS
  • JSON Feed