1. Alexey Naumov in Performance Battle: AnyView vs Group:

    I cannot say there is a clear leader among AnyView and ConditionalView, but the myth about negative AnyView performance implication is BUSTED.

    I take most of what I see on Twitter with a grain of salt, especially when it’s technical advice that’s not backed by examples or reproducible benchmarks. In the case of SwiftUI, there is no shortage of guesswork and hearsay given how under-documented the framework is. And while I appreciate the openness, the occasional tweet from members of the core team is not enough to fill the knowledge gap.

    Luckily, there is always an industrious soul in the community that rises up to the task of winnowing the chaff from the grains.

  2. PeerTube 2.0

    The nice folks over at Framasoft have been doing a tremendous job with PeerTube in general, and this release in particular.

    For those unfamiliar, PeerTube is the Mastodon of video—a federated video platform that uses the ActivityPub protocol to connect multiple independent instances. Any individual or organization can run their own instance and have total control over their content and communities.

    I am very excited to see this platform evolve and get more traction the world over. Hating on YouTube is cliché these days, but the service deserves every bit of it. And then some.

    Now, I understand that people making a living out of content creation can’t move to other platforms willy nilly, but that’s exactly the reason why it’s important to get behind projects like these.

  3. Inset Grouped Lists in SwiftUI

    When SwiftUI shipped earlier this fall, it didn’t provide any APIs to use the newly introduced inset style for grouped lists and table views. The latest release seems to have addressed that, albeit silently and not in the way that I had expected.

    As of iOS 13.2, grouped lists will use the inset style when the horizontal size class environment value is .regular, such is the case with fullscreen scenes on iPad.

    Grouped Lists

    If you want to forcibly enable or disable this style, you have to manually set the size class environment variable, preferably directly on your list to avoid any unintended side effects:

    List {
      Text("Item 1")
      Text("Item 2")
      Text("Item 3")
    }.listStyle(GroupedListStyle())
    // To enable the inset style
    .environment(\.horizontalSizeClass, .regular)
    // To disable the inset style
    .environment(\.horizontalSizeClass, .compact)

    While I am not aware of any issues caused by this approach, I’d have preferred if Apple introduced a dedicated InsetGroupedListStyle or, even better, exposed the underlying ListStyle protocol instead.

  4. Embrace the Darkness

    Dark mode support for this website, fresh out of the oven.

    I have a soft spot for dark interfaces. So much so that the several instances I had to switch back to light mode while working on this made me feel inexplicably uncomfortable.

    Since I had already done the legwork of using CSS variable for the colors used throughout the site, it was relatively easy to implement this new dark theme. Here is the commit for those of you interested in the nitty-gritties.

    Currently the site takes the same appearance as the OS, but I am entertaining the idea of adding a manual toggle sometime down the road.

  5. Swift Server Work Group Annual Update 2019

    I absolutely love the work that the SSWG has been doing, and this annual update published by Tanner is a testament to this.

    The lack of consensus on how to write server-side Swift has been dividing its fledgling community; something that the SSWG has been working towards solving since its inception.

    I am hoping to be able to contribute more to this ecosystem, and I urge anyone who’s interested in making Swift more ubiquitous to do the same.

  6. As a developer, there are days when I feel fortunate to work with bleeding edge technology. It’s empowering, eye-opening, and above all, fun.

    Then there are days when I feel like I’m wasting a chunk of my life working with broken, undocumented, and unfinished tools that work against me at every turn.

    I’ve come to accept that living with this ebb and flow is an integral part of professional life in this field. But sometimes I can’t help but wonder if we can do without the high mental and emotional toll that comes with this churn.

  7. A Note About Assumed Audiences

    You might have noticed that I’ve been opening every Unredacted article with a sentence describing the assumed audience. The intention behind this is to help readers quickly determine if the content aligns with their interests, especially given the non-thematic nature of the blog.

    I first saw this pattern on Chris Chrycho’s blog, and immediately fell in love with it. The ability to juggle between the generic and the specific, the momentous and the mundane, the subjective and the objective, is the main selling point of a personal blog. This editorial freedom, however, often comes at the price of readership loyalty.

    Adopting this habit as an author should in theory alleviate the burden for visitors, both new and returning, to winnow the content to their liking.

    Granted, it’s not an exact science, and I welcome everyone—especially the curious among you—to read past the disclaimer even when it doesn’t appear to be an exact match at first glance.

  8. Update About Nope & Syndicate

    Nope & Syndicate

    Since Safari 13 was released last week on macOS, I’ve been getting an unusually high amount of inquiries about the status of Nope and Syndicate. This is the TL;DR of the response I’ve been copy-pasting so far.

    I’ve been planning to update these extensions since the deprecation announcement, but I haven’t managed to find the time to do that yet. This means that users who relied on either will have to find alternatives in the AppStore.

    Both Nope and Syndicate are open-source and will remain so in future iterations. While I don’t have a concrete release window yet, I still believe in the importance of investing in open-source, community-driven browser extensions and content blockers.

  9. Sneak peek of something I’ve been working on this past couple of weeks.

    SwiftUI Directory

  10. Impressions from the Apple Event

    Some of my impressions from the Apple Event from last Tuesday:

    • The intro video was fun to watch, for once.
    • Tim skipped the numbers update this time around. Good riddance.
    • Apple Arcade is at least 4 years late. I’ll bite, regardless.
    • Apple TV+ is cheaper than I, and many others, had expected.
    • While I got used to the notch, I could get used to not having it. Too bad it’s here to stay another year, at least.
    • Hats off for showing the name of the photographers and artists when showcasing photos and videos.
    • “It’s so pro.”
    • Save for the Watch’s always-on feature, the event didn’t quite live up to its title, “By Innovation Only.”
  11. On Truncating Feed Content

    While skimming my RSS subscriptions this morning, I came across this tidbit from NetNewsWire:

    On the Many NetNewsWire Feature Requests to Show Full Web Pages

    […]

    There is a solution to the problem of showing full content and not leaving the app, and it’s a feature that really does belong in an RSS reader: using content extraction to grab the article from the original page.

    If you’ve ever used Safari’s Reader view, then you know what I’m talking about. The idea is that NetNewsWire would do something very much like the Reader view (but inline, in the article pane), that grabs the content and formats it nicely, without all the extra junk that is not the article you want to read.

    Let me be clear about this: Truncating content in RSS feeds is, without a sliver of doubt, an accessibility issue first and foremost. Accessibility as in making your content accessible to your readers wherever they chose to subscribe to your feeds—be it a mobile app, a command line, or through a screen reader. Undermining this aspect and coercing them to visit your website, no matter how much love you poured into it, is short-sighted at best, and inconsiderate at worst.

    I get that authors need to pay the bills, and some of them resort to sponsorships or ads to do so. But it’s user-hostile to presume that your subscribers don’t know any better and that they can only enjoy your content directly on your website.

    Authors who care about their readership have already figured out perfectly legitimate ways to monetize their feeds without any content truncation. Those who don’t are unlikely to bother and will continue to do a disservice to their readers.

    In the meantime, industrious developers will keep finding workarounds to restore functionality that users yearn for. And nothing can stop that, because open always wins.

  12. I’ve been running into several abstruse issues when smoke testing this website’s feeds in some RSS readers, including the newly released NetNewsWire 5. Upon further investigation, I found out that due to a glaring misuse of some Node.js APIs, all URLs have been missing a / after the https part1.

    The bad news? Once the fix is deployed, most RSS readers will reset the read status of the entire feed, since the individual entries are uniquely identified by their URLs. What’s worse, readers that cache previous entries will end up with duplicate content.

    I apologize for the inconvenience that this might cause and promise to be more careful to avoid syndication-related bugs like these in the future.


    1. For the curious, I inadvertently used the path.join(…) method instead of url.resolve(…) to construct these URLs. The former is only supposed to be used in the context of file system paths, not URLs.

  13. By default, git fetch gets an updated list of remote branches from the remote and sets up remote branch references locally. However, it doesn’t delete stale references that no longer have a counterpart on the remote. Trying to get rid of these using the git push -f origin:branch-name command will fail with a remote ref does not exist error.

    To delete stale remote references, you need to pass the --prune option to fetch like so:

    git fetch --prune
    - [deleted]  (none) -> origin/branch-name
  14. On Keeping SwiftUI DRY

    If we were to represent the hierarchy of our refactoring itches as a pyramid, eliminating duplication would be at the very bottom. The DRY principle, as it is often referred to, is the uncontested cornerstone of good development practices.

    SwiftUI, as new and unpolished as it is, already exposes some powerful APIs for that end. We’ve been using view extensions, view modifiers, and preference keys on a constantly growing codebase at work. If you’re looking to learn more about these largely undocumented capabilities, look no further than these two fresh posts:

  15. @kaishin onPreferenceChange() did not change 😉 It always required Equatable (how would it detect changes otherwise!). What did change though, is that Anchor no longer conforms to Equatable 😕

    — The SwiftUI Lab (@SwiftUILab) August 3, 2019

    I previously assumed that onPreferenceChange(_:perform:) added the Equatable requirement, but it turns out that Anchor<Value> dropped its Equatable conformance instead, as pointed out by @SwiftUILab and the official documentation.

    A curious change, regardless.

  16. Undocumented View Preference Changes in Beta 5

    Update: My assumption was wrong. Corrected here.

    I haven’t seen this mentioned anywhere, but the onPreferenceChange(_:perform:) view instance method now requires the preference key value type to conform to Equatabale as of Beta 5.

    In practice, this means that oPreferenceChange can no longer be used with preference keys that have an Anchor<Value> as their value type or a as a property of their value type, since Anchor itself doesn’t conform to Equatable. I fixed this by passing the GeometryProxy instance to a custom view modifier that unpacks the anchor first using the provided proxy, then passes the result as a CGFloat instead.

    For more on preference keys and anchors check out this informative blog post series.

  17. My Screen Time breakdown for last week. The time I used to spend on Twitter and Mastodon has shifted to reading, writing, and—ahem—browsing Reddit.

    Screen Time Summary

  18. Hello, world.

    Not long ago, I started toying with the idea of rolling out a custom-built microblog instead of relying on third party platforms. Today, I am happy to launch a modest first iteration.

    I will keep my single-user Mastodon instance and Twitter for the time being, but I will use them mainly to syndicate posts that will be published here first.