Tom Insam

I’ve been experimenting migrating Flame from ObservableObject to Observable.

It’s very simple to have SwiftUI watch these obejcts for changes, but because of the legacy navigation I have in the app, I rely on the ability to watch the object for changes from normal Swift code. Previously I was subscribing to the Combine publishers for this, but now that I’ve moved to the new macro, I’ve been missing this simple functionality.

Here’s a simple extension that makes this easier:

public func observeObject<T: Observable & AnyObject, S>(
    _ object: T,
    keypath: KeyPath<T, S>,
    onChange: @escaping (S) -> Void
) {
    withObservationTracking {
        _ = object[keyPath: keypath]
    } onChange: { [weak object] in
        guard let object else { return }
        Task { @MainActor in
            onChange(object[keyPath: keypath])
            observeObject(object, keypath: keypath, onChange: onChange)
        }
    }
}

And here’s how you use it:

observeObject(viewModel, keypath: \.selection) { [weak self] selection in
    self?.valueDidChange(selection)
}

The Flame codebase change is here.

Apple Photos

Photos: “Here’s a favorites album! You can put the photos you like the most in it! There are other albums, and you can share the whole library with your family, but not your albums. Apart from the favorites album, you have to share that one.”

Watch: “You can sync any album of photos you want to your watch! But just one.”

Lock screen: “You can have a rotating collection of photos on your lock screen now! What’s that? Albums? Favorites? What’s an album? Nah, I’ll pick the photos for you, but I have magic, so I know you’ll love them. I don’t care what you marked as favorite. And no, if you actually like one of them, I won’t show you where it is in your library, that would be too easy. No, you can’t just use an album.”

iPad lock screen: “Hahaha no.”

Watch: “Magically chosen photos that you’ll love? Interesting! No, you can’t use those. Just albums.”

Photos: “No, why would you want to see the specially curated list of magical photos the lock screen thinks you’d love? That’s absurd. Here’s a set of unrelated Memories that I know you’ll enjoy.”

Lock screen: “Don’t be silly, you can’t put Memories on the lock screen, why would you want that? My selection is better.”

Watch: “Sure you can have Memories on the watch! No you can’t pick which one.”

Third party API: “No of course you can’t let users pick photos from any of these curated lists, that’s silly.”

[This is a lightly-edited flow-of-consciousness thing taken from a slack conversation]

Is it worth trying to write complicated Codable implementations in Swift to map your network model to the local business model you prefer? It’s a lot of complicated code, but maybe having a single representation of your data is worth the trade-off.

I hate doing it, but no. I always end up with a network model and a database model and a “used in the app” model and mapping methods between them.

I hate it because it’s irritating, because the models are always almost the same as each other and it grates maintaining the conversions which are always boring 40 line functions of self.foo = other foo over and over. (At least now we have swift you don’t get weird bugs where some of your mappings forget to instantiate all the properties.) It feels like it should be possible to build a magical perfect object that you can decode from the wire and then put directly into the database, then pull it out and power the UI from it.

But the Codable example is an excellent demonstration of why you can’t really do that. Your wire protocol isn’t quite what you want, so to have a wire object that’s also your business object you need to write a ton of Codable conformance code. (You can’t write just a little bit - Codable auto-conformance is all or nothing.) In fact, you need to write so much code that it would be easier and more maintainable to just write a simple-as-possible wire-format Codable container and a mapping function. For all intents and purposes the Codable conformance is a wire format object, you’re just writing it in an inconvenient syntax.

Your database representation needs to be much flatter than the business object - your model is a deep structure but you don’t want 300 tables and 600 joins so you need to flatten out the deep object to a single table - you’re doing the same thing with custom SQL statements to serialize and un-serialize your rich object, whereas a flat object and a mapping could write to the table without custom code, and would be easier to read.

And that’s even before getting to things like “it would be super convenient if my view data models were immutable, so I can use swift UI / redux” but core data models are not only mutable, but some other thread can mutate them for you with no indication that things changed unless you’re explicitly observing for that sort of thing.

More complication - there are actually 2 (sometimes 3) network representations, because you sometimes need to send objects back to the server. The create call (normally) won’t have, say, an object ID or a created date, but those properties are non-optional on your network model for incoming objects, so your create call object is different from the get call. And your update call probably wants everything to be optional so you can update only one property at once.

So no, your life will be easier if you just write multiple specialist representations of your objects for different contexts, keep them all as simple as possible, and write mappings between them.

No True AI

Shower thought, but I’m getting a certain amount of “no true scotsman” vibe from my twitter stream recently about AI. “Even if it does pass the turing test it’s not sentient” which is interesting! When people are presented with something that passes the previously impossible-to-approach barrier they set, do they accept that it passes the barrier, or was the barrier wrong? Were we just not really seriously considering the barrier as a good test until something arrives to challenge it? Or is it an indication that the entire problem is bad? Why does this test matter? etc. We just don’t believe in intelligent computers as a society, and so pre-writing a test decades ago doesn’t help at all, because we’ve used the test until something “passes” it (to be clear, this expert system is not intelligent, but that’s not the interesting thing here) and as soon as something passes it we’ll move the test.

Feels somewhat like the mistake of the google engineer is that they jumped from “I can have a conversation with this” to “therefore we should never turn it off and it should be allowed to vote” and when/if intelligent machines arrive they’re not going to get that. I have a feeling that (assuming we can build intelligence) we’ll end up with star wars droids - intelligent, have personalities, you can make friends with them, they’ll have wants and dreams, but at the same time absolutely everyone in society (including the droids) accepts/assumes that they’re slaves / property / subhuman and have no rights.

Egyptian calendar, dating system established several thousand years before the common era
...
[It] consisted of 365 days organized into 12 months of 30 days each [..] There was apparently no attempt to introduce a leap-year day to compensate for the slippage of one day every four years; as a result, the civil calendar slowly rotated through the seasons, making a complete cycle through the solar calendar after 1,460 years.
..
The Egyptian civil calendar was altered by Julius Caesar about 46 BCE with the addition of a leap-year day occurring once every four years.

Egyptian calendar, Encyclopædia Britannica

Code Points

A commonly touted disadvantage of UTF-8 is that string indexing is O(n). Because code points take up a variable number of bytes, you won’t know where the 5th codepoint is until you scan the string and look for it. UTF-32 doesn’t have this problem; it’s always 4 * index bytes away.

The problem here is that indexing by code point shouldn’t be an operation you ever need!

[..]

Unicode itself gives the term “character” multiple incompatible meanings, and as far as I know doesn’t use the term in any normative text.

Let’s Stop Ascribing Meaning to Code Points

Pendulums

So while it's nice that I'm able to host my own email, that's also the reason why my email isn't end to end encrypted, and probably never will be. By contrast, WhatsApp was able to introduce end to end encryption to over a billion users with a single software update.

The ecosystem is moving

Chromium vs Android

There are a couple reasons why we open activities launched from Chrome with NEW_TASK flag. The biggest reason is that it provides a much clearer user experience: the user sees the new app as a separate entry in recents -- also, if we were to place a different application on top of Chrome activity, the Chrome icon may end up opening that application sitting on top of Chrome, which is fairly confusing for the user.

-- Bug 445956, wherein the Chromium developers consider the Android task metaphor too confusing for users and work around it.