The Making of Bloop — OMG the logs are too noisy!

Making of Bloop

One of a series of posts about our next app as we make it. It’ll include some details about our technology and design choices and challenges.

Since I added some functionality to Bloop that uses Flint, we have been getting Flint’s detailed logging for free. You can however have too much of a good thing.

By default Flint’s logging will let you know when every action you perform starts and completes or fails. As I have added actions for some of the on-screen controls that show in our content player, the console output has become a bit too noisy.

Flint’s logging lets you toggle logging levels per-topic (a Feature is a topic) at runtime, so all I had to do was call this for the features that are too noisy and set the log level to warning on those, as I want debug for everything else in the app at the moment.

What we were getting was a mix of our on-screen controls logging as well as output from Flint’s internal workings.

🐞 16:58:18.428 Debug • main | Activity 'Dispatch' | OSDFeatures/PlayerOSDFeature/#ShowPlayerOSDAction | Starting | State: XXXXXX
🐞 16:58:18.428 Debug • main | Activity 'Dispatch' | OSDFeatures/PlayerOSDFeature/#ShowPlayerOSDAction | Completed (success (not closing action stack)) | State: XXXXXX
🐞 16:58:18.428 Debug • N/A | Activity 'Intent Dispatch' | FlintFeatures/SiriIntentsFeature | Action completed but did not return an intent to donate: Request 5 for PlayerOSDFeature action ShowPlayerOSDAction

The fix was very simple, but I had forgotten my own Flint API for this and hadn’t yet written up Flint documentation for it, so I added it. I changed my code so that the App delegate does this:


FlintFeatures.setLoggingLevel(.warning)
OSDFeatures.setLoggingLevel(.warning)

All features that are sub-features of those feature groups are now quiet as a mouse. All the other features of the app retain the default logging level.

The Making of Bloop – Converting some code to Flint Actions

Making of Bloop

One of a series of posts about our next app as we make it. It’ll include some details about our technology and design choices and challenges.

Bloop has been in the works on and off since 2017. Late in 2017 I switched my focus to building the Flint framework because I had an epiphany about how I think things should be built, and Bloop would need all this to be a great Apple platforms citizen. TL:DR; Flint lets you design and define the features of your app as small chunks of code, and does cool things mostly automatically like NSUserActivity, URL handling, Siri, Analytics, In-App Purchase feature gating etc.

Now we’re back on Bloop, I have to take this pre-Flint code and turn it into Features and Actions. This is a useful exercise to discuss here as it can help demystify usage of Flint.

My starting point was for the on-screen controls we have, and the app’s default behaviour which is to automatically start playing the last content you were watching.

In order to do this I had to break this down into conceptual features and actions, and then they can be represented in code. This brings clarity to what your app is and represents to users. We’ll just look at the player part of it as an example.

So we have a player feature that lets users play content. Right now, it can show the player UI, and it can be told to play the last content it was playing, so those are two actions: “show” and “play the previous content”.

In code this becomes:


public class PlayerFeature: Feature {
    public static var description: String = "Player"

    public static let show = action(ShowPlayerAction.self)
    public static let playPreviousContent = action(PlayPreviousContentAction.self)

    public static func prepare(actions: FeatureActionsBuilder) {
        actions.declare(show)
        actions.declare(playPreviousContent)
    }
}

The action types mentioned there deal with the internal plumbing of how we show and play the content, calling into other dependencies in the app to do the actual work using a presenter passed in when performing the action.

If we look at the PlayPreviousContentAction, we see something cool:


public final class PlayPreviousContentAction: FlintUIAction {
    public typealias InputType = NoInput
    public typealias PresenterType = UIViewController

    public static let activityEligibility: Set<ActivityEligibility> = [.handoff, .prediction]
    
    public static func prepareActivity(_ activity: ActivityBuilder<PlayPreviousContentAction>) {
        activity.title = "Play the previous content"
    }
    
    …    
}

I’ve not included the details of the perform implementation of that action. However with the above coded and wired up to load the previous content and play it, and the AppDelegate set up to use these actions to start the player when the app launches, we can run the app and we get:

  • Free logging of when the player is shown or previous content played (including Flint’s timeline/activities debugging tools)
  • Siri prediction for the playback of previous content.

This latter part is great. Though I’m cheating a bit because without implementing anything else new the app starts and shows previous content anyway — Siri already starts suggesting I run Bloop to show the previous content when I’m at work. Flint deals with all of this because of the activityEligibility setting in the action. The app has no explicit code to register activities.

Screenshot of Siri suggestion Bloop activity

