The Breakroom

Masto-do or Masto-don’t?

January 27, 2023

By Webmaster

Let’s just say that January 12th was expected, yet still surprising. We knew the cutting and slashing at Twitter would affect us at some point, but how we’d get eviscerated was an unknown.

Many other people saw it was coming as reality began to sink in at the end of October. We’ve been asked, countless times: “Are you folks working on a Mastodon app?”

The answer isn’t a simple one.

(But honestly, we loved seeing all the creative names that people have come up with when asking the question!).

First Things First

Our current priority is completing chapter 3 of Frenzic: Overtime on Apple Arcade. The game has been immensely fun to work on and we’re grateful Apple gave us a long-awaited opportunity to revisit our first game for the iPhone. We’ll be working on Frenzic until late spring of this year. We’re a small team and don’t have the resources to work on more than one major internal project at a time. Any new social media app will unfortunately have to wait.

Beyond Twitter

Even though we’re not currently working on an app, we’re all definitely thinking about the Fediverse and are active on Mastodon. We feel like this is a good time to step back, slow down, and think about where we want to head post-Twitter.

As we speak, teams of talented developers are building a plethora of great apps for Mastodon. It’s going to be a crowded, more mature market, and we don’t like being latecomers. We have a long history of firsts on Twitter: the first app ever in 2007, the first iPhone app in 2008, and the first iPad app in 2010.

However, the Fediverse is bigger than Mastodon: A new thing called ActivityPub is being used to power not just Mastodon but a lot of other interesting services.

This open standard is exciting, and it’s just one of the things we’ll be exploring and experimenting with as we decide which direction to take later this year.

Stay Tuned

So yeah, a simple question without a simple answer.

But one thing is for sure: When the time comes, we won’t be able to do it without your help. We’re grateful for all the love you’ve shown us and Twitterrific in the past, and we know we can count on you for support and feedback for whatever comes next!

To keep apprised of what our future looks like, make sure to follow our new @Iconfactory account (on our shiny new Mastodon instance!)

Announcing Linea Sketch 4.2

January 24, 2023

By Ged Maheux

Today’s Linea update brings several new and exciting features designed to give you more options while sketching and when moving images into and out of the app. Version 4.2 also includes a bevy of improvements that reduce friction when transforming artwork, provide new and updated templates, and much more. 

The Nitty Gritty

Linea’s new Organic Ink makes it easy to create textured strokes and fills when sketching. Use the Pen tool to create rough-edged strokes, and even vary the size with pressure sensitivity. It also works in conjunction with the Fill tool to quickly create solid areas with organic edges.

Organic Ink gives your sketches a rustic and natural appearance that is unlike anything previously offered in Linea. You can even tilt your Apple Pencil as you draw to quickly shade wide areas with texture.

The Ins and Outs

Linea 4.2 brings the ability to import and export a much wider range of file types that make your workflows faster and easier. Import multiple images simultaneously, including PNG, JPEG, TIFF, and even Photoshop PSD files. Linea also supports the ability to import multi–page PDF files to make annotating documents quick and painless.

Users of the venerable drawing app Paper, by 53/WeTranfer, can now easily transfer their creations into Linea. When imported via iCloud, Paper files are converted into a layered sketch document, perfect for further refinement in Linea.

Something Bold, Something Undo

ZipLines now respond to pressure from the Apple Pencil as you draw, which means one end of the line can be thin and light and the other can be thick and bold, or any combination. The line updates as you drag the end point and adjust the pressure, allowing you to get exactly the result you want.

ZipLines now respond to pressure as you draw allowing you to vary the opacity, thickness and shading on the fly.

Undoing now restores each step of a ZipShape transformation, all the way back to your original stroke. This means you no longer have to start over each time you transform a perfect square, circle, or polygon, which is a great time saver.

A Few More Things

We’ve added a new, larger 1×1 grid and a new iPhone design template that includes Apple’s Dynamic Island. Sketches also remember if their orientation was locked via the Canvas Compass, so you don’t have to re-lock each time you open them.

