I’ve been thinking about all the generative AI slop that’s appearing, especially with tools like “Reimagine”, and I think it’s going to be a great thing for the open web.
Why?
Because Google is unwittingly shooting itself in the foot in a way that will change the character of the web.
How?
The web has always been built on trust.
The very core of the Internet is built on trust: I try to connect and someone else accepts because I’m using a trusted protocol.
Trust is also an important part in the way people work together: a recommendation from a friend is a hell of a lot more important than any other media (including TV, print, and the web). We also negotiate once a level of trust has been established: just like our protocols.
That trust in people extended to companies that built their business on the Internet. We trusted Amazon to deliver our books. We trusted Google to deliver answers to our queries. We trusted The Onion to deliver us the lols. We trusted Twitter to connect with friends.
And all was good because it was built on top of trusted protocols.
Twitter was the first to break the human trust. Its popularity attracted a lot of bad actors: scammers, bots, and billionaires. And when things started falling apart, many lost trust in the service.
And just like a Nazi bar, when you can’t trust a place, you stop visiting. You find new places like Mastodon, Threads, and Bluesky that are welcoming with people you know and trust.
As these generative technologies get better, you will be less likely to trust what appears in your search results. This change will happen at an exponential rate thanks to slop being generated from other slop.
Search engines you can’t trust because they are cesspools of slop is hard to imagine. But that end feels inevitable at this point. We will need a new web.
What?
The human component of the web won’t change. People will need answers that they can trust. Folks on the web are also resourceful; they always have been.
Something new will fill the gap and give people what they need and want. And my guess is that the open web, personal reputation, and word of mouth will be key components of that thing.
This site now supports Dynamic Type on iOS and iPadOS. If you go to System Settings on your iPhone or iPad, and change the setting for Display & Brightness > Text Size, you’ll see the change reflected on this website.
This is a big win for accessibility: many folks make this adjustment on their device to match their abilities. Just because you can read a tiny font doesn’t mean that I can. It also is a win for consistency: my site’s font size matches the other text that a visitor sees on their device.
The best part is that this improvement can be realized with only a few lines of CSS:
The font-size property sets the default text size for the page. All browsers recognize this setting and so do you.
The new addition is the font property with the -apple-system-body value. This font is the key to getting support for Dynamic Type. This feature has been in WebKit for almost a decade and is fully documented. This property overrides the font-size that was defined in the line above and our page now has a size that matches the system setting for body text.
One unfortunate side effect of the font value is that it also sets the page in the system font. I like San Francisco, but I don’t want it on my blog.
With a hint from Mastodon, it occurred to me that I could override the face with font-family. So I now have the best of both worlds: a size that makes my visitor happy and a font that makes me happy.
One other addition that I made to my CSS was a tweak for desktop browsers. There is no Dynamic Type setting on macOS (yet?!) and the default size was a bit small for my taste. A @media rule fixed that:
@media screen and (min-width: 801px) {
body {
font-size: 1.2rem;
}
}
Now any browser window that’s wider than 800 points will get a slightly larger font.
You can, of course, use any of the other predefined font values, such as -apple-system-headline or -apple-system-footnote, but you’ll also need to override the family with each use.
But it’s likely that you’re already using em and rem sizes so that elements scale correctly in other contexts. By setting the base size in the html element, my rule for headers “just worked”:
Another important point: if you’re using WKWebView or SFSafariViewController on an Apple platform, it will have the same capabilities as you’ve seen above. This means that you can have dynamic text in a SwiftUI view and a web page that matches exactly. This is why I needed to solve the problem in the first place.
Take a moment to look at your blog, product, or company style sheet and think about how this approach to accessibility can improve things. If you’re like me, in a couple of hours you’ll have a much better site.
A toot by my friend Casey brought back some frustrating memories about expired subscriptions that haven’t expired (yes, really). This blog post will hopefully help you avoid having these same recollections.
It all begins when a customer contacts you with a screenshot that looks something like this:
Your code and the App Store don’t agree about when a subscription expired. The cause of this is Apple’s StoreKit sample code. It’s likely that you have some code similar to line 246 of Store.swift:
That code will work fine until you encounter a customer that has Family Sharing enabled, as most do. The issue is that the Product.SubscriptionInfo can contain multiple items, and the code above only checks the first one.
How can that happen? With Family Sharing, the people who are using the subscription act independently: one may subscribe for a year and then cancel. Then another could subscribe at a later date for only a month. You have to check all of the subscriptions, not just the first one. Something like this:
The documentation and sample code doesn’t say it, so I will: Apple’s StoreKit sample doesn’t support Family Sharing.
If you’re looking for code from Apple that does support Family Sharing, you can find it buried in one of the WWDC demo apps. Obviously.
What’s most frustrating about this situation is that you know it exists if you’ve read the documentation:
The array can have more than one subscription status if your subscription supports Family Sharing. Provide the customer with service for the subscription based on the highest level of service where the state is subscribed.
Which makes no sense until you’ve read the paragraphs above.
Actually, I was wrong. The most frustrating thing about this situation is that it’s essentially untestable. You can’t reproduce the problem, even after a customer lets you know they’re having issues and you’ve read this blog post. That’s because there is:
No way to test this in Xcode (even if it’s turned on in .storekit configuration).
No way to test this in TestFlight (fake purchases don’t use Family Sharing).
No reasonable way to test this in production (red flags will be raised with refunding and changing purchases repeatedly while testing with real Apple IDs).
The StoreKit test harness in Xcode has been a godsend, but in this case it’s just not up to the task. And the result is lots of frustrated developers who are testing code in production on a customer’s device.
Apple folks: you can learn more in FB13212468. It’s been closed as “Investigation complete” — maybe you should ask Casey if he agrees with that resolution.
On Mastodon, Alex Chaffee points out some of Tapestry’s shortcomings. These are all valid concerns and I’ll deal them individually here (rather than with a long toot thread).
iOS-only
Building for iOS first is a strategic choice. There is a lot of work to do here, and many new concepts that need to be designed and developed, so we’re picking our battles carefully.
Personally, I’d prefer to do a macOS client first, but iOS is a choice that makes the product available to as many folks as possible.
We also get asked a lot about supporting Android and that’s something, like macOS, that we will look at after we have a strong footing with this new concept.
But what about the web?
Another platform we get asked about frequently: can Tapestry be a web app? We covered this in a FAQ, but I’d like to add a little more detail here.
The only way a web app could be done is if there’s a server to marshal requests to the various services. Since we want the privacy and independence of a device-only design, that option is off the table.
If you’re working in a single browser window as a web app, you put all of your code into a single secure JavaScript context. That’s not a problem, but any code you pull in will need things to communicate with all of the services, and in most cases that will leak private authentication data like OAuth/JWT keys and tokens. All it takes is one malicious plug-in to make your life hell.
The root of the problem is that Tapestry’s fundamental design is different than a web browser. The easiest way to think about: it’s an app that asks a bunch of browser tabs if they have anything new to show. Each tab is secure and none of them know what the others are doing. The results that each tab provides are aggregated and displayed to the user.
This design is wholly different from the web we’ve known, but is just as flexible and adaptable. Building a new thing for the community of the open web is what excites me most about this project. Giving folks tools to be creative is what the web has always been about.
Again, we are in the very early days of this project and have no idea where things will head. It’s like if you had asked me in 2006 if a shithead would be running the show in 2024.
I think it’s important to think about how Netscape Navigator was a proprietary product until Mozilla happened. Establishing a new idea is a first step; letting it flourish is another.
All I can say at this point is that I’m fully committed to letting this idea flourish.
Scraping
There is no such thing as a proper web scraper for authenticated content. It’s a cat-and-mouse game where the scrapee wins in the end. We have every reason to believe that Facebook or Instagram will go to great lengths to protect “their” data.
On the other hand, if you want to get standardized information from a page, such as OpenGraph, that’s already something we’re doing.
Posting
The prototype I built (named Muxer) was able to post to Mastodon and Micro.blog. But it’s a harder problem than you first think.
The issue is that each service has a different set of capabilities. On Bluesky, you can’t post video. On Mastodon, you can post polls. With RSS, you can’t post at all. Size limits and file formats differ wildly (including between Mastodon instances).
You quickly end up in a situation where a user interface gets confusing. For example, you have a video ready to post on Mastodon and decide that you’d also like to send it to Bluesky. As soon as you do that, the post button gets disabled and it’s hard to explain to a user why that happened.
It’s also not clear in my mind if cross-posting between services is a good thing or not. Once you have an app that can display information from many different sources, it quickly gets annoying to see duplicates.
As we learn more about this product, and what people want from it, we’ll have a better idea of how to handle posting.
And more…
I’m always happy to talk about Tapestry and explain the thought processes behind it. If you have questions or concerns not covered here, please feel free to reach out on Mastodon.
Last week’s 40th anniversary of the Mac got me thinking. I’ve also been contemplating this week’s release of Apple Vision Pro.
It feels like we’re at a crossroads for platforms, but one that’s impossible to pass.
I was one of the folks who bought a Mac in 1984. At the time I was a member of a team building a Unix workstation from the ground up. We had bigger displays, better networking, faster processors, more memory, and larger disks.
But we were all jealous of what the team at Apple had done. That first Mac and its system software was brimming with new user interface ideas and techniques. Better ways of doing everything we had done.
And you can say the same thing about Apple Vision Pro and visionOS.
Except there is a problem.
Processes
If you’re a software developer, the Apple Vision Pro cannot be used standalone for your work. You’ll be able to use it as much as you do an iPad. You can experiment in Playgrounds and build some simple apps, but you’ll quickly hit a wall.
That’s because developers use a lot of processes. And these processes talk to each other in very creative ways. Maybe it’s as simple as creating child processes to handle work. Maybe it’s a more complicated process like a Docker container running a web server that talks to a database process via a Ruby on Rails process. There are processes everywhere you look.
And in an Apple sandbox, you get one process. You can’t fork and exec a child. And if you query the Mach kernel for information about another process, you get back KERN_FAILURE.
(To get a very good idea of what’s possible at the fringes of a sandbox, take a look at a-Shell on your mobile device. It does an amazing number of things, but you’ll quickly feel frustrated that ps, kill, top, and anything else that deals with processes is missing.)
There is a good reason for apps only having visibility of their own state. Imagine the kind of fingerprinting that Google and Facebook could do by seeing what apps you’re using. We’ve already seen apps trying to do the same thing using URL schemes.
When my pal John Gruber talks about Macs doing the heavy lifting, it’s not just about complex and resource-intensive tasks. It’s also about the security exposure: the Mac is the only “dangerous” Apple platform.
Windows
There is a thing that developers love almost much as processes: windows. We have so damn many. Hundreds on a good day. Thousands on a really good day.
And this is why I get frustrated every time I see a demo of Apple’s headset. I can easily imagine fitting my work into a space with an infinitely large interaction surface.
But you’ll still be carrying the Mac around to get any work done. Somewhat ironically, the Apple Vision Pro is not doing the heavy lifting, but it will be the thing that’s cumbersome in your daily life.
Here’s a comparison of the headset’s carrying case and a MacBook Air:
The Apple Vision Pro is almost 15 times taller than the MacBook Air. Even worse, I can’t even close my backpack, much less fit in a laptop:
After the Mac was introduced, you didn’t have to carry around an Apple ][ or Lisa to do software development.
This isn’t a sustainable situation for the next 40 years. Without some low-level structural changes in visionOS, it will never thrive as a developer platform. Just as the iPad has not.
It also doesn’t bode well for the Mac. I’m sure Apple can continue to add incremental changes to satisfy developers, but there won’t be anything revolutionary with how we work. There is also little incentive for Apple to change here: you are buying an Apple Vision Pro along with a MacBook, after all.
One of the extrordinary things that happened back in 1984 was the ability to have more than one terminal window. Even though my Mac had to be connected to a VAX 11/780 over a serial cable (sound familiar?), this was a completely new way of working. We were suddenly free of working within the confines of a single 24×80 character display.
And here’s the thing: developers don’t come up with these ideas unless they have a place to experiment. Seeing multiple windows that contained code, debugging, and other tools led some folks to start thinking about integrating this environment using the new interaction mechanisms.
Those same kind of folks may find inspiration in spatial computing, but will ultimately get thwarted by the restrictions of a single process. An architecture developed for mobile devices with only one app on the screen is now being used for apps on an infinitely large screen.