It already feels like we’re getting things for free with Flint. I just need to hook up the App delegate activity continuation code to call into Flint and we’ll have something that can properly handle this when there are multiple actions or URLs that could launch the app.

The Making of Bloop — SwiftUI Shenanigans

Making of Bloop

One of a series of posts about our next app as we make it. It’ll include some details about our technology and design choices and challenges.

Bloop will not be releasable until well after iOS 13 is public. It’s a spare-time project right now and that means progress is slow. There’s often a great benefit to this however, in that you get a lot of “out-of-band” time to think about the problem you’re solving and how to do it.

As a consequence of this future release date, we can use all the iOS 13 specific APIs right now and this includes SwiftUI.

Our app is a sort of video player with on-screen controls and a bunch of UI for editing content or settings. Most of our on-screen controls are likely to use UIVisualEffectView for system blurs, vibrancy and materials. This sadly means we can’t seem to use SwiftUI for this because of issues with nesting views inside the effects views and SwiftUI not supporting materials or vibrancy yet.

However since the Xcode 11 beta which added support for SwiftUI forms, the main editing and settings UI have turned out to be a brilliant candidate for SwiftUI. Especially so because we will want to build up many different variations of some of this UI based on the user’s content. SwiftUI should make this really easy because we can conditionally include UI elements easily in code.

This approach has already allowed us to quickly prototype the UI and prove out possible navigation controller issues we were concerned about. I also learned something I never fully understood about UINavigationController — namely that no matter what nesting level a view controller is, if it pushes something new onto the navigation stack, that nested view controller’s navigation item is used for Back navigation. This is really smart and very handy — and amazingly is supported with SwiftUI code that sets navigation bar titles, even when nested inside a UIKit UINavigationController and another child UIViewController. Go SwiftUI team!

Obviously all things SwiftUI are still very rough, but we found a part of the app where SwiftUI will give us a great deal of productivity and reduce code while minimising risk. In the unlikely event it doesn’t work correctly, it has been so easy with SwiftUI that we won’t lose much if we then have to write it in UIKit.

One very painful problem with SwiftUI has been that at the time of writing, it seems impossible to run SwiftUI previews that are inside a framework of your own or in an app containing certain frameworks — the preview process crashes with what look like Thread Sanitizer related issues. We have to run previews in a separate project with no dependencies as a scratchpad.

I’ve raised Feedback of course (FB6962455 “SwiftUI preview canvas crashes when adding a framework to an app”).

Our First Electric Car: A week with a petrol SUV made us yearn for our electric LEAF

Green

This summer we rented a KIA Sportage to drive to our holiday on the South West coast, because our day to day car is a used electric Nissan LEAF without enough range for this kind of rare journey — a conscious compromise we made to make real change to our emissions now instead of waiting forever for the perfect electric car that is big enough and has enough range for our family.

This journey would have required about three rapid charging stops in the LEAF which would have added up to 1.5 hours to the travel time. This would have been doable except for the fact that the South West of the UK where we were travelling has very little rapid charger infrastructure so charging en route would have been very tricky if not impossible.

The petrol KIA Sportage was frankly a disappointment. Compared to even our little LEAF it had no power on hills, feeling very sluggish, heavy and externally way too large purely to project ego and status. We found ourselves really missing the smooth and powerful drive of our EV. It was very unexpected.

The KIA did not have significantly more room, and arguably less, than our old diesel Toyota Corolla Verso which was a great family car. “Bizarrely ungenerous” is how I would describe it.

While the range of the KIA on a single tank is huge at about 400 miles compared to the LEAF’s 100 miles at best, filling up after our trip cost about £75 in petrol. At about 450 miles of driving this would be far cheaper with an electric vehicle, costing around £35 using even the most expensive rapid chargers available. On a slower home charger it would have cost about £15!

Interestingly as we return from holiday EV cars are in news again. EV sales are up while the rest are flat or down. The new Nissan LEAF plus with 60kW battery is now available, which seems very interesting for people wanting a ~200 mile range small car.

Many people can change their car now. There’s no time to lose. Larger batteries and better charging infrastructure are coming now but a great many of us can change right now with little or no compromise.

Our switch to EV this year means we’re not buying ~£3000 of diesel fuel this year and every year now on. It will not take that many people switching to truly change the market for oil and derivatives. Our local petrol station will be feeling this change, and so will the oil companies as well as the vehicle industry. The switch is going to happen very quickly now.

Our First Electric Car — Adventures in Charging

Green

The next in the series of posts covering our experience as a family of switching from a single diesel car to a single Nissan LEAF car.