Version 4.2 also includes improvements to the iOS sharing extension, subtle efficiencies to the user interface when selecting layers, the ability to merge selections into completely different layers, as well as improved handling of large images when they are imported into Linea.

Today’s update is the perfect opportunity to see why so many people call Linea their favorite sketchpad. Visit Linea’s version history page for the complete list of what’s new, and then head on over to the App Store and grab the FREE download of Linea Sketch. It’s where your ideas begin!

Twitterrific: End of an Era

January 19, 2023

By Sean Heber

Twitterrific has been discontinued.

A sentence that none of us wanted to write, but have long felt would need to be written someday. We didn’t expect to be writing it so soon, though, and certainly not without having had time to notify you that it was coming. We are sorry to say that the app’s sudden and undignified demise is due to an unannounced and undocumented policy change by an increasingly capricious Twitter – a Twitter that we no longer recognize as trustworthy nor want to work with any longer.

Since 2007, Twitterrific helped define the shape of the Twitter experience. It was the first desktop client, the first mobile client, one of the very first apps in the App Store, an Apple Design award winner, and it even helped redefine the word “tweet” in the dictionary. Ollie, Twitterrific’s bluebird mascot, was so popular it even prompted Twitter themselves to later adopt a bluebird logo of their very own. Our little app made a big dent on the world!

None of those amazing achievements would have been possible without the generous and loyal support of you, our wonderful customers and fans. Your financial support may have paid the bills, but your spiritual support enriched our souls and for that we can never thank you enough. You changed our lives forever.

But, as much as it pains us to say it, Twitterrific for iOS and macOS have now been removed from both App Stores. If you had a subscription on iOS, it will be automatically cancelled by the App Store.

Finally, if you were subscriber to Twitterrific for iOS, we would ask you to please consider not requesting a refund from Apple. The loss of ongoing, recurring revenue from Twitterrific is already going to hurt our business significantly, and any refunds will come directly out of our pockets – not Twitter’s and not Apple’s. To put it simply, thousands of refunds would be devastating to a small company like ours.

While this chapter may have ended, our story is not over. As long as we’re able, we’ll continue improving our other apps, creating new apps, doing amazing design work for our clients, and posting awesome wallpapers to Wallaroo and Patreon. Stick around!

State of the Twitterverse

January 13, 2023

By Ged Maheux

Last night at about 7:30pm PST, Twitterrific customers started reporting problems accessing Twitter via the iOS app.

News quickly spread on Twitter and Mastodon that a wide range of third party apps like Twitterrific, Tweetbot, Echofon, and many others had been disabled. Strangely, Twitterrific for macOS continues to work normally. We cannot say for certain why some clients are unaffected, but it seems possible that there is a new (seemingly unstated and unannounced) policy that is only being applied to apps with large numbers of users.

There’s been no official word from Twitter about what’s going on, but that’s unsurprising since the new owner eliminated the employees dedicated to keeping the API up and running smoothly, including the developer evangelists who previously provided communication with third-parties.

We wouldn’t know whom to reach out to at Twitter even if such people existed. We’re in the dark just as much as you are, sadly. 

As soon as we have a better understanding of what has happened, we’ll update this blog post and let you know. In the meantime, if you own a Mac you can use Twitterrific for macOS (but we don’t know how much longer this will last).

You can also follow @iconfactory on Twitter or find several of us on Mastodon: Ged, Sean, Craig, Talos and Anthony

We’d also like to say thank you to all the people who have reached out to us on Twitter and told us how much they appreciate Twitterrific and our dedication to making Twitter usable over the years. These kind words mean a great deal to every one of us here at the Iconfactory. Quite honestly we wouldn’t be where we are today without your support of our apps like Twitterrific.

Stay tuned and beaks up!

Updated January 17th, 2023: We still have not received any clear communication as to why Twitter deactivated Twitterrific on January 13th. We have been respectful of their API rules, as published, for the past 16 years. We have no knowledge that these rules have changed recently or what those changes might be.

The Year In Review for 2022

December 16, 2022

By Ged Maheux

More than any other year in recent memory, 2022 brought big changes—some good, some bad, and some sad. During our 25 years in business we’ve learned to roll with all manner of punches and continue forging ahead. This year was no different. 

Old Friends

This year brought a string of notable updates to some of our most popular apps. Linea Sketch, loved by artists and designers from around the world, got a big 4.1 update. This brought great features like Clear Ink that turns any of Linea’s drawing tools into textured erasers, and Project Collections that help you stay organized as you create. We’re now working on Linea 4.2 for release early in 2023, and we’re excited by where the app is heading!

In April we unearthed CandyBar (originally launched circa 1995?) and shipped an update called the Sugar Free Edition™, which let CandyBar fans continue to browse their icon collections on modern versions of macOS. Though the app remains officially unsupported, fans were surprised and delighted to find collecting icons still tasted as sweet as they remembered.

Spring also saw the arrival of Chapter 2 of Frenzic: Overtime on Apple Arcade, bringing over 30 new and exciting levels with unique, fast-paced puzzles to solve. Frenzic’s Test Lab also received new daily challenges for players from around the world to compete in for the spot of top bot.

Tot, our tiny hyper-focused note taking app, got an update in the summer which added smart bullets and iOS widgets as well as Apple Watch support a few months later.

Fresh Faces

We launched two great new apps in 2022, both of which scratched some personal itches we’ve had for many years.

WorldWideWeb was introduced in June as a free, personal web server to make developing websites a snap. The app makes running your own web server about as easy as can be and has been welcomed with open arms by the Mac and iOS community. Be sure to check it out! 

We celebrated our love of creating desktops and wallpapers with the launch of Wallaroo, an iOS app that makes it easy to browse our library of hundreds of handcrafted images. When you find one you like, you can use it right on your own device with a tap! The app includes a large collection of wallpapers that our Patreon members been enjoying since 2019, and the best part is that it keeps growing with new releases from us and other notable designers every week. As always, our Patreon subscribers enjoy additional, exclusive content every month both in and out of Wallaroo, plus sneak peeks at upcoming projects.

The Giant Blue Bird in the Room

If any one had told us Twitterrific would still be tweeting in 2022, nearly fifteen years after the app first launched, we wouldn’t have believed you. Despite all the turmoil and changes at Twitter over the years, Twitterrific has persisted.

Our users are some of the most dedicated, loyal, and wonderful people we’ve ever encountered, and if it wasn’t for them, we might have already bowed out of supporting Twitter.

Twitter’s new owner seems to be intent on tearing the company, its employees, and civil discourse itself apart at the seams. What all this means for third party apps like Twitterrific isn’t clear, but until the API flies away, Twitterrific, in its current state, will continue to support the service. We don’t know where it’ll go beyond that.

Saying Goodbye 

Change also came in a profoundly sad way this year. After a multi-year battle with cancer, we lost our dear friend and Iconfactory founder, Corey Marion. Needless to say we were all devastated. Corey was the very heart of The Iconfactory and his presence touched everything we did.

The outpouring of support his family and all of us received was amazing. He was a life that was loved and appreciated by all who knew and worked with him. We still miss him every single day.

Taking Time to Recharge

The road ahead may be uncertain but what’s rock solid is our appreciation of you, our loyal customers, friends, and fans. You make all this possible as we push into the new year and recharge our batteries. We wish all of you a very happy holiday. Stay safe, enjoy your time with family and friends, and we’ll see you all in 2023!

Tot 1.5 – Smarter Bullets and Watch Out

October 4, 2022

By Craig Hockenberry

Our last release of Tot introduced a new feature called Smart Bullets. These special bits of text made it easy to create lists where you could check off items. (Check out “What’s New?” in the app help and you’ll get a quick demo.)