Previous posts:
* The Decision Process and Purchasing
* Real world range & charging.

Home Charge Point Installation

Last week we finally got our home charging point installed. We had to delay the install until I got back from Apple’s WWDC conference, so we had the LEAF for about 1 month before being able to charge at home. This was fine thanks to the InstaVolt rapid chargers in our local town.

A photo of the oak post on our driveway, with the Rolec charging point on it and the cable running to our LEAF as it charges.

The install only took one person about half a day. I helped out a little running the armoured cable from the house approximately 30m to the driveway. Interestingly they chose to fit a new small consumer unit (fuse box) inside our meter box which is on the outside of the house, rather than mess around trying to connect to the main consumer unit which is further inside the house. We were lucky we had space in the meter box to do this.

The cost of the electrical work was around £1000 including the Rolex charging point, but after the EV installation grant it only cost us about £450.

Now we can charge whenever we like at home, and basically every day we’re at 100%. Yay. In reality we only need to charge once every couple of days. People like to point out that you shouldn’t charge to more than 80% for best battery health, but it seems this is not relevant any more, and seems frankly ridiculous given that Nissan used to but no longer provide a way to have the car stop charging automatically at 80%.

Surprised by range

Last week, we lost our first “bar” of “battery health”. There are 12 bars on the right of the LEAF dashboard, next to the range indicator. As a battery ages, it loses the ability to maintain maximum charge and this indicates roughly how much of that maximum is now available. When we bought the car it had all 12 despite being three years old. Of course going from 12 to 11 feels like a huge change, but it is to be expected, and it is probably the change from 93% to 92.95% of the maximum charge compared to a new battery.

As with so many of the “digital” stats in these cars it can cause unwarranted anxiety. It also makes you wonder if rapid charging it ~10 times since we got it contributed to this, but it seems this is unlikely. Many LEAFs have hundreds or thousands of rapid charges.

Today we did our first run of the long journey to visit family that I wanted to make sure was not a problem for the LEAF’s range so that we could charge just at the other end before returning, instead of stopping on the way.

I’m pleased to say we made this journey starting on 100% charge and ended at the destination on 34% — for a journey of approximately 57 miles through mixed non-motorway roads in the British countryside; winding, hilly parts, a mix of 60mph, 50mph, 40mph and 30mph roads.

This was without any especially careful driving, and trying to drive at or slightly above the limit of each section — with Climate Control on because it was actually a hot day here in the UK. Compared to the experience of 70mph motorway travel covered in a previous post, this was seemingly much more efficient.

Relatively speaking there is a charge point wilderness in Wiltshire between Swindon and Salisbury, so I’m glad this worked out. At the Salisbury end we got to charge at the Type 7kW chargers at the local Leisure Centre which has 8 (!) bays run by Polar. One of the 4 posts was not working. As I write, after about 2 hours we should be close to 100% again for the journey back later this evening.

There’s only one rapid charger in Salisbury which seems crazy for such a populous town. We used to live in this town and one of the reasons we moved to a house near to Stroud instead was that we struggled to find anybody in Salisbury who cared at all about environmental issues. So this comes as no great surprise — diesel Land Rovers, Range Rovers and large Mercedes don’t need EV chargers.

In terms of estimating range without mental hijinx, my initial rule of thumb still holds – on average 1% of battery charge relates to just under 1 mile of range, and this journey bears that out pretty well.

Type 2 Connectors… the standard from hell

You may have read in a previous post my comments about the complexity of charging connecters.

To recap, Type 2 connectors are typically used for AC fast charging (fast not being as fast as rapid which uses DC, but the only practical charging level for the home). To add spice to this, the LEAF has a Type 1 socket in it — and to fast charge you have to use a cable that is Type 2 at one end and Type 1 at the car end.

There’s also another confounding user experience factor — Tesla also use Type 2 connectors for DC charging. However it turns out there’s even more complexity than I understood

A LEAF can fast charge at 7kW. Some cars like the Renault Zoe can fast charge at up to 22kW. So there are public Type 2 charging points which support up to 7kW and some up to 22kW — and a LEAF can charge at either of those, but will only ever draw 7kW.

There are however seemingly many Type 2 charge points that support up to 43kW. It turns out a LEAF cannot use these, because the Type 2 cable is tethered to the charge point, which means you cannot use your own Type 2 to Type 1 converter cable as you do with all the other Type 2 charging points.

Trying to explain this to someone is incredibly painful. Apps like Zap-Map are too complicated partly as a result of this, and trying to find a charger you can actually use is still way too complicated.