One shortcoming with Smart Bullets was that you needed a mouse to manage them. And now with version 1.5, that’s been improved: there are keyboard shortcuts to both add and toggle Smart Bullets. Your hands never have to leave the keyboard!

We’ve also added support for printing out the contents of your dot. In today’s digital world, that seems kind of a weird feature until you realize that it’s a great way to create a quick PDF for email or to print a one-off shipping label.

Folks on iOS 16 will also be able to take advantage of the new system Find & Replace when editing text. After selecting some text, you’ll find “Find Selection” in the Edit menu. “Find” is also available if there is no selection. If you want to replace text or use other options, tap the magnifying glass menu.

There are also some user interface improvements. If you have aging eyes (/me raises hand) then the adjustable line height in the Format menu (macOS) or Settings (iOS) will be a welcome addition. We’ve also heard your customer feedback and made Tot’s menu bar icon a standard size and appearance. There’s even a new icon that matches the watchOS app.

Your menu bar is getting a new look, and there’s’ a new “Dot” app icon available.

Hello Tot Mini

Wait a second: watchOS app?

Yes, you can now tote Tot around on your wrist. It’s surprisingly handy: one of our beta testers is a distance runner and uses a dot for directions. Shopping lists and other kinds of reminders are also a natural fit.

In addition to viewing your text, you can also make simple additions with dictation or the Scribble keyboard. Full editing on the watch isn’t feasible, but it’s great to be able to quickly get a random thought into a dot and having it sync back to your phone or desktop!

Tot Mini is a watchOS 9 app and available to download as a separate purchase on the App Store. To learn more about Tot, check out the product website. Full details about this and other Tot releases is available in our version history.

Enchant Your Inktober with Linea Sketch

September 28, 2022

By Webmaster

October returns once again with an orange armful of pumpkin spice, a crisp autumn breeze, and a way to channel our creativity every day of the month. Inktober encourages artists, both amateur and professional alike, to pick up pen and pencil and express themselves by crafting a drawing each day based on the official list of prompts.

The official list of drawing prompts for Inktober 2022

For the past three years we’ve contributed to the cause by creating a set of handy templates for Linea Sketch that outlines the prompts for each of the 31 days in Inktober. This year’s list of templates is once again ready for download and broken out by page numbers that contain the prompt for that day’s drawing. The templates are perfect for those who like to have a uniform set of sketches for the entire month or who want to stay tight and focused on their daily creations.

As always, Linea’s Inktober 2022 templates are completely free, easy to set up, and should quickly get you on your way to joining in on the inking fun. Last year we created a short video tutorial on how to get the templates into Linea Sketch for the iPad that will help guide you through the process this year as well. Be sure to check it out

Lastly, don’t forget to tag your posts with #LineaSketch – we always share our favorite drawings over on Linea’s Instagram so it’s a great way to step into the lime light! Apply new screen protectors and charge those Apple Pencils because October will be here before you know it!

Wallaroo and SwiftUI (5 of 5)

September 23, 2022

By Sean Heber

We were getting near the end of Wallaroo development and the app was close to fully functional, but as mentioned at the tail of the previous post, there was still one more important thing that I had to figure out how to implement.

The wallpaper detail view has a popup with metadata about the wallpaper you are viewing. Some of that metadata takes the form of tags and tapping a tag needed to push a new page on the navigation stack.

Normally this would be relatively easily done with a NavigationLink but there was a wrinkle: The button that needed to have these links lived inside of a .popover which, when presented, is not part of the navigation stack! That resulted in the NavigationLink being disabled since from its point of view there was no navigation stack to push anything to.

Wallaroo’s tags let users quickly find similar content across the entire app

Luckily Apple introduced NavigationPath this year and it seemed like it might be possible to use it to solve this problem.

Of course since NavigationPath was a brand new API, there wasn’t a lot of information out there about how to work with it. Many of the quick “tutorial” posts that showed up within hours of the WWDC announcement didn’t help, either. (Unfortunately most were nothing more than reprints of Apple’s simple demonstration code which was immensely unhelpful.)

Those that went farther did things like tuck the NavigationPath into an ObserverableObject which was then added to the environment or passed explicitly to subviews for them to manipulate. I’m certainly not a SwiftUI style expert, but that didn’t feel right to me.

Fueled by my prior research into finding the “right” way to pass what I needed around for the parallax effect, I opted to model a solution based on the way that dismiss and openURL are implemented in SwiftUI itself: As an environment action.

Instead of passing an object around that was nothing more than a thin wrapper of the navigation path, I created an action struct that implemented callAsFunction() just as Apple does with DismissAction, OpenURLAction, RefreshAction and others. The root view then added the action to the environment so all child views could use it.

With this approach, the NavigationPath is private to the root view which remains in control of adding things to the path. The root view also already implemented .navigationDestination for the views in the navigation stack, so it made sense to me that it should own how pages get pushed, too.

When the detail view needs to open a new gallery page, it uses my new OpenPageAction from the environment to request the new page much like this: openPage(.gallery(.tag("abstract"))).

We also sometimes needed to present other standard views that were overlays of the whole UI (like the purchase screen) and all I needed to do was add a case to the Page enum and a new line to the function that opens the pages. The same mechanism that pushes a new view on the navigation stack can also now open a new modal popup without the caller needing to know the difference.

(Note: When the first iOS 16.1 beta dropped, we suddenly had a navigation regression where sometimes empty views were pushed on the navigation stack. I spent an afternoon and isolated a simple reproducible example [filed: FB11518877]. We reached out to multiple Apple engineers that we had contact with and eventually learned this is a known regression which is expected to be fixed before 16.1 ships. I implemented a workaround in the meantime – but fingers crossed!)

The last bit of polish we wanted before shipping was to make the favorite button do something a bit more fun when you pressed it.

Visual flourishes like the Favorite Heart animation add joy and tactility to Wallaroo

Gedeon hand drew a wonderful animation frame by frame as a reference and I set about trying to figure out how to make it work.

My first instinct was that this should exist as a custom view transition, but I spent a lot of time trying to figure out how to make that work and couldn’t get it right. I suspect I was missing something obvious, but I was running out of time and couldn’t spend any more time digging into it so I needed a different way.

I was also torn between deciding if I should find a way to translate all of the frames of Gedeon’s animation to code or just use the images he had already made and be done with it.

Given enough time I could have probably worked out a way to actually draw each of the shapes into an animated Canvas, but ultimately I gave up on that and built a flip book-style animation view powered by TimelineView to play a sequence of Image views.

I set up the flip book animation to play when it first appeared and then to update a binding when it was done which allowed me to overlay it on top of the normal heart button, play when it appeared, and then have it disappear when finished. I think it turned out pretty great!

Building Wallaroo in SwiftUI was quite an eye-opening experience for all of us. There were a lot of unexpected troubles along the way, but we worked through them and found solutions. There were certainly some problems that, in my opinion, would simply have never occurred had we built the app with UIKit – but that said, I’m not sure we would have completed the app in time for the launch of iOS 16 without SwiftUI.

While I ran into a lot difficulties that may or may not be bugs and spent a bunch of time trying to better understand the “right way” to do things for future reference, Craig was able to easily implement the entire set of settings screens, the purchasing flows, Patreon integration, and a bunch of other simple but important views without any significant problems – likely because they all better fit the types of things that SwiftUI was originally designed for in the first place.

In my opinion, a lot of the app’s ancillary views would have required far more code and time to build with UIKit and that’s an important point in favor of SwiftUI despite the occasional trouble in some corners.

I think it’s important to remember that our goal was to build an entire app which means dealing with a lot of the incidental stuff that SwiftUI makes trivial – like experimenting with different interactions, layouts, animations, and even whole view hierarchies. Making quick changes to those things in SwiftUI requires practically no code and absolutely no auto layout constraints, no management of delegates or controllers, and no subclassing all while adapting to different aspect ratios and accessibility settings with nearly no effort. (Plus you can see the changes as you type them if you have the previews set up!)

Clearly there are some blindspots in the framework that need to be addressed, but without trying to use it we could not have known where they were. Just because some parts of the app may have been less troublesome to implement had we used UIKit doesn’t mean SwiftUI itself is a failure or useless. Our new experience will hopefully allow us (and perhaps you) to better pick battles going forward. There’s no rule against using UIKit when necessary, after all – but ideally (in my opinion) we shouldn’t have to.

I hope you enjoyed this deep dive into the Wallaroo development process. For my part, I mostly liked working with SwiftUI once I started to get used to the way it works. There were certainly some points of deep frustration along the way, but in my experience that happens in every single project no matter what technologies are being used to develop it (yes, even UIKit). Unfortunately there will always be things that just don’t seem to work as well as you’d hoped. Maybe someday someone will create the one true holy grail of app development frameworks, but until then, we might as well play with what we’ve got.

Get Wallaroo from the App Store!

Wallaroo and SwiftUI (4 of 5)

September 22, 2022

By Sean Heber

In the last post, I talked about overcoming some issues with Wallaroo‘s main gallery view, but unexpectedly even the simple wallpaper detail screen held its share of challenges!

The biggest surprise for me was how much trouble I had implementing the paging view to swipe left and right between wallpaper variants. In UIKit it’s trivial to enable paged mode on a UIScrollView to get this behavior, but I couldn’t find a way to do it in SwiftUI for some reason. I ran across a bunch of tutorials that were manually implementing panning gestures and paging and I was flabbergasted. Did I really need to do all of this myself from scratch?

I tried a few different open source paging view implementations I found on GitHub but they were all lacking in one way or another.

The most glaring problem with them tended to be that the scrolling physics felt entirely wrong. The swipes didn’t have the same momentum that Apple uses everywhere else and the resistance on the rubber band effect (if it even had it) was never quite right. I started going down the road of adapting one of the better ones with more correct physics after I discovered this gem of a project which meticulously deconstructed the formulas necessary to get an Apple-like scroll view feeling. Still, though, nothing about this approach felt right.

Eventually I decided that maybe I should just wrap a UIScrollView to accomplish what we needed and started to dive into that.

I was pretty far into this when I accidentally stumbled across a Stack Overflow comment noting that SwiftUI actually does have a native paging view – it’s just cleverly hidden as a TabView style!

srsly?

OMFG.

I wasted most of a week on approaches to this before I managed to discover that, lo and behold, SwiftUI actually does have a way to do it – and it’s a single line of code: .tabViewStyle(.page).

I was supremely pissed off at myself for somehow missing this. Despite all of my searching, I never managed to notice any reference to it in the documentation and apparently didn’t remember anything about it from any of the WWDC videos I had watched, either. I suppose perhaps my search terms were too laced with an assumption that it’d be a part of ScrollView somehow or that it would exist as its own view?

I soon learned that SwiftUI didn’t always include support for a paging view – which is why there were so many outdated tutorials and open source projects out there for me to accidentally find instead of the official API. Argh.

SwiftUI changes so much every year and I can’t count the number of times I found an accepted Stack Overflow answer for a thing that was completely wrong as a result. (This is also true of Swift itself – although it’s getting better over time as they’ve become more resistant to source breaking changes.)

There’s also something off about how SwiftUI documentation is written and organized. It frequently feels next to impossible to find the name of whatever view modifier you might be needing unless you more or less already know what you’re looking for. To make matters worse, the fact that SwiftUI’s view modifiers almost all exist as function extensions on View means just pressing the period key and browsing autocompletion suggestions tells you nothing about what might or might not make sense to use in your current context.

The plethora of tutorials, Medium articles, YouTube videos, and books also mostly have a feeling of “magical thinking” to them – as if you just need to memorize incantations to spells rather than learn any of the actual science behind the effects you are trying to achieve. I find that aspect of the SwiftUI ecosystem extremely frustrating and disappointing.

All together these things make SwiftUI feel very difficult to learn and master.

So anyway, after discovering TabView‘s support for paging, I threw away everything I had been working on for a week and used it instead.

Unfortunately it is extremely buggy.

I don’t want to get in to every single problem I ran into with it or this blog series would be about three times longer than it is already, but suffice to say there were a lot. My source file has 6 different bullet points of issues I ran into!

Luckily I was able to workaround them all (one dumb issue was needing to wrap the TabView inside of a ScrollView with scrolling disabled in order to get it to stop jumping around when it first appeared which then of course ended up requiring a second workaround to fix an issue with that workaround…), but the worst, by far, was a memory leak/retain cycle!

We discovered the memory problem around the time I was working on the AsyncImage replacement I mentioned earlier. I had implemented various caching schemes for the images and I kept modifying my approach because it turned out the app was eating RAM and the most obvious culprit was the cache. In fact the initial reason I ripped most of the caching out and revisited it was in an attempt to hunt down this leak!

Unfortunately even with all caching removed I was still seeing a huge memory leak somewhere. I tried using Instruments to find it, but I had no luck deciphering where it was coming from – other than to see confirmation that memory was indeed leaking like crazy.

“Be a software developer,” they said.

I spent two days on this problem.

I don’t mean two work days – I mean pretty much two entire days stopping only for food and sleep.

I was trying everything I could think of to narrow it down. I first started by commenting out random stuff and manually running through the very long sequence of events I had found that could reliably consume the memory. Over… and over…. and over… (And over and over and over…)

Eventually I discovered (around 1am on the second day) that using a selection binding on the TabView was keeping the entire TabView instance alive somehow even when the view that contained it was destroyed.

So over time, after viewing a bunch of wallpapers and scrolling through the variants, all of those huge images were staying in memory even when you went back to the gallery view and loaded a different wallpaper! Slowly, with regular usage, the consumed memory climbed and climbed until iOS killed the app.

My paging view was its own standalone view that had a TabView inside of it. To expose the currently selected tab to the wallpaper details view, I passed in a binding which was in turn used by the TabView. Somehow, though, this is what was causing the problem because when I commented that part out, it wouldn’t leak – but I needed the selection binding to be passed along from the detail view to the inner TabView somehow!

On a whim I tried doing something more indirect and added a new state variable to the paging view which would hold the selection and passed that to the TabView as a binding instead. Then I used .onChanged to note when the selection state variable changed and updated the original binding that was passed into my wrapper view from outside. This appeared to break whatever retain cycle was happening.

I don’t know exactly why this worked (I can only speculate), but some part of my tired and sleepy brain thought to try it and it turned out to solve the issue in the end – but not without some pretty serious frustration and anger.

I feel like I should send Apple a bill for the pain and suffering I endured on this one.

Since this caused me so many problems, I decided that I would share my PagingView code to maybe save someone else some time or see if anyone has any better ideas. (And if we’re really lucky, maybe a SwiftUI engineer will see this and fix it!)

Now that this rather unexpected battle with the paging view was behind us, we were surely on the homestretch! Wallaroo was nearly fully functional – except for one minor detail

Wallaroo and SwiftUI (3 of 5)

September 21, 2022

By Sean Heber

Now that Wallaroo had a unique and good-looking main gallery view along with nice parallax and blurs and everything, my focus shifted to optimizing scrolling performance.

The parallax and lazy custom layout seemed to perform just fine, but rendering the glyphs/buttons on each tile view as well as the actual wallpaper image seemed to be slowing things down a lot.

When I commented each of these bits of code out in turn, scrolling would become silky smooth, but enabling just the glyph/button overlays or just the wallpaper images would result in unacceptable stuttering on my 2016 iPad Pro. Hmm!

Slow and steady might work for races against rabbits, but it’s a drag during user interactions.

Eagle-eyed readers might have noticed earlier that I said I was using AsyncImage “at the time.” Eventually I replaced it with a custom implementation on my quest to achieve smoother scrolling and then extended it a bit more to solve another problem we ran into.

It seemed (at least on the earlier iOS 16 betas), that AsyncImage did not prepare the image for display before rendering it. That meant that when two or three images all loaded during the same view update cycle it would cause animation hitches.

I solved this in my custom view by using a .task modifier so I could async load the image data myself, then convert it to a UIImage, and then call the async version of UIImage.byPreparingForDisplay all before finally assigning the SwiftUI Image instance that would ultimately be displayed to a state variable.

Around this time Craig also realized that as I worked throughout the day my computer was hitting the image server a whole lot and this probably was not going to scale very well once it went live in the App Store.

To address this, I adjusted the URLRequest for the images to always return from the URL cache directly when possible, but ultimately we fixed our server to set the correct HTTP caching headers so the URL loading system stopped rechecking for freshness all the time and trusted that the cached data was good.

If Craig hadn’t been watching our server logs at the time, I’m betting we’d have totally missed this and then had to deal with a flood of server traffic on launch day!

(Aside: It may sound like we arrived at all of this in a matter or hours, but of course we didn’t. I spent a ridiculous amount of time off and on over weeks trying various caching schemes for my custom AsyncImage that prepared the image in different ways, kept the prepared image around keyed by URL, used NSCache or a simple Dictionary, used a Swift actor, etc. Happily I was able to get rid of all of that complication once the required pieces finally fell into place. This sort of thing happens a lot and I often spend a bunch of time on things that turn out to be dead ends. I tend not to stop the moment I get something working, but instead keep thinking about it for a while trying to simplify or get rid of as much code as I can. Sometimes the results are good as in this case, but other times it can go badly where stuff becomes much too specific to a single situation and ends up causing problems for myself later. Such is the nature of evolution, I guess?)

Another of the limitations of AsyncImage that I was able to work around in our custom implementation is that AsyncImage only delivers the final Image after the load finishes and not the original data.

We wanted to be able to access the raw downloaded data of the big images as seen in the full wallpaper detail view so we’d have it when saving or setting the wallpaper. I built a way into my custom image view to get the downloaded image’s raw Data as well as the prepared Image to avoid needing to re-encode the image or download it again separately. There are other ways to solve this problem (I tried a few), but it seemed the most straightforward to do it this way since it ensured the image data was only ever downloaded once.

After getting the thumbnails to load without a hitch, I moved on to the troublesome issue with the glyphs.

There are at most three symbols on each wallpaper gallery tile: The favorite heart, the multi-variant indicator, and the lock (which only shows up on some of them when unsubscribed). When these overlays were enabled, it caused a ridiculous amount of stuttering.

I had a suspicion as to the root cause: Gedeon had wanted the glyphs to be rendered with a custom shadow using a blend mode that didn’t interfere with the semi-transparent favorite heart but also worked against a wide range of wallpapers. This effect was first implemented early in the prototyping stage and hadn’t been revisited.

Wallaroo’s drop shadows use a custom setup so they look good in all states (semi transparent and fully opaque) and against a wide array of backgrounds.

Sure enough when I disabled these effects the scrolling was perfectly smooth. I tried a whole bunch of things to get this to perform better until finally stumbling on the fact that I was using a .mask modifier on the glyph views in order to get the appearance we wanted. When I came up with a much smarter way to make the effect work without masking, the performance skyrocketed. I guess the lesson here is: Don’t use view masking if you can help it!

This highlights one of the weakness of SwiftUI. It is very hard to debug this sort of thing or to get a sense of where time is being spent by the engine because so much of it is inaccessible and outside of our control. Hopefully these things can be improved somehow in the future. For all I know, maybe there are already ways to use instruments to diagnose problems like this – although personally I find a lot of the instruments to be inscrutable and it ends up taking me just as long to trace through a problem with them as it does to hunt around with educated trial and error. But maybe that’s just me.

With most of the issues surrounding the main gallery view taken care of, it was time to finally move on to the much more straightforward wallpaper detail view! How hard can it be?