<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Daniel Gauthier</title>
    <atom:link href="/feed.xml" rel="self" type="application/rss+xml"/>
    <link>https://danielgauthier.me//</link>
    <description>iOS developer and designer writing about indie app development. Doing it from Ottawa, Canada.</description>
    <pubDate>Sun, 29 May 2022 18:09:37 +0000</pubDate>
    
      <item>
        <title>Identity Crisis!</title>
        <link>/identity-crisis/</link>
        <guid isPermaLink="true">/identity-crisis/</guid>
        <description>&lt;p&gt;Hi there! It’s been a while since I wrote anything. There are reasons for that, but they’re all rather nebulous and hard to pin down. That’s &lt;em&gt;sort of&lt;/em&gt; what this article is about. It’s also a retrospective of sorts; a look back at the last year and a bit, which has been wacky and wonderful and horrible all at once. It’s about some career decisions I’ve made in that timespan, and how those have collided with and ricocheted off of the rest of my life, and where I’ve ended up for the moment.&lt;/p&gt;

&lt;p&gt;Truthfully, this one’s largely for and about me, and I don’t think there’s going to be any sort of &lt;a href=&quot;https://danielgauthier.me/2020/01/26/indie-intro.html&quot;&gt;Going Indie&lt;/a&gt;-esque lesson or a perfectly relatable story to take from this one, but I would be absolutely thrilled to have you read along anyways — maybe you’ll see parts of yourself reflected in whatever it is I’m about to write. Onwards!&lt;/p&gt;

&lt;h2 id=&quot;setting-the-scene&quot;&gt;Setting the scene&lt;/h2&gt;
&lt;p&gt;In July 2019, I quit my job. I had been at that job for 3 and a half years, and I cared deeply about it and the people I worked with. I was in a leadership role and had been a big part of significant company growth and change over the years. And yet, I was wickedly anxious and deeply unhappy. There were a variety of complicated reasons for that, but I think the best shorthand we’ve got for what I was feeling is “burned out.” And when I got into a scary car accident at the end of June (everyone was okay, thankfully; the cars were not), my camel’s back gave out, and all the straw it was carrying went &lt;em&gt;everywhere&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/identity-1.png&quot; alt=&quot;Straw breaking a camel's back&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;“The straw that broke the camel’s back”, in case that wasn’t abundantly clear.&lt;/p&gt;

&lt;p&gt;So I left, without really knowing what I was going to do next. Part of me thought what I needed was to simply do nothing for a little while, but I just wasn’t able to stomach that idea – irrationally, I was too afraid of somehow missing out on my next big opportunity, or falling into some sort of perpetual pit of unemployment.&lt;/p&gt;

&lt;p&gt;Part of me thought maybe I just needed a different job to shake things up and challenge myself in new ways. But after putting a ton of effort into several take-home projects for one prospective employer, only to be told quite plainly that my implementation was “poor,” I realized I was nowhere near the right frame of mind to be applying for jobs, and immediately cancelled the other interviews I had lined up.&lt;/p&gt;

&lt;p&gt;Part of me liked the idea of picking up some freelance work, which would allow me to set my own pace and schedule. Through sheer luck and happenstance, I stumbled into a few flexible, long-term contracts pretty early on, and so…that’s what I started doing. I was now officially a freelance iOS developer!&lt;/p&gt;

&lt;p&gt;But meanwhile, part of me also really wanted to commit time to building my own apps, and wondered if there was some reality that existed in which I could make some semblance of a living off of work that belonged entirely to me. So after a few months of focusing solely on part-time freelance work and my mental health, I endeavoured to “become an indie developer” at the start of 2020.&lt;/p&gt;

&lt;h2 id=&quot;doing-the-indie-thing&quot;&gt;Doing the indie thing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://danielgauthier.me/imposter-syndrome/&quot;&gt;This&lt;/a&gt; &lt;a href=&quot;https://danielgauthier.me/hard-left/&quot;&gt;part&lt;/a&gt; is &lt;a href=&quot;https://danielgauthier.me/launch-an-app/&quot;&gt;well&lt;/a&gt; &lt;a href=&quot;https://danielgauthier.me/hang-on-tight/&quot;&gt;documented&lt;/a&gt;, so no need to rehash most of it. Here it is in broad strokes: at the beginning of 2020, I established a 3-pronged approach (patent pending) to getting some independent work off the ground. I started participating in iOS/tech/design Twitter, I started writing some stuff for this blog, and I started building an app.&lt;/p&gt;

&lt;p&gt;This was really great for a while. It was exciting to try to establish something of an online presence and see my writing gain a bit of traction, and the continued freelance work prevented me from going into full panic mode financially while also making it a lot easier than it might otherwise have been to carve out time for the personal writing and app development work I was doing.&lt;/p&gt;

&lt;p&gt;Once the pandemic landed, I came up with a new idea for a silly little app, and decided to put the in-progress indie project on hold to pour my energy into getting that idea out the door. That idea became &lt;a href=&quot;https://ohbother.app&quot;&gt;Oh Bother&lt;/a&gt;, and quite suddenly, a bunch of the things I had been pursuing with this indie work – some real press coverage and recognition online, some non-negligible download numbers, some pocket change, and ultimately, proof that I was capable of designing and building something that I was proud of and that people were into — all became realities.&lt;/p&gt;

&lt;p&gt;That was pretty rad, and I’m so dang thankful to have had that experience.&lt;/p&gt;

&lt;h2 id=&quot;losing-momentum&quot;&gt;Losing momentum&lt;/h2&gt;
&lt;p&gt;Here’s the thing about accomplishments: the warm glow that follows is fleeting, and inevitably, that nagging question — “What’s next?” — rears its head. Practically speaking, I thought what I wanted next was to keep working on Oh Bother and on the blog, and see if I could build off of the momentum and grow them each into more significant projects with wider audiences. That’s still my goal today, but a good chunk of time has passed now since I last did any writing or released any updates, so…what happened here? Where’d all that momentum go?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/identity-2.png&quot; alt=&quot;Me napping on the path from Oh Bother 1.0 to Oh Bother 2.0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s a loaded question, but let’s try to sort it out. First of all, I’d be remiss not to mention a weird truth about humans, which is that it can be hard to stay motivated to work on something once you’ve received validation for it. By my own very modest standards, Oh Bother succeeded, which meant that after release, the onus was right back on me to set brand new goals for this thing. But when you hit your targets and receive a bunch of praise in the process, it can be tough to set new targets that you’re just as genuinely hungry to hit. It’s easy to get complacent and start wandering off toward some other shiny new project. Truthfully, I don’t think this has been a huge concern for me over the past few months — I still love Oh Bother and so badly want to make progress on it — but it’s definitely tricky to avoid this feeling entirely.&lt;/p&gt;

&lt;p&gt;The bigger, often paralyzing issue I started to have after releasing the app was the question of how much of my time Oh Bother really warranted. I always thought of it as a pretty silly little project, but clearly my work had found some sort of audience, which left me really unsure of how much of myself to commit to it. There’s definitely a part of me that wonders if, after the surprise momentum the app built on launch, I should have put everything else on hold to focus on the indie work full-time. Ultimately, I never considered that as a serious option, because of my lack of belief in the app’s ability to maintain users’ interest, and because of my aversion to scrapping the freelancing stuff (a.k.a. the only stuff actually paying the bills).&lt;/p&gt;

&lt;p&gt;So, this left Oh Bother as an evenings-and-weekends kind of side project. Totally viable in theory, but here’s the thing about side projects: in my experience, at least, they’re the first thing to get thrown out the window when life gets shaken up in any way. And while I grappled to figure out how much time I should be putting into this side project, everything around me was shifting just slightly as a new reality started to set in: in a few months, I was going to be a dad.&lt;/p&gt;

&lt;p&gt;To be clear, this was not unexpected, and was quite thrilling. But I also knew my life was about to get turned upside-down in ways I couldn’t really understand or predict, and I felt my priorities start to shift out from under me. The months since I had left my last job had been all about experimentation and change and figuring out what I really wanted to do next, but with a baby on the way, it started to feel urgent that I “get my shit together”, so to speak. What exactly that meant was, and continues to be, vague and ill-defined, but ultimately, I wanted to feel settled and confident — at least for a time — in my career, in order to give myself the mental space to really focus on and appreciate this new chapter in my family life.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;So I decided the lofty, long-term dream of making a living off of indie work would definitely have to wait a few years. I started dedicating more time to freelancing work as I tried to figure out if running my own little iOS consulting business was something I could settle into longer term, but found the more I leaned into it, the less happy I felt. I found that at its worst, freelancing meant doing work I didn’t really have ownership over, while missing out on all of the important social, educational, mental and financial benefits a great job can provide.&lt;/p&gt;

&lt;p&gt;I needed a new plan. And although it probably wasn’t wise to be putting all this pressure on myself…I needed it pretty quick. The baby wasn’t going to wait for me. Suddenly, after months of me strolling along, casually wondering “Hmm, I wonder if I could turn these freelance gigs into a proper business!” or “Hey, I’m a pretty good writer, let’s spend a bunch of time on that!” or “Huh, maybe this indie thing could turn into something some day!”, or even “Wait, am I a designer?”, I had to turn all those thoughts into My Next Big Career Move.&lt;/p&gt;

&lt;p&gt;And when you’re a bit neurotic and regrettably career-obsessed like I am, this career move pretty quickly starts to feel like a career- &lt;em&gt;defining&lt;/em&gt; move, which immediately turns into a &lt;em&gt;personal identity&lt;/em&gt;-defining move. And just like that, I found myself pretty frantically trying to figure out who I am and what on earth it is I even like to do.&lt;/p&gt;

&lt;h2 id=&quot;job-hunting&quot;&gt;Job hunting&lt;/h2&gt;
&lt;p&gt;The play here was simple, in theory: find a new job as an iOS developer. I’ve got lots of experience at this point, and I had a good story to tell about the independent work I had done in the time since my last full-time job, so the rational side of my brain was pretty confident I’d be able to get some interviews lined up and find the right job.&lt;/p&gt;

&lt;p&gt;Unfortunately, I found myself feeling very unsure about what “the right job” actually was. My career up until now had consisted of jobs at startups, on fairly small teams, all of which I ended up leading in some capacity. I’m thankful for those jobs, and there’s a lot to be learned by taking full responsibility for a team or an app, but I’ve also never had much of an opportunity to be mentored or learn about my craft from more experienced peers. I’ve been a big fish in small ponds for a while now, and eventually, I realized that I wanted to try moving to a bigger pond — or at least, one with lots of bigger fish.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/identity-3.png&quot; alt=&quot;Big fish in small pond&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s the thing about “big pond” jobs  – they’re typically pretty tough to get. Successful companies full of talented people attract talented, successful people, which, as a job applicant at one of these companies, usually means two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You’re competing against a whole bunch of other very qualified folks.&lt;/li&gt;
  &lt;li&gt;The application process is…not trivial.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Long story short: I applied for a few of these jobs, and found the process to be intimidating, frustrating, and most importantly, really not aligned with my identity as a software developer.&lt;/p&gt;

&lt;h2 id=&quot;who-am-i&quot;&gt;Who am I?&lt;/h2&gt;

&lt;p&gt;Some backstory: I was never one of those kids who, you know, learned how to program when they were eight, and spent a bunch of their free time taking computers apart or building elaborate mods for their favourite games or whatever. I thought computers were interesting, and I eventually took a computer science course in high school, but I didn’t think too much of it, and only continued the following year because the music course I wanted to take ended up getting cancelled. I got the hang of it, and did well when I ultimately studied it in university, but it was more or less just school to me. I wasn’t a “great hacker,” like so many of my classmates seemed to be; I was just good at writing exams.&lt;/p&gt;

&lt;p&gt;This is all to say that while I’d like to think I’m pretty competent at writing code at this point, I’ve never really been passionate about it for its own sake, and I’ve always felt like a bit of an outsider to the “computer science”-ness of it all. What’s kept me going in this field is the idea that code is a tool that can be used to create things that are beautiful and joyful and meaningful and empowering – not unlike a song or a book or a piece of art.&lt;/p&gt;

&lt;p&gt;So while I am definitely a “software developer” on my LinkedIn page, I don’t think that really captures how I think of my work. I’m a designer and a writer and a teacher and, heck, even a marketer on some days, and one of my most strongly-held beliefs about our industry is that all this stuff is way more important than we give it credit for; the best software comes from teams of developers with diverse non-technical skillsets and equally diverse backgrounds.&lt;/p&gt;

&lt;p&gt;Long story short: in applying for jobs, I don’t love being reduced to a timed LeetCode solution or an architectural decision in a contrived take-home challenge.&lt;/p&gt;

&lt;p&gt;I won’t belabour the qualms with interviews – I’ve been on the hiring end of many, and recognize the challenge of crafting a good technical interview – but some of these things really started to throw me for a loop. I thought I wanted these jobs, and yet, I was being tested on things that, frankly, I didn’t care much about, and that, in my mind, had very little to do with what actually made me a good candidate. One company, two interviews in, suggested a whole list of study materials (“Cracking the Coding Interview”, etc.) to prep for interview three…and I just cancelled it. None of it felt like me.&lt;/p&gt;

&lt;p&gt;With the stress of these technical interviews weighing on me, I seriously started to question whether this was the kind of work I wanted to pursue. The excitement and positive feedback around my indie work had everything to do with my creativity and product thinking, my design skills, and my ability to communicate effectively. Meanwhile, most of these interviews weren’t really interested in any of that; these were purely technical evaluations that stripped away everything that makes me uniquely effective as a product creator, and pitted me against a slew of other folks who I can only assume care a whole lot more about big O notation than I do.&lt;/p&gt;

&lt;p&gt;Of course, this is all an emotional oversimplification; each interview process was different, and it’s quite possible — probable, even! — that in each of these cases, I was simply not the best candidate for the job. But in general, the misalignment between how I see myself professionally and how I was often being evaluated felt significant, and it really made me wonder whether software development is the field I’m actually “supposed” to be in. Would I be happier looking for design jobs? Am I better suited to some sort of “product owner”-type role, perhaps? Should I be seeking out professional opportunities to write or to teach? ShOuLd I jUsT bE aN iNdEpEnDeNt ApP dEvElOpEr??&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/identity-4.png&quot; alt=&quot;Me, as an artist, showing off my painting&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;Can someone just pay me to do bad drawings please?&lt;/p&gt;

&lt;p&gt;Anyway, it was a stressful summer. And to bring this back around: all my indie work certainly suffered for it. Freelancing, job hunting, and impending parenthood — not to mention this little pandemic we’re having — left me intellectually and emotionally drained.&lt;/p&gt;

&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;At the end of August, I finally received an offer from a company whose interview process felt quite a bit more aligned with my values and strengths than some of the others, and I accepted it. Amidst all my hand-wringing about struggling to find the “right” job, I was extremely lucky and relieved to have found this opportunity as quickly as I did. It’s very much a “big pond” job that I think will challenge me in a bunch of new ways and as more than just a code monkey, and I can’t wait to see where it goes. Ultimately, my professional identity is still “software developer,” and I’m feeling good about that. Landing an exciting job — effectively being told “hey, we value what you do and we want you here” — has a funny way of resolving career uncertainties and insecurities, for a while.&lt;/p&gt;

&lt;p&gt;A couple weeks later, my daughter was born. Suddenly, the whole parenthood thing was real, and we had to figure it out on the fly. Everything about this was (and continues to be) strange and stressful and surreal and wonderful, and I’m more grateful than ever for so many of the people in my life — especially my little family.&lt;/p&gt;

&lt;p&gt;A couple weeks after that, I started the new job. This was also right around the time the baby started to get suuuuper colicky (an imprecise term that just means “screams always, inconsolably, for no reason”). Suffice to say, the past couple months have been a hilariously overwhelming blur, but we’re starting to come out the other side, I think. Thankfully, my wife (like so many moms) is, quite literally, a superhero.&lt;/p&gt;

&lt;p&gt;This year has been an absolutely wild ride. Everything has happened, and everything has changed. Inevitably, there’s lots I wanted to do this year that I haven’t — namely, the indie stuff that has ground to a halt since the summer — but I think the main reason I wrote this rambling piece is to remind myself of two things: 1, to be kind to myself, and proud of the stuff I &lt;em&gt;have&lt;/em&gt; managed to do in this crazy year; and 2, to recognize and appreciate how lucky I am to have been in a position to do any of this stuff at all — in 2020, of all years.&lt;/p&gt;

&lt;p&gt;I’m still really optimistic that once I settle into my new normal — hopefully in the next month or two — I’ll be able to ramp up the independent work again. I’ve got so much more I want to do with Oh Bother and beyond, and I’m feeling bullish on having barely scratched the surface of what I can accomplish on the indie iOS scene. I’m also as eager as ever to keep getting more involved with this community and build relationships with the ridiculous number of you that inspire me every single day. Let’s keep building cool stuff together.&lt;/p&gt;

&lt;p&gt;I’ll be back soon – stay tuned! 📻&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Nov 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 10: Hang on tight</title>
        <link>/hang-on-tight/</link>
        <guid isPermaLink="true">/hang-on-tight/</guid>
        <description>&lt;p&gt;It’s been about a month since the initial release of &lt;a href=&quot;https://apps.apple.com/us/app/oh-bother/id1511197431&quot;&gt;Oh Bother&lt;/a&gt;, and after a surprisingly action-packed rollercoaster of post-launch buzz, the dust has finally settled. &lt;a href=&quot;https://danielgauthier.me/launch-an-app&quot;&gt;Last time round&lt;/a&gt;, I wrote about the development and launch of the app, and touched a little bit on how, by my modest standards, the whole thing felt like a success. Today, I thought I’d tell the more detailed story of how the app, through many strange and unexpected twists and turns, came to receive the attention it did. My goal with this article really isn’t to say “look at all these great things that happened!”, but rather, to shed some light on &lt;em&gt;how&lt;/em&gt; they happened (hint: I pretty much accidentally stumbled into all of it), and what can be learned from how it all went down.&lt;/p&gt;

&lt;h2 id=&quot;chapter-1-in-which-a-small-audience-is-built&quot;&gt;Chapter 1: In Which a Small Audience is Built&lt;/h2&gt;
&lt;p&gt;Truthfully, this story begins back in January, when I decided to get on Twitter, start writing a blog, and figure out if I could get anyone to listen to me. I’ve talked about this process lots before and won’t dwell on it now, but suffice to say: without this first step, the rest of this story would not have happened, at all, even a little bit. I’ll come back to this point later. On to the next chapter!&lt;/p&gt;

&lt;h2 id=&quot;chapter-2-in-which-some-promo-tweets-are-crafted&quot;&gt;Chapter 2: In Which Some Promo Tweets are Crafted&lt;/h2&gt;
&lt;p&gt;The day before the launch, &lt;a href=&quot;https://twitter.com/danielmgauthier/status/1260619499713290240&quot;&gt;I tweeted about it&lt;/a&gt; and attached some promo art:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-10-1.png&quot; alt=&quot;pre-launch artwork&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That tweet took off in a way I hadn’t really seen since making a &lt;a href=&quot;https://twitter.com/danielmgauthier/status/1237484203668688897&quot;&gt;bad joke about VIPER&lt;/a&gt;. Aside from a healthy dose of dumb luck, I think there are a few key reasons this was able to capture some attention.&lt;/p&gt;

&lt;p&gt;First, and perhaps most importantly: the promo art looked good! That’s kind of a silly thing to say — of course you want your promo art to look good — but I put some effort into making sure it felt polished and intentional, and I think it paid off. I &lt;a href=&quot;https://www.ls.graphics/simplemockups&quot;&gt;bought some device templates&lt;/a&gt; that matched the clean, round aesthetic of the app, and carefully chose a small set of screenshots that highlighted both the app’s functionality and its unique design.&lt;/p&gt;

&lt;p&gt;On top of that, I had come up with what I think is a reasonably snappy one-liner to describe Oh Bother — “a little app I built to help you and your housemates avoid poorly-timed interruptions when you’re working from home together” — and made that the focal point of the tweet. It’s a bit wordy, but let’s be honest: being wordy is kind of becoming my thing. Ultimately, I think it works because it describes a single, very specific (and hopefully relatable) problem, and because it emphasizes the idea that this is a small, handcrafted app made by one person.&lt;/p&gt;

&lt;p&gt;I can try to justify the response to the tweet all I want, but at the end of the day, the main reason it took off is thanks to a &lt;a href=&quot;https://twitter.com/JordanMorgan10/status/1260648210994876416&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;https://twitter.com/_chuckyc/status/1261006069955997698&quot;&gt;key&lt;/a&gt; &lt;a href=&quot;https://twitter.com/JPEGuin/status/1260909814714118144&quot;&gt;retweets&lt;/a&gt; from some folks with much larger audiences than me. I’m super thankful and lucky that those people were jazzed about my work, and it’s made me eager to pay it forward when I see other indies trying to get their own inspiring little projects off the ground.&lt;/p&gt;

&lt;p&gt;On launch day, I &lt;a href=&quot;https://twitter.com/danielmgauthier/status/1260935068106788864&quot;&gt;tweeted about the app again&lt;/a&gt;, as one does. I had big plans to put together some sort of video for the big go-live announcement, and spent a bunch of time messing around in Rotato and iMovie, but I couldn’t for the life of me come up with anything that didn’t look like it had been made in Windows Movie Maker in 2002. Making marketing videos is, apparently, not my forté. So I moved to plan B, and attached my App Store screenshot art to the tweet instead.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-10-2.png&quot; alt=&quot;App Store screenshots&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This tweet caught on fairly well for roughly the same reasons the first one did, though interestingly, the first tweet was still doing the rounds at this point, and I wonder if that didn’t actually take away from this one’s ability to attract the same amount of attention. Also, hilariously, I forgot to include a link to the App Store in this tweet, and of course the pre-release tweet didn’t have one either, so I had two somewhat viral tweets going around with no direct way for people to get to the thing I was trying to market.&lt;/p&gt;

&lt;p&gt;All in all, these were a really exciting and expectation-shattering couple of days, but I do wonder if just a single tweet on launch day, with the nice promo art and a direct link to the app, may have driven more download traffic on day one. I guess we’ll never know ¯\_(ツ)_/¯&lt;/p&gt;

&lt;h2 id=&quot;chapter-3-in-which-a-9to5mac-article-appears&quot;&gt;Chapter 3: In Which a 9to5Mac Article Appears&lt;/h2&gt;
&lt;p&gt;I didn’t bother (🥁) reaching out to any press leading up to launch. Though I’m sure there are plenty of counter-examples out there, cold emails have never felt to me like a great marketing tool if you don’t already have a reputation or some buzz around your product, and ultimately, I decided that for this small and somewhat experimental project, I would just focus on using Twitter for any marketing efforts.&lt;/p&gt;

&lt;p&gt;Having said that, I did decide to throw a little press kit onto the &lt;a href=&quot;https://ohbother.app&quot;&gt;app’s website&lt;/a&gt; — just in case! — and I’m glad I did. I included a high-res app icon, my App Store screenshots, a couple extra pieces of promo art, and some short, friendly blurbs about myself and the story behind the app.&lt;/p&gt;

&lt;p&gt;Some way or another, a 9to5Mac writer came across that first tweet on the day before the launch, and left a comment expressing interest. I quickly got in touch, and was able to make his life easy by sending him a couple promo codes and pointing him to the press kit. Sure enough, less than 24 hours later, &lt;a href=&quot;https://9to5mac.com/2020/05/14/oh-bother-keeping-peace-working-at-home/&quot;&gt;a great little article&lt;/a&gt; went up on 9to5Mac to coincide with the release.&lt;/p&gt;

&lt;p&gt;Making an appearance on 9to5Mac, on launch day, was not &lt;em&gt;at all&lt;/em&gt; on my radar as even the stretchiest of stretch goals for this app, and I’m super lucky and thrilled that things played out the way they did.&lt;/p&gt;

&lt;h2 id=&quot;chapter-4-in-which-the-app-somehow-ends-up-on-product-hunt&quot;&gt;Chapter 4: In Which the App Somehow Ends Up on Product Hunt&lt;/h2&gt;
&lt;p&gt;At some point on launch day, amid a veritable deluge of tweets, someone mentioned to me that they had posted Oh Bother on Product Hunt. I had no plans to post on Product Hunt myself — again, I was really just focused on making a bit of noise on Twitter — so I said something like “Oh cool, thanks!”, and didn’t think much of it. I figured it would fizzle out pretty quickly.&lt;/p&gt;

&lt;p&gt;Late the following day, someone tweeted at me with a &lt;a href=&quot;https://www.producthunt.com/posts/oh-bother&quot;&gt;link to the Product Hunt post&lt;/a&gt; saying “This is you!” Sure enough, Oh Bother had a few hundred upvotes and was near the top of the chart. Next thing I knew, &lt;a href=&quot;https://twitter.com/rrhoover/status/1261429331508621312&quot;&gt;Ryan Hoover was tweeting about my app&lt;/a&gt;, and I was scrambling to stake my claim as the product’s “maker” in order to add some more detail to the post.&lt;/p&gt;

&lt;p&gt;At that point, it was largely too late for me to really move the needle on the post, but nonetheless, it was pretty neat to see people discovering and talking about the app without my involvement. In retrospect, I can’t help but think a polished, properly-timed, well-written post on Product Hunt might have generated a lot more interest on launch day.&lt;/p&gt;

&lt;p&gt;Oops.&lt;/p&gt;

&lt;h2 id=&quot;chapter-5-in-which-the-app-sort-of-ends-up-on-reddit-eventually&quot;&gt;Chapter 5: In Which the App sort of Ends Up on Reddit Eventually&lt;/h2&gt;
&lt;p&gt;Launch day was a Thursday. By Saturday, I had figured out that this little app idea had more legs than I thought, and around noon I realized “Oh, don’t people post their apps on &lt;a href=&quot;https://www.reddit.com/r/apple/&quot;&gt;r/Apple&lt;/a&gt; on Saturday? Maybe I should actually think about doing that.”&lt;/p&gt;

&lt;p&gt;I’ve spent time on Reddit here and there over the years, but I’ve never had an account or really gotten involved in any Reddit community. I created an account, headed to the Apple subreddit, read the rules carefully (I’ve heard Reddit mods can be &lt;em&gt;even meaner&lt;/em&gt; than Stack Overflow mods), and &lt;a href=&quot;https://www.reddit.com/r/apple/comments/gkxrvq/i_just_released_oh_bother_an_app_about_working/&quot;&gt;posted something about Oh Bother&lt;/a&gt; as part of r/Apple’s “Promo Saturday”.&lt;/p&gt;

&lt;p&gt;It was already pretty late in the day to be posting; to make matters worse, this was my first ever post on Reddit, so it had to be approved by mods. That approval took several hours, and my post didn’t actually become visible until late Saturday afternoon.&lt;/p&gt;

&lt;p&gt;Despite my inability to do any part of this correctly, the post still ended up with a decent number of upvotes and spent some time on the front page of r/Apple. Perhaps you’re starting to sense a theme here: what if I had actually planned &lt;em&gt;this&lt;/em&gt; part out properly?&lt;/p&gt;

&lt;h2 id=&quot;chapter-6-in-which-a-very-exciting-email-arrives&quot;&gt;Chapter 6: In Which a Very Exciting Email Arrives&lt;/h2&gt;
&lt;p&gt;The following Monday, I received an email from App Store Connect with the subject line “Your app, Oh Bother, may be featured on the App Store”.&lt;/p&gt;

&lt;p&gt;This was hugely exciting, and equally unexpected. Getting featured on the App Store is something I had always dreamed about when I started building iOS apps, and although features in 2020 may not carry the same weight that they did in 2012, this still felt like an important milestone.&lt;/p&gt;

&lt;p&gt;When your app is being considered for a feature in the App Store, the whole process from the developer’s point of view is automated; this email wasn’t a real person from Apple patting me on the back, but rather, a templated set of instructions on what I had to do to be considered, and a deadline. There are different types of features that require different actions, but mine was a pretty standard case of providing “Artwork for the Apps Tab”. The artwork had to be provided in a photoshop file, based on a provided psd template, and follow a whole bunch of other rules I didn’t understand like “Provide all elements as high-resolution scalable vector Smart Objects on individual layers” or “Evergreen art required”.&lt;/p&gt;

&lt;p&gt;I use Sketch for all my design work, and do not own or really have any expertise at all in Adobe products, so the next 24 hours involved me downloading a free trial of Photoshop and giving myself a crash course on how to properly move my vector assets over (while also trying to design some really simple artwork that didn’t look half-bad).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-10-3.png&quot; alt=&quot;App Store feature art&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;It’s like the icon, but…with little thingies.&lt;/p&gt;

&lt;p&gt;The end result was…fine, I think? I got an automated message the day after submitting that said the artwork was approved. It still hasn’t been used in the App Store yet (which, anecdotally, seems pretty par for the course — you never really know if and when you’ll actually get that bigger banner feature). But, the app &lt;em&gt;did&lt;/em&gt; get some smaller “Apps we love right now”-type features in the US and Canadian stores in the couple weeks following the release, which were an absolute thrill to stumble across:&lt;/p&gt;

&lt;p class=&quot;large-screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-10-4.jpeg&quot; alt=&quot;App Store feature screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;chapter-7-in-which-a-very-strange-email-arrives&quot;&gt;Chapter 7: In Which a Very Strange Email Arrives&lt;/h2&gt;
&lt;p&gt;On May 21st — a week after launch — my dad woke up to a very strange email that began like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-10-5.png&quot; alt=&quot;email screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sorry, I just want to make sure you caught that last thing I said: this email was sent to &lt;em&gt;my dad&lt;/em&gt;. It addressed him by his first name, and was sent to his personal Hotmail address.&lt;/p&gt;

&lt;p&gt;???????????&lt;/p&gt;

&lt;p&gt;To be clear, I’m 30 years old — this wasn’t some situation that was easily explained away by, I don’t know, my dad having set up my App Store account or something. My dad is many things to me, but I assure you he has &lt;em&gt;nothing&lt;/em&gt; to do with the app or any part of my software development business.&lt;/p&gt;

&lt;p&gt;So he forwarded the email to me, saying something along the lines of “uhhhh almost deleted this, not sure what it is, but you can sort it out!” Naturally, I was extremely skeptical. Why on earth did my &lt;em&gt;dad&lt;/em&gt; get this email? What kind of an elaborate scam was this?&lt;/p&gt;

&lt;p&gt;But the email was sent from an apple.com email address, and I matched it to what looked like a real person on LinkedIn. They weren’t asking for money, and I couldn’t really figure out how anyone could benefit from receiving a short video of my free app. Initially I responded by asking how they ended up with my dad’s contact info, and all I got was “The address was found in our internal system; maybe it was entered when the app was set up?” (It wasn’t.)&lt;/p&gt;

&lt;p&gt;All of this defied logic entirely, and it remains a total mystery to this day. But I already had a screen-captured video pretty much ready to go from my botched earlier attempts at filmmaking, so I shrugged, fired it off, and waited to see what would happen.&lt;/p&gt;

&lt;p&gt;Two days later, in comes another email: “The feature is now live! Please share with your friends and followers!”&lt;/p&gt;

&lt;p&gt;And, sure enough, &lt;a href=&quot;https://twitter.com/AppStore/status/1266061578702589957&quot;&gt;there it was&lt;/a&gt;:&lt;/p&gt;

&lt;p class=&quot;large-screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-10-6.png&quot; alt=&quot;App Store promo tweet&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;I’m not sure when this became a nudity thing, but okay Apple.&lt;/p&gt;

&lt;p&gt;This might be the weirdest thing that’s ever happened to me. And the difference between the precise, impersonal, automated App Store artwork request and “a guy from the social media team emailing my dad” gave me whiplash. But at the end of the day, my app was shared, by Apple, to 4.5 million people. Totally wild.&lt;/p&gt;

&lt;h2 id=&quot;chapter-8-in-which-a-blog-post-goes-viral&quot;&gt;Chapter 8: In Which a Blog Post goes Viral&lt;/h2&gt;
&lt;p&gt;Meanwhile, on May 23rd, I put out &lt;a href=&quot;https://danielgauthier.me/launch-an-app&quot;&gt;the aforementioned blog post&lt;/a&gt; about the process of building and launching Oh Bother. It was fun to write and seemed to strike a chord with a few folks, which is always what you hope to see. Usually, my blog posts don’t make too much noise beyond a couple days of slightly increased traffic on the website, and this one looked no different at first. But Dave Verwer had other ideas!&lt;/p&gt;

&lt;p&gt;That Friday, &lt;a href=&quot;https://iosdevweekly.com/issues/458&quot;&gt;iOS Dev Weekly&lt;/a&gt; went out, and my article made the cut. Awesome! I had a pretty good idea of what to expect this time round, after my &lt;a href=&quot;https://danielgauthier.me/imposter-syndrome/&quot;&gt;imposter syndrome article&lt;/a&gt; got the iDW treatment a few months back.&lt;/p&gt;

&lt;p&gt;But then at some point, I actually checked in on my traffic and quickly realized that something else was going on. Sure enough, totally unbeknownst to me, my weird little blog about indie app development — that’s right, the one you’re reading now, with the long-winded articles and the bad drawings — &lt;a href=&quot;https://news.ycombinator.com/item?id=23352001&quot;&gt;was on the front page of Hacker News&lt;/a&gt;.&lt;/p&gt;

&lt;p class=&quot;large-screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-10-7.png&quot; alt=&quot;Graph of hacker news spike&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I braced myself. I’ve heard Hacker News commenters can be &lt;em&gt;even meaner&lt;/em&gt; than Reddit mods. Thankfully, the mob spared me: I got a truly nutty amount of additional traffic, but almost everyone seemed to enjoy my writing and was generally pretty nice about it. My post even ended up making it into the Hacker News weekly newsletter that weekend. Once again, I wonder if there are things I could have done to be more prepared for something like this — I had to scramble to make a Hacker News account, tried to answer some comments, quickly got rate-limited for commenting too much, etc. — but at the end of the day, this was yet another unexpected channel by which people were finding and getting excited about the app, and I’m just grateful to whoever it was that posted my article.&lt;/p&gt;

&lt;h2 id=&quot;epilogue-lessons-learned&quot;&gt;Epilogue: Lessons Learned&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-10-8.png&quot; alt=&quot;My flying away on a balloon&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;I may not have thought this through.&lt;/p&gt;

&lt;p&gt;It’s been an exciting, wacky ride, but it’s ultimately not worth a whole lot if there aren’t some takeaways I can try to apply next time round. So, here are some quick, bigger picture reflections on what I think worked for me here, and what I learned along the way.&lt;/p&gt;

&lt;p&gt;First, let’s get back to chapter 1 for a moment. I realize there are certainly counter-examples out there, and I’m not suggesting this is the only way to launch a new thing and have people notice, but: I truly don’t think Oh Bother would have gotten off the ground at all if I hadn’t spent the previous few months engaging with the iOS community and building an audience on Twitter. The audience didn’t have to be huge — I think I had something like 400 followers when Oh Bother launched — but the fact that some like-minded folks cared to some extent about who I was and what I was building made all the difference here.&lt;/p&gt;

&lt;p&gt;Something else that I think has worked well for me through all this is my insistence on making everything I create feel fun, personable and human. I’m not really interested in Oh Bother as some sort of standalone brand; rather, I want Oh Bother to be an app created by me, Dan Gauthier, and I want my personality to come through in the app in the same way I try to have it come through in my writing. There are definitely pros and cons here, but at this early stage in my “indie adventure,” I think I’ve benefited from being friendly, making people smile, and having people root for me when I put out new stuff.&lt;/p&gt;

&lt;p&gt;On a related note: I’ve now seen firsthand how being the creator of multiple thoughtfully-made things — in my case, a blog and an app — can have a multiplying effect on the success of both. This blog is a pet project, with no lofty ambitions beyond continuing to be a place for me to write fun stuff. But it’s been really valuable in building that initial audience, and is now also responsible for driving a ton of traffic to the app. Likewise, the discovery of Oh Bother has led plenty of new people to my website, and some of those people have since really enjoyed a bunch of older articles. It’s a virtuous cycle — a very small one at the moment, but hopefully one that I can keep growing over time.&lt;/p&gt;

&lt;p&gt;Now, despite all the good stuff I’ve been prattling on about, here’s something that might surprise some of you: Oh Bother hasn’t been downloaded &lt;em&gt;that&lt;/em&gt; many times.&lt;/p&gt;

&lt;p&gt;To be clear, my numbers have far exceeded anything I could have possibly expected when I pushed this little app out the door — I’ve had around 8000 downloads to date, and I was probably optimistically hoping for something like 500. But I think if a couple years ago, I had read a blog post like this about an app that had been covered in 9to5Mac on launch day, that had been tweeted about by the founder of Product Hunt, that had been featured in the App Store in multiple countries including the US, that had a whole promo video shared on social media by the App Store, and that indirectly ended up in iOS Dev Weekly and on the front page of Hacker News, I probably would have just assumed that app was doing some bonkers numbers.&lt;/p&gt;

&lt;p&gt;Although I’ve known this to be true all along, and have written as much in the past, this experience has further brought into focus just how tricky it can be to find any sustainable success as an indie on the App Store. This was never meant to be a long-term or financially successful project, so it’s all just been a silly and largely enjoyable ride as far as I’m concerned, but it does make me wonder: were there any young devs out there who saw that App Store tweet, for example, and thought “Gosh, if I could somehow just get my app tweeted about by the App Store like this guy…that’s when I’ll know I really made it!”&lt;/p&gt;

&lt;p&gt;Because, folks…suffice to say, I have not “made it.”&lt;/p&gt;

&lt;p&gt;(Actually, for what it’s worth, given the number of people the App Store’s Twitter account is ostensibly reaching, that tweet did &lt;em&gt;remarkably&lt;/em&gt; little for my downloads.)&lt;/p&gt;

&lt;p&gt;And although downloads were never the goal of this particular project, this &lt;em&gt;has&lt;/em&gt; nonetheless solidified an important idea for me that I’m not sure I fully appreciated before: splashy publicity does not guarantee App Store success. Like, not even close. Some of you App Store old-timers out there are rolling your eyes and thinking “yeah, duh,” but I think it bears repeating for folks who are trying to break in. As a community, we do a lot to celebrate wins like App Store features and viral tweets — as we should! — but while those wins can help, it seems to me they’re rarely a defining reason for real, sustained product success.&lt;/p&gt;

&lt;p&gt;Next time round, I’ll have more of a plan in place to market the app on launch and beyond; I think I missed out on a bunch of easy downloads by ignoring folks outside of my Twitter bubble — press, Reddit, Product Hunt, etc. — who were clearly interested in the app. But I also have bigger questions about Oh Bother, and about any subsequent apps I build, which basically boil down to this: how can you tell whether you’re dealing with a marketing issue or an app issue? In the case of Oh Bother, is this app simply not quite compelling or useful enough to drive bigger numbers? (It &lt;em&gt;is&lt;/em&gt; a very tiny app.) Or is this just a case where I could have done a lot more to really grab people’s attention? Or, is there something here worth chipping away at such that with steady improvements over time, this little app could somehow, eventually, find itself a stronger foothold in the App Store?&lt;/p&gt;

&lt;p class=&quot;emphasis-text&quot;&gt;&lt;strong&gt;Find out next week in “Going Indie, Step 11: Solve the app store and become rich and famous with this one weird trick!”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Hey, you. Yeah, you! No, no— not you, I’m— I’m talk— yeah, you right there. Hi! Come hang out with me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; and let me know what you think about the novel you just read. 🦆&lt;/p&gt;
</description>
        <pubDate>Fri, 19 Jun 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 9: Launch an app</title>
        <link>/launch-an-app/</link>
        <guid isPermaLink="true">/launch-an-app/</guid>
        <description>&lt;p&gt;On May 14th, I launched &lt;a href=&quot;https://ohbother.app&quot;&gt;Oh Bother&lt;/a&gt; — my first independent app in 7 years. I wrote a bit about my decision to build Oh Bother &lt;a href=&quot;https://danielgauthier.me/left-turn&quot;&gt;here&lt;/a&gt;, but now that my little app is actually out in the world, trying to make a name for itself, I thought it’d be fun to reflect on the whole process and on what comes next. Here we go!&lt;/p&gt;

&lt;h2 id=&quot;the-timeline&quot;&gt;The timeline&lt;/h2&gt;
&lt;h3 id=&quot;april-2&quot;&gt;April 2&lt;/h3&gt;
&lt;p&gt;Initial commit! Had the idea, had the name, and had big ambitions to ship this thing in, like, two weeks tops.&lt;/p&gt;

&lt;h3 id=&quot;april-4&quot;&gt;April 4&lt;/h3&gt;
&lt;p&gt;Got the basic structure and aesthetic of the app sort of fleshed out with some fake data.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-1.png&quot; alt=&quot;screenshot 1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;april-7&quot;&gt;April 7&lt;/h3&gt;
&lt;p&gt;Nailed down the structure of the status update drawer, and the way that it opens and closes. None of the buttons actually did anything yet.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-2.png&quot; alt=&quot;screenshot 2&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;april-10&quot;&gt;April 10&lt;/h3&gt;
&lt;p&gt;Very quickly threw together an icon in Sketch (I am &lt;em&gt;not&lt;/em&gt; an icon designer). Shared it on Twitter, and hilariously claimed I was hoping to launch in a week or two. The UI was basically done! There wasn’t much left to do really!&lt;/p&gt;

&lt;p&gt;Anyway, that icon ended up being &lt;em&gt;the&lt;/em&gt; icon. I kinda like it ¯\_(ツ)_/¯&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-8-icon.png&quot; alt=&quot;app icon&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;april-11&quot;&gt;April 11&lt;/h3&gt;
&lt;p&gt;Finished building all the fun little interactions in the status update drawer — the wobbly buttons, the modal inputs, the status change animations. Was definitely &lt;a href=&quot;https://danielgauthier.me/2020/02/11/indie-3.html&quot;&gt;following my own advice&lt;/a&gt; by getting this nice and polished early on 🤓 Pretty close to the final look and feel at this point.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-3.png&quot; alt=&quot;screenshot 3&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;april-16&quot;&gt;April 16&lt;/h3&gt;
&lt;p&gt;Started implementing local data persistence, i.e. structuring and saving user status to disk. Also built those little in-app notification views, and the system to coordinate them.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-4.png&quot; alt=&quot;screenshot 4&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;april-20&quot;&gt;April 20&lt;/h3&gt;
&lt;p&gt;Started building the push notification system, and got timed notifications working properly. Realized it was finally time to bite the bullet and stop avoiding the hard part: CloudKit 💀&lt;/p&gt;

&lt;h3 id=&quot;april-26&quot;&gt;April 26&lt;/h3&gt;
&lt;p&gt;Had iCloud syncing (not sharing) working pretty well, and built the basic onboarding flow as a much-needed palette cleanser.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-5.png&quot; alt=&quot;screenshot 5&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;may-1&quot;&gt;May 1&lt;/h3&gt;
&lt;p&gt;Built the People screen, and finally had iCloud sharing working pretty well. This was the first time the app actually worked, and could be used for its intended purpose. Getting close!&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-6.png&quot; alt=&quot;screenshot 6&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;may-5&quot;&gt;May 5&lt;/h3&gt;
&lt;p&gt;Built the Extras screen, diving deep into the scary world of in-app purchases in the process. The App Store review folks ended up making me change this screen a few times. I’m still not &lt;em&gt;entirely&lt;/em&gt; sure why.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-7.png&quot; alt=&quot;screenshot 7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;may-6&quot;&gt;May 6&lt;/h3&gt;
&lt;p&gt;Built the menu screen, which represented the final chunk of UI the app needed to be complete.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-8.png&quot; alt=&quot;screenshot 8&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;may-7-9&quot;&gt;May 7-9&lt;/h3&gt;
&lt;p&gt;Tons of little tweaks, bug fixes and polish. My commits were way too big and irresponsibly spaced out before this point, and now all of a sudden I was pushing 10 tiny commits every day.&lt;/p&gt;

&lt;h3 id=&quot;may-10&quot;&gt;May 10&lt;/h3&gt;
&lt;p&gt;Finalized all the extra colour palettes and app icons.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-9-screenshot-9.png&quot; alt=&quot;screenshot 9&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;may-11-12&quot;&gt;May 11-12&lt;/h3&gt;
&lt;p&gt;Submitted for review!&lt;/p&gt;

&lt;p&gt;…And promptly rejected.&lt;/p&gt;

&lt;p&gt;I went back and forth with app review a few times, all because of weird in-app purchase issues that, honestly, weren’t really issues. At some point I think they were just rejecting me for the fun of it, because I eventually had to submit a formal appeal and say “uh…your rejection has nothing to do with my app. What’s the deal?”&lt;/p&gt;

&lt;h3 id=&quot;may-13&quot;&gt;May 13&lt;/h3&gt;
&lt;p&gt;Thankfully, the appeal was quick, my app was approved, and I scrambled to finish up the website and some simple marketing materials.&lt;/p&gt;

&lt;h3 id=&quot;may-14&quot;&gt;May 14&lt;/h3&gt;
&lt;p&gt;Launch day 🚀&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-9.png&quot; alt=&quot;me holding the Oh Bother balloon&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;was-it-a-successful-launch&quot;&gt;Was it a successful launch?&lt;/h2&gt;
&lt;p&gt;Short answer: resoundingly, yes.&lt;/p&gt;

&lt;p&gt;Longer answer: Success is subjective! Oh Bother has not made me rich or famous, nor was it intended to. (My &lt;strong&gt;next&lt;/strong&gt; app will do that, I assume). I had a bunch of smaller things I was hoping to do with this app, and luckily, those things seem to have largely panned out.&lt;/p&gt;

&lt;p&gt;First off, my primary goal was to get something created entirely by me into the App Store quickly. I was tired of thinking of myself as an “independent iOS developer” without actually having a single app in the App Store, and if nothing else, I knew it would be helpful for networking and for potential future job hunts to have at least one thing in the world to point to and say “that’s mine.” So, mission accomplished!&lt;/p&gt;

&lt;p&gt;But more to the point, I wanted the app to be something I was proud of, and that, however small, reflected what I’m capable of as a developer, as a designer, and as someone who can see a project through from start to finish. I wanted to prove to myself that I could make something that people would be interested in, and maybe even inspired by. And, ultimately, I think I did! I’m proud of my quirky little app, and the design and attention to detail that went into it seems to have really resonated with some folks.&lt;/p&gt;

&lt;p&gt;I’ve made a &lt;a href=&quot;https://danielgauthier.me/2020/01/26/indie-intro.html&quot;&gt;well-documented&lt;/a&gt; attempt this year to get involved in the online iOS community in the hopes of growing my network and learning from a pretty incredible group of people. To that end, I was also hopeful that building and showcasing an app I was proud of would open up opportunities to connect with more of the people I’m inspired by in this community. In this sense, Oh Bother was definitely a success; to put it crassly, sharing the progress and launch of the app more than doubled my follower count on Twitter, which is wild. But more importantly, I feel like I’ve started to form some real (albeit heavily internet-mediated!) relationships with a whole bunch of remarkably smart, generous and supportive people, and I’ve been absolutely floored by this community’s eagerness to lift each other up. Thanks y’all.&lt;/p&gt;

&lt;p&gt;FInally, my stretch goal in all this was to build something that garnered enough of a positive response that I’d feel justified in continuing to put time into it after release. This project was all about cutting scope and getting something out the door quickly, but I’ve always loved the idea of actually having a project to maintain, and make decisions about, and add fun new features to over time. At some point during development, I off-handedly mentioned to my wife that I’d be thrilled if I could just make 100 bucks from this app, because that would tell me people were seeing &lt;em&gt;some&lt;/em&gt; amount of value in the idea, and that it might be worth pursuing a while longer.&lt;/p&gt;

&lt;p&gt;Well, dear readers, thanks in large part to your generosity, I’ve made 100 bucks! I’ve also received a ton of thoughtful feedback, and have received over 100 (&lt;em&gt;mostly&lt;/em&gt; very positive) reviews on the App Store worldwide. Again, nowhere near rich and famous numbers (or even significant side-hustle numbers), but enough to give me the conviction to keep exploring this idea for a little while longer, and see if I can make anything more of it. There seem to be some folks out there who are actually using and enjoying this app (and who also aren’t related to me), and that’s truly an incredible thrill.&lt;/p&gt;

&lt;p&gt;Oh, also, the app actually mostly worked and didn’t explode on launch day. So by that metric alone: Successful Launch 😎&lt;/p&gt;

&lt;h2 id=&quot;what-comes-next&quot;&gt;What comes next?&lt;/h2&gt;
&lt;p&gt;There are so many great, obvious ideas that I think could make this product more useful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;iPad, Watch and Mac apps&lt;/li&gt;
  &lt;li&gt;Siri shortcuts&lt;/li&gt;
  &lt;li&gt;Custom contexts&lt;/li&gt;
  &lt;li&gt;Notification center widget&lt;/li&gt;
  &lt;li&gt;Calendar integration&lt;/li&gt;
  &lt;li&gt;etc, etc, etc…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… not to mention some really important additions like localizations and accessibility improvements. In other words, I’ve got my work cut out for me.&lt;/p&gt;

&lt;p&gt;The question now is whether continued support and some added features over the next few weeks can move the needle on my download numbers. Because, unsurprisingly, after the excitement of launch, those numbers bottomed out pretty quickly. So far, I’m still encouraged by the slow trickle of downloads, and especially by the consistently positive reviews the app continues to get — that tells me the app might be sticking for at least a few users. But I’ll be honest — I’m still not &lt;em&gt;personally&lt;/em&gt; convinced this is an idea that has real staying power. I do, of course, believe the app can be useful — we’re still using it at home! — but it’s also a little bit goofy. That silly simplicity is by design, and is definitely part of the app’s charm, but I don’t yet know if there’s really enough value here to keep people engaged long-term.&lt;/p&gt;

&lt;p&gt;So, I’m going to keep excitedly chipping away at it for the next little while, with the lofty ambition of getting those numbers to start ticking upwards again. I’ll also be thinking about how I can not-too-aggressively morph the payment model to make sure that, on the off-chance the app does start to gain more traction, I can continue to financially justify working on it.&lt;/p&gt;

&lt;p&gt;And if things just sort of fizzle out over the next few weeks or months, that’s okay! That’s what’s kind of fun about the aggressively time-boxed approach I took to building Oh Bother — I don’t feel like I’ve invested too much of myself into it, and so I’ve got space to experiment with it now and see where things end up.&lt;/p&gt;

&lt;p&gt;In my &lt;a href=&quot;https://danielgauthier.me/imposter-syndrome&quot;&gt;article about imposter syndrome&lt;/a&gt; earlier this year, I wrote the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;From the moment you decided to get into iOS development, your career has been coloured by this vague idea that true success in this industry is nearly or completely single-handedly building something that grabs people’s attention. At the end of it all, you don’t just want to be “[Your Name Here]”. No, at the pinnacle of this climb, you want to be known as “[Your Name Here], creator of [Your Brilliant and Beloved App]”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Calling Oh Bother “brilliant and beloved” is definitely a stretch. But I’m having a blast with this app, and I’ll happily wear my “Dan Gauthier, creator of Oh Bother” name tag for the foreseeable future 🎈&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;If you haven’t checked it out already, you can find Oh Bother on the App Store &lt;a href=&quot;https://apps.apple.com/us/app/oh-bother/id1511197431&quot;&gt;here&lt;/a&gt;. Come find me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt;; I’d love to hear what you think about the app  🦆&lt;/p&gt;
</description>
        <pubDate>Fri, 22 May 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 8: Take a hard left</title>
        <link>/hard-left/</link>
        <guid isPermaLink="true">/hard-left/</guid>
        <description>&lt;p&gt;Hi! 👋&lt;/p&gt;

&lt;p&gt;It’s been a little while since I last wrote something on the ol’ blog. I’ve been hard at work on a new project over the past few weeks — the subject of this article, as it happens — and much to my everlasting chagrin, there’s only so much time in a day. So, the blog had to be put on hold for a few weeks. The good news is that I’ve now got lots to catch up on here!&lt;/p&gt;

&lt;h2 id=&quot;so-what-have-i-been-up-to&quot;&gt;So, what have I been up to?&lt;/h2&gt;
&lt;p&gt;Well, my indie journey took a bit of a turn right around the start of April, when I got a pretty silly idea stuck in my head. My wife and I both work from home now, thanks to COVID-19, and we’re finding that it’s pretty easy to step on each other’s toes, so to speak. If I’m taking a quick break from work to make coffee, for example, I’ll often accidentally make a bunch of noise, or try to strike up a conversation, forgetting that she might be in the middle of a phone call, or just really focused on a difficult task. Of course, with my brain being broken in the particular way that it is, I thought: what if there was an app for that?!&lt;/p&gt;

&lt;p&gt;Now, before I go any further, I want to make something abundantly clear. The fact that we even have this “problem” to begin with is a reflection of how extremely fortunate we both are to a) not be living alone, b) have jobs that we can do from home, and c) have jobs that are at once relatively secure and utterly irrelevant, in the grand scheme of things, in this moment. This is a dumb problem, and honestly, I don’t really want to frame this app I’m about to describe as anything other than a fun, trivial little side project.&lt;/p&gt;

&lt;p&gt;Nonetheless, a silly problem deserves a silly app to solve it, and so I started thinking about whether this was something I could feasibly build. Of course, as this whole blog has made clear, I’ve already got a significant indie project on the go, and conventional wisdom would suggest that you probably don’t want to make a habit of abandoning ideas-in-progress for whatever shiny new idea pops into your head.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-8.png&quot; alt=&quot;Going down the &amp;quot;shiny new thing!&amp;quot; path&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;Now there’s an idea…&lt;/p&gt;

&lt;p&gt;But, I’ll be honest: freelancing work, writing and real life have been taking up a lot of my time, and I haven’t been making progress on the indie app work as quickly as I had hoped. I still think I’ll get the big app done at some point this year, but the idea of shifting gears for a few weeks in order to get a much smaller finished product out the door felt pretty appealing. I haven’t actually released an app independently since 2013, and I figure I can probably learn a lot from going through that process and seeing if I can drum up a bit of interest online. If nothing else, it gives me something recent I can point to and say “look, I’m capable of building cool things!”&lt;/p&gt;

&lt;p&gt;So, I’m building this app. I’m not sure the idea itself has any real legs, but I figure the cost of building it is low enough that it’ll be worth doing, regardless of any response it does (or doesn’t) get.&lt;/p&gt;

&lt;h2 id=&quot;okaywhat-is-it-exactly&quot;&gt;Okay…what is it, exactly?&lt;/h2&gt;
&lt;p&gt;The app is called Oh Bother. Why? Because it’s all about communicating whether or not you want to be bothered, and because &lt;a href=&quot;https://www.youtube.com/watch?v=Fhl4oKvxphs&quot;&gt;I like Winnie the Pooh&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a rather simple app that helps you and your housemates avoid poorly-timed interruptions when you’re working from home together. You can set your status to either botherable or unbotherable — neither of which are real words. You can put your status on a timer, such that it flips automatically when time is up, and you can get notified when time is up, if you wish. You can also provide context for your status by indicating that you’re on the phone, or taking a nap, or going outside, or what have you.&lt;/p&gt;

&lt;p&gt;Meanwhile, you’ll always have up-to-date info on your housemates’ statuses, synced automatically through iCloud. You can also set up notifications to let you know when a housemate’s status changes, which might come in handy if your housemate is busy, but you’re &lt;em&gt;really&lt;/em&gt; eager to talk to them about the hilarious tweet you just wrote.&lt;/p&gt;

&lt;p&gt;The app will be free, with an option to tip inside the app, and perhaps get a few fun cosmetic options as a thank-you hug. I haven’t quite sorted out that last part yet — that’s one of the things I’ve still got left to do before launch.&lt;/p&gt;

&lt;h2 id=&quot;my-goals-in-building-this-thing&quot;&gt;My goals in building this thing&lt;/h2&gt;
&lt;p&gt;My initial commit was on April 2nd, so I’ve been working on this on and off for close to a month. I can see the finish line now, but the process has been a pretty tricky tug-of-war between three goals that are quite at odds with each other: shipping quickly, shipping something that feels complete and polished, and learning some stuff along the way.&lt;/p&gt;

&lt;h3 id=&quot;ship-quickly&quot;&gt;Ship quickly&lt;/h3&gt;
&lt;p&gt;On April 10th, &lt;a href=&quot;https://twitter.com/danielmgauthier/status/1248697974718226438?s=20&quot;&gt;I tweeted about this app for the first time&lt;/a&gt; , and said I was hoping to launch it “in the next week or two.” It’s now April 28th, and I’ve still got work to do, so…oops.&lt;/p&gt;

&lt;p&gt;Despite this very unsurprisingly dragging on a bit longer than I had initially hoped, speed has been a priority throughout. This is intended to be a small side project, and I want to make sure the cost-benefit of building it isn’t skewed &lt;em&gt;too&lt;/em&gt; far towards “cost.” To make sure of this, I’m building it largely with tools and frameworks I’m super familiar with — this app is 100% Swift &amp;amp; UIKit, with one small third-party library, &lt;a href=&quot;https://github.com/robb/Cartography&quot;&gt;Cartography&lt;/a&gt;, that I’ve written about before. I’m also leaning heavily on my own UI library, which I’ve built up over time, and that sits on top of UIKit and implements a bunch of reusable controls, custom transitions, etc. that match my aesthetic and coding style. In other words, I’m reusing everything I can!&lt;/p&gt;

&lt;h3 id=&quot;polish&quot;&gt;Polish&lt;/h3&gt;
&lt;p&gt;Seeing as this is a small project I’m releasing for free, the biggest practical benefit I can hope for from a career/indie cred perspective is for this app to showcase some of what I’m good at. And in general, what I’m good at (according to me) is paying attention to detail and building things that are polished and thoughtfully designed.&lt;/p&gt;

&lt;p&gt;Here’s the problem: polish takes time — sometimes, absurd amounts of time — and because it’s what I care most about, I am extremely susceptible to falling down nearly endless rabbit holes of tweaking and obsessing over tiny design details that regular users likely could not care less about.&lt;/p&gt;

&lt;p&gt;So, building this app has definitely been an exercise in trying to find some balance here. There are lots of fun details in there that I’m proud of, but I’ve taken plenty of shortcuts that I’d love to spend more time on too.&lt;/p&gt;

&lt;h3 id=&quot;learn&quot;&gt;Learn&lt;/h3&gt;
&lt;p&gt;Regardless of any “success metrics” an app release does or doesn’t hit, there’s always some value in building something if you can learn and improve as a developer along the way. 
Of course, learning takes time, so there was a balance to be struck here too. I would have loved to build a small app like this using SwiftUI, for example — and who knows, maybe that’s a project for another day — but I know that would have taken me a lot longer, and likely resulted in a less polished end-product.&lt;/p&gt;

&lt;p&gt;So, I largely stuck with the tools I knew, with one big exception: I’ve never built an app using CloudKit before. Building this with Firebase likely would have taken me less time, but CloudKit is an important framework that represented a big gap in my knowledge, and this seemed like an easy, constrained use case to get started on.&lt;/p&gt;

&lt;p&gt;Adding robust CloudKit syncing and sharing was definitely the hardest part of building this app, but I think I’m out of the weeds now, and have a good grasp on a framework I knew very little about just a few weeks ago. I may write a more technical article about my experiences with CloudKit soon, but in the meantime, I will simply say this: &lt;a href=&quot;https://twitter.com/_inside&quot;&gt;Gui Rambo&lt;/a&gt;’s &lt;a href=&quot;https://rambo.codes/posts/2020-02-25-cloudkit-101&quot;&gt;CloudKit 101&lt;/a&gt; was an absolutely massive help in getting my implementation off the ground. If you’re at all curious about learning CloudKit, I’d highly recommend starting there.&lt;/p&gt;

&lt;h2 id=&quot;cool-when-can-i-get-the-app&quot;&gt;Cool, when can I get the app?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Update from the future&lt;/strong&gt;: It’s available now! &lt;a href=&quot;https://apps.apple.com/app/id1511197431&quot;&gt;Check it out&lt;/a&gt; and let me know what you think 🎈&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-8-icon.png&quot; alt=&quot;Oh Bother app icon&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Tue, 28 Apr 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 7: Keep up!</title>
        <link>/keep-up/</link>
        <guid isPermaLink="true">/keep-up/</guid>
        <description>&lt;p&gt;Putting earth-shaking world news aside for a moment: the BIG news in our little iOS developer bubble over the past couple weeks has been the official addition of mouse and trackpad support in iPadOS 13.4. For various reasons, I think most of us love the idea of being able to use our iPad as a “computer” — with Xcode for iPadOS being the holy grail we’re all longing for — and this is a big step in that direction. On top of that, this is not your standard, run-of-the-mill mouse cursor; Apple has, &lt;a href=&quot;https://www.theverge.com/2020/3/18/21185188/ipad-trackpad-how-to-support-mouse-cursor&quot;&gt;in some subtle but significant ways&lt;/a&gt;, reimagined and redesigned the pointer on iPadOS, and the results are pretty exciting.&lt;/p&gt;

&lt;p&gt;Naturally, iOS developers and designers far and wide are now scrambling to figure out what this means for them and their apps. There are some new APIs to learn, sure; but perhaps more importantly, there are now a whole new set of design considerations to take into account when building an iPad app, and we all kind of have to collectively figure out what exactly those are. Of course, Apple’s &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/pointers/&quot;&gt;Human Interface Guidelines&lt;/a&gt; are a crucial starting point, but inevitably, there’s still a bit of a gap between written guidelines and effective third-party implementation. Twitter pal &lt;a href=&quot;https://twitter.com/_chuckyc&quot;&gt;@_chuckyc&lt;/a&gt; got a bit of a conversation going to this effect, and then summed it up nicely &lt;a href=&quot;https://twitter.com/_chuckyc/status/1243166785056182272&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m not going to dive deeper into these brand new interactions at this point, because I’ve still got some work to do myself to better understand the impact all this will have on building apps for iPad. As for properly adding pointer support to the little app I’m currently building, I haven’t gotten much further than tacking “Add thoughtful pointer support” onto my ever-growing to-do list.&lt;/p&gt;

&lt;p&gt;Which got me thinking: we sure are juggling a lot, huh?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-7.png&quot; alt=&quot;Juggling all the iOS features&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Things were pretty different when, on a whim, I decided I wanted to “be” an iOS developer in 2012. For starters, there was one iPhone size and one iPad size, and I don’t &lt;em&gt;think&lt;/em&gt; universal apps were a thing yet. There was no AutoLayout, no Storyboards, no Swift or SwiftUI. There were no app extensions, no Watch apps, no Siri intents and shortcuts, no 3D Touch. None of this “multiple scenes” business, no haptic feedback, no drag and drop, no dark mode, and of course, no trackpad and mouse support. I’m sure the list goes on and on — those are just a few things that spring to mind.&lt;/p&gt;

&lt;p&gt;These are all wonderful tools and features that improve our lives as developers on the whole, and allow us to build ever-more-engaging and delightful ways for people to interact with our apps. But for the most part, they’re adding to, rather than replacing, existing tools and features, and I wonder: are these all piling up and making the barrier to entry for iOS development significantly higher? Or has this been offset by other improvements to the developer experience? Part of me feels like iOS development in 2020 is a whole lot bigger and scarier-looking than iOS development in 2012. On the other hand, maybe a new iOS developer doesn’t need to care a whole lot about the details of drag and drop, dark mode, or hover states, and in fact, a modern UI framework like SwiftUI is making app development more accessible than ever.&lt;/p&gt;

&lt;p&gt;I kind of suspect the reality is closer to the latter, but if anyone out there is just getting into iOS development now…I’d love to hear what that experience has been like!&lt;/p&gt;

&lt;p&gt;More to the point: designing and building a great, fully-featured app — one that takes full advantage of the impressive hardware and vibrant platform it’s built on top of — is really quite difficult, regardless of your experience level. I don’t think that’s news to anyone reading this blog, but I thought it was worth dwelling on for a minute. Not to lament the complexity we deal with — I’m hugely thankful I build software on a platform that has evolved and improved so rapidly, and has stayed so relevant, for so long — but simply to remind us all that there’s a lot going on here, and we don’t need to be instant experts on every additional feature and tool that gets thrown our way.&lt;/p&gt;

&lt;p&gt;If you’re building an app on your own, all of the design and development complexity falls squarely on your shoulders. If you can write code and make your UI look “nice,” that’s a start, but it certainly doesn’t get you to the finish line — and with each iOS update, the race seems to get a little bit longer. In 2012, thanks to the combination of building on a younger platform and &lt;em&gt;truly&lt;/em&gt; having no idea what I was doing, most of what I was thinking about was plopping views into Interface Builder, moving things around to make them look good, and hooking IBActions up to my app logic. Today, even an app that seems straightforward on its surface likely has me thinking about:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Designing layouts that nicely adapt to a wide array of screen sizes&lt;/li&gt;
  &lt;li&gt;Making sure my universal app is built according to best practices on both iPhone and iPad&lt;/li&gt;
  &lt;li&gt;Supporting Dynamic Type, and ensuring my layouts can handle it&lt;/li&gt;
  &lt;li&gt;Supporting a wide variety of important accessibility features&lt;/li&gt;
  &lt;li&gt;Using 3D touch in a useful way, without hiding important functionality behind it (though this seems to be on its way out)&lt;/li&gt;
  &lt;li&gt;Thoughtfully designing for dark mode&lt;/li&gt;
  &lt;li&gt;Properly handling both software and hardware keyboards&lt;/li&gt;
  &lt;li&gt;Supporting useful keyboard shortcuts&lt;/li&gt;
  &lt;li&gt;Adding haptic feedback (without getting carried away)&lt;/li&gt;
  &lt;li&gt;And now…appropriate hover states in my UI for mouse/trackpad use!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the app, it’s not unusual to also need to think about significant bolt-ons like Siri integration, Today widgets, Spotlight search indexing, Watch extensions, and the like. And of course, this doesn’t even touch on all the more technical complexity in writing code that interacts with networking services, that’s modularized and well-architected and testable, that keeps up with best practices in Swift, etc.&lt;/p&gt;

&lt;p&gt;It’s a lot!&lt;/p&gt;

&lt;p&gt;Again, I’m not at all suggesting this is a bad thing, nor do I think this is unique to iOS development. But I do think it’s worth taking a step back and recognizing the breadth of knowledge involved in designing and building a great iOS app, both for the sake of celebrating the effort that goes into the best apps on the App Store, and as a helpful reminder when the scope of what you thought was a simple project starts to feel a bit overwhelming.&lt;/p&gt;

&lt;p&gt;While I may have a long list of iOS features that I’d love to integrate into my app eventually, I’m trying not to think of it as a checklist that needs to be completed in order for the app to be “finished.” Instead, it’s simply a ranked list of priorities. I realize I could probably spin my wheels for years trying to check off everything on that list, while constantly adding new things to the list as the iOS ecosystem continues to evolve. Instead, in this pre-release stage of development, I’m trying not to worry too much about keeping up with every shiny new tool or feature we get from Apple. There are a few existing features at the top of that priority list that I feel strongly about getting into version 1; anything beyond that can be chipped away at — and probably prioritized more intelligently — once I’ve got some folks actually using the app.&lt;/p&gt;

&lt;p&gt;My lofty ambitions are absolutely to get to everything on that list eventually, and build my little idea up into a best-in-class app on the App Store. But, honestly, I get frazzled easily when I feel like I’m juggling too many ideas and competing priorities at once. So, for myself and anyone else who needs to hear it: focus on a few high priority items, iterate, and don’t worry too much about keeping up with the latest on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPointerInteraction&lt;/code&gt;s until you’ve got a working app to interact with 😬&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thanks for keeping up with my happy little blog. Stay home, wash your hands, and find me on Twitter &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here!&lt;/a&gt;  🦆&lt;/p&gt;
</description>
        <pubDate>Sun, 29 Mar 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 6: Do it during a pandemic, I guess?</title>
        <link>/covid-19/</link>
        <guid isPermaLink="true">/covid-19/</guid>
        <description>&lt;p&gt;I’ll be honest: I didn’t accomplish a whole lot this week. As of right now, COVID-19 appears to be more or less causing the world to fall apart in a way that most of us have never really seen. It’s, uh…not great!&lt;/p&gt;

&lt;p&gt;So it’s been genuinely tough to focus on much of anything. On the face of it, my week shouldn’t have been particularly disrupted by any of this — I work from home every day anyways, and thankfully most of the work I’m doing right now hasn’t (yet) been meaningfully impacted by what’s going on — but it turns out global pandemics are distracting and stressful all on their own. Who knew?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-6.png&quot; alt=&quot;Trying to do work in a room that's on fire&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;&lt;a href=&quot;https://www.theverge.com/2016/5/5/11592622/this-is-fine-meme-comic&quot;&gt;This is fine.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nonetheless, the show must go on. From the sounds of it, we may all be practicing social distancing for quite some time, so it’s on me to make sure I don’t fall down an endless rabbit hole of hourly news updates and anxiety, and instead stay motivated to do the things I love to do and build the app that this blog is ostensibly all about.&lt;/p&gt;

&lt;p&gt;So, this week, I thought I’d keep it light and write up a short list of things I’ve been doing in an effort to stay on track and prevent my brain from melting out of my ears. This list is presented in order from “frivolous and possibly unhelpful” to “earnest and well-meaning,” so feel free to skip ahead if you’re not particularly interested in the 25-year-old sitcom I’ve been binge-watching.&lt;/p&gt;

&lt;p&gt;I promise I’ll get back to writing real, well-considered articles about indie app development next week. For now, stay safe out there, listen to the experts, and enjoy whatever nonsense I’m about to come up with 👇&lt;/p&gt;

&lt;h2 id=&quot;watch-seinfeld&quot;&gt;Watch Seinfeld&lt;/h2&gt;

&lt;p&gt;In a time where the walls of our modern globalized world are falling down around us, our reliance on technology is absolute and unsettling, and the true implications of this pandemic feel altogether too big to wrap your head around, consider watching this self-proclaimed “show about nothing,” steeped in 90s nostalgia, full of humour that (aside from the poor handling of certain social issues that so much 90s comedy cringily veers into) holds up remarkably well.&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;such&lt;/em&gt; a dumb show, but it’s a nice bit of respite from the real world in 2020.&lt;/p&gt;

&lt;h2 id=&quot;play-altos-odyssey&quot;&gt;Play Alto’s Odyssey&lt;/h2&gt;

&lt;p&gt;A bunch of wonderful indie game developers have decided to make their games free this week as a small gesture to help us all deal with this wacky and stressful moment. I picked up &lt;a href=&quot;http://www.altosodyssey.com&quot;&gt;Alto’s Odyssey&lt;/a&gt; this week, made by &lt;a href=&quot;https://twitter.com/builtbysnowman&quot;&gt;Snowman&lt;/a&gt;, a game studio in Toronto, and &lt;a href=&quot;https://twitter.com/harrynesbitt&quot;&gt;Harry Nesbitt&lt;/a&gt;, an artist and developer in the UK. It’s simple, it’s meditative, and it’s visually and aurally stunning. I’m not typically someone who plays many games on my phone, but this game has been a joy to pick up here and there over the last few days.&lt;/p&gt;

&lt;p&gt;A couple other chill games I love that you can grab for free right now are &lt;a href=&quot;https://dinopoloclub.com/games/mini-metro/&quot;&gt;Mini Metro by Dinosaur Polo Club&lt;/a&gt;, and &lt;a href=&quot;http://ashorthike.com&quot;&gt;A Short Hike by Adam Robinson-Yu&lt;/a&gt;. Check ‘em out!&lt;/p&gt;

&lt;h2 id=&quot;make-good-coffee&quot;&gt;Make good coffee&lt;/h2&gt;

&lt;p&gt;I’m a big coffee nerd, so this isn’t new behaviour, per se, but I’ve been trying to really appreciate the ritual of making and enjoying a great cup of coffee or two in the morning. It’s a good excuse to take a break from your screens, do something with your hands, and check in with your senses. Coffee is the best ☕️&lt;/p&gt;

&lt;p&gt;If you’ve only ever thought of coffee as bitter wake-up fuel, but you’re bored and stuck at home and want a new hobby: it’s not hard or particularly expensive to massively up your coffee game! Order some whole beans from a good local roaster, get yourself an &lt;a href=&quot;https://www.youtube.com/watch?v=QLEBfom0mhM&quot;&gt;inexpensive manual burr grinder&lt;/a&gt;, a &lt;a href=&quot;https://www.amazon.com/Hario-Ceramic-Coffee-Dripper-White/dp/B000P4D5HG/&quot;&gt;v60&lt;/a&gt; and some &lt;a href=&quot;https://www.amazon.com/Hario-V60-Coffee-Filters-Natural-Tabbed/dp/B001O0R46I/&quot;&gt;filters&lt;/a&gt;, and you’re off to the races.&lt;/p&gt;

&lt;h2 id=&quot;play-pandemic-and-win&quot;&gt;Play Pandemic (and win)&lt;/h2&gt;

&lt;p&gt;If you don’t know, &lt;a href=&quot;https://www.zmangames.com/en/games/pandemic/&quot;&gt;Pandemic&lt;/a&gt; is a cooperative board game about a rather far-fetched scenario where the world is at war with a dangerous and highly contagious virus, and it’s your job to contain the outbreaks and find a cure before it gets out of hand.&lt;/p&gt;

&lt;p&gt;Anyway, we thought it’d be funny to play last night, and I’m happy to report that we won handily. We can beat this thing, folks!&lt;/p&gt;

&lt;p&gt;Actually, we played twice and lost pretty badly the first time, but that first one was just bad luck or whatever. Doesn’t count.&lt;/p&gt;

&lt;h2 id=&quot;listen-to-some-good-tunes&quot;&gt;Listen to some good tunes&lt;/h2&gt;

&lt;p&gt;Music can be a great tool for both relaxing and getting focused, provided you find some stuff that tickles your brain in just the right way. Here are a handful of records I’ve been listening to this week to help me chill out, be productive, or both.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fellow Twitter user &lt;a href=&quot;https://twitter.com/mattie&quot;&gt;@mattie&lt;/a&gt; tweeted about &lt;a href=&quot;https://open.spotify.com/artist/2c0SyAUT82Al3gJQ3uezBv?si=dfYj0f8kQliD-C4RPxQvhQ&quot;&gt;Slumberville&lt;/a&gt; this week, and I’ve been grooving to these lo-fi beats ever since.&lt;/li&gt;
  &lt;li&gt;For a similar but fuller, more jazzy vibe, I’ve really enjoyed putting &lt;a href=&quot;https://open.spotify.com/artist/6rvxjnXZ3KPlIPZ8IP7wIT?si=DnTJVkroRfuGSszDBkpHpQ&quot;&gt;Sweatson Klank&lt;/a&gt;’s new record on in the background.&lt;/li&gt;
  &lt;li&gt;If you’re looking for some more folksy tunes to get wistful and hopeful to, I highly recommend &lt;a href=&quot;https://open.spotify.com/album/56e3BVWR49BZT5DrWsL4gN?si=hiuTowhKSKSseRPNZ0Ifqg&quot;&gt;Walter Martin’s latest, The World at Night&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;get-excited-about-news-from-apple&quot;&gt;Get excited about news from Apple&lt;/h2&gt;

&lt;p&gt;It’s been a little bit weird and disorienting to scroll through Twitter this week and bounce wildly between sobering tweets about the world being on fire, and giddy tweets about the new Magic Keyboard and cursor support in iPadOS 13.4. My initial impulse was to wonder: is it somehow insensitive or unhelpful to be focused on relatively frivolous product releases from one of the world’s richest companies at a time when so many people’s finances and well-being are being thrown into disarray?&lt;/p&gt;

&lt;p&gt;Ultimately, I’m confident the answer is no. I think it’s important and healthy to find things to get excited about as a community, and do our best to find some normalcy in all the uncertainty. I’m really jazzed about what looks to be a really thoughtful and well-crafted implementation of &lt;a href=&quot;https://www.theverge.com/2020/3/18/21185188/ipad-trackpad-how-to-support-mouse-cursor&quot;&gt;mouse/trackpad support on iPad&lt;/a&gt;, and I can’t wait to see where the iPad goes from here.&lt;/p&gt;

&lt;p&gt;Let’s just remember how lucky we are to be able to keep doing and talking about this stuff for a living 🙂&lt;/p&gt;

&lt;h2 id=&quot;improve-your-approach-to-project--time-management&quot;&gt;Improve your approach to project &amp;amp; time management&lt;/h2&gt;

&lt;p&gt;Here’s an actual work-related thing I did this week: I could already feel my motivation and focus fading pretty quickly last weekend, so to try to get a step ahead of it, I made an effort to refine and improve the way I plan my time.&lt;/p&gt;

&lt;p&gt;I ended up leaning into &lt;a href=&quot;https://www.notion.so&quot;&gt;Notion&lt;/a&gt; pretty heavily, and for all my whining about not getting much done this week, it’s actually really helped to keep me on track, all things considered. Without getting too far into the weeds, the crux of what I’ve got set up is a kanban board that I’ll clean up and review every week, with a calendar view hooked up to it that allows me to easily schedule and adjust my tasks throughout the week. The bigger point is this: if you’re lacking motivation and having a hard time getting stuff done, now might actually be a good time to think a bit about your tools and habits, and see if you can’t leverage some new ones to give yourself a bit of a boost.&lt;/p&gt;

&lt;h2 id=&quot;be-kind-to-yourself&quot;&gt;Be kind to yourself&lt;/h2&gt;

&lt;p&gt;Despite my best efforts — and despite being in the extremely fortunate position of not having had my world turned upside down by layoffs, business closures or illness — this week was still…a total mess. I slept like garbage, didn’t exercise enough, wasted an unbelievable amount of time staring at my phone, and got done about half of what I was aiming for. But, ultimately: who could blame me? We’re all still learning about this strange new reality, and it’s going to take some time to get the hang of it. Even if this week felt like a total write-off, don’t beat yourself up about it. Let’s be thankful for what we’ve got, stay positive wherever we can, help out those in need, and take another crack at it next week 💪&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;WHAT A TIME TO BE ALIVE. Let’s get through it together. Find me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; and let me know how you’re doing 🦆&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Mar 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 5: Suffer from crippling imposter syndrome</title>
        <link>/imposter-syndrome/</link>
        <guid isPermaLink="true">/imposter-syndrome/</guid>
        <description>&lt;p class=&quot;emphasis-text&quot;&gt;Has this ever happened to you?&lt;/p&gt;

&lt;p&gt;You’re an iOS developer. Maybe you’ve been doing it for a while, or maybe you’re new to the scene. Maybe you prefer to think of yourself as an “iOS engineer,” because it sounds better and no one seems to agree on whether there’s a difference. Whatever the situation: you’re feeling restless. You’ve worked on interesting projects before — successful ones, even — and you recognize that getting paid to do what you do is in many ways a privilege. Nonetheless, you can’t shake the feeling that none of the work you’ve done is truly &lt;em&gt;yours&lt;/em&gt;. Much of your time has been spent helping others achieve their goals — and that’s okay! — but you increasingly feel like you’ve got some of your own goals to chase too.&lt;/p&gt;

&lt;p&gt;You want to build something that belongs to you, you want to pour your heart into it, and frankly, you’d like to find some success doing it. “It’s time,” you proclaim boldly, “for me to build an app.”&lt;/p&gt;

&lt;p&gt;You built an app or two on your own, back in the day. Oh, the naïveté of your youth! You tried to be smart about it and temper your expectations, but be honest: you were pretty sure that thing you built — you know, the one with the ball of mud code base, the baffling feature choices and the inscrutable UI — was going to be a hit. You shared it with your aunts and uncles on Facebook, sent a few bland emails to some app review websites, and sat back and waited for this thing to spread like wildfire.&lt;/p&gt;

&lt;p&gt;It didn’t.&lt;/p&gt;

&lt;p&gt;This time, though, maybe things will be different. You’re &lt;em&gt;much&lt;/em&gt; older and wiser. You’ve got loads of real-world experience under your belt now, and, albeit from a distance, you’ve seen what it takes to have some success on the App Store in 2020. If all these other folks can do it…surely there’s space for you too?&lt;/p&gt;

&lt;p class=&quot;vertical-image&quot;&gt;&lt;img src=&quot;/assets/img/imposter-1.png&quot; alt=&quot;Reaching to place your app among the very best&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;Yeah, I’ll just put this…right…here…&lt;/p&gt;

&lt;p&gt;From the moment you decided to get into iOS development, your career has been coloured by this vague idea that true success in this industry is nearly or completely single-handedly building something that grabs people’s attention. At the end of it all, you don’t just want to be “[Your Name Here]”. No, at the pinnacle of this climb, you want to be known as “[Your Name Here], creator of [Your Brilliant and Beloved App]”. Of course, rationally, you know there are loads of people who are living successful and fulfilling careers as employees at great companies, but a big part of you still feels that, as someone who can competently design and build software, you are uniquely positioned to create your own life’s work. That’s what you’ve been led to believe, anyway. And isn’t that the dream? Wouldn’t it be a shame not to try? You’re tired of deferring your dreams to your future self; it’s time to act!&lt;/p&gt;

&lt;p&gt;So, naturally, you fire up Twitter.&lt;/p&gt;

&lt;p&gt;You’ve been lurking on Twitter for years, but you’ve never tweeted much. A few times, you’ve toyed with the idea of actually trying to build some sort of “presence” on Twitter, but there are a few things that keep getting in the way:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You’ve never felt you have anything interesting to say that hasn’t already been said by at least a thousand other people desperately trying to build their own Twitter presence.&lt;/li&gt;
  &lt;li&gt;You’re already addicted enough to Twitter &lt;em&gt;without&lt;/em&gt; chasing likes and retweets.&lt;/li&gt;
  &lt;li&gt;You basically believe that Twitter is bad for you and for society.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This has tended to manifest itself in the following way:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Every year or two, you work up the courage to tweet something clever.&lt;/li&gt;
  &lt;li&gt;Zero (0) people like your tweet — probably because you effectively have zero (0) followers.&lt;/li&gt;
  &lt;li&gt;You delete your tweet sheepishly and hope no one noticed you had the audacity to think you were clever enough for Twitter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, though, you are not only older and wiser; you are emboldened by your decision to jump head-first into building your own app. You’re feeling a little bit reckless, in an intentional and exciting sort of way. And for better and for worse, anyone who’s anyone in the iOS community seems to be on Twitter. If you want a shot at making a name for yourself — at going from “[Your Name Here]” to “[Your Name Here], creator of [Your Brilliant and Beloved App]” — having a real presence on Twitter kind of seems like a must.&lt;/p&gt;

&lt;p&gt;So, you start following more iOS folks, and you endeavour to start shamelessly yelling into the void.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/imposter-2.png&quot; alt=&quot;Yelling into the void&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;*Crickets*&lt;/p&gt;

&lt;p&gt;In the beginning, you’re feeling confident. You don’t have any followers yet, but you believe in your ability as an iOS developer, and you’re committed to this new idea that, actually, you &lt;em&gt;do&lt;/em&gt; have something unique and interesting to say, damn it, and there &lt;em&gt;are&lt;/em&gt; strangers out there who want to hear from you. You comment on an iOS celebrity’s tweet (who at this point, to you, is anyone with more than a few hundred followers)…and they like it! You feel &lt;em&gt;seen&lt;/em&gt;. What a rush.&lt;/p&gt;

&lt;p&gt;Sometimes you tweet, and no one seems to care, but you brush it off. “Just keep yelling,” you tell yourself. Slowly but surely, some followers of your own start trickling in. One day you tweet about something you’re working on — maybe it’s a sneak peek at your new app, or a blog post you wrote — and a few complete strangers go out of their way to comment on your thing in a kind and supportive way. “Thanks, [Stranger’s Name]!”, you reply. You make sure to use their name so that you come across as genuine. You feel validated. Hey, Twitter is fun!&lt;/p&gt;

&lt;p&gt;One day you’re feeling a bit down, and out of nowhere, unprompted, someone tweets about some work you shared, singing your praises and encouraging others to check it out. Your mood flips. What a thrill! What a supportive community! You immediately want to get more involved and help lift up others. Your app is coming along, and it looks like there might actually be some people out here who care about what you’re doing. This is good! If you can keep this up, then with a bit of luck, you might just build up enough of an audience to give your app some traction when it finally comes out. You’re feeling hopeful.&lt;/p&gt;

&lt;p&gt;Eventually, though, the novelty starts to wear off a bit. Realistically, you’re still miles away from having any sort of real iOS Twitter clout, the work required to get there seems like an unpredictable slog, and you start to feel a little bit uneasy about the whole thing.&lt;/p&gt;

&lt;p&gt;For starters, it sure can be hard not to live and die by the reaction you get to your tweets. Unsurprisingly, you find that the sense of validation you get from a like or a retweet feels really good, for a minute, while the lack thereof can kind of hijack your mood, and make you question…[gestures broadly] everything. You feel this happening, and you try your best not to tie your self-worth so tightly to the whims of social media, but that sure is easier said than done — especially when it’s all tangled up with this big decision you made to build your own product and prove to yourself that you can grind your way to some level of success.&lt;/p&gt;

&lt;p&gt;You give your head a shake. “I’m not here for an ego boost,” you think to yourself. You want to get to know some like-minded people! You want to learn from those who are a few steps ahead of you on your iOS development journey, and help out those who are a few steps behind! You want to put yourself out of your comfort zone a little bit to challenge yourself and grow as a person! That’s all true, and it makes you feel better, for a while.&lt;/p&gt;

&lt;p&gt;But soon, your human nature starts to get the better of you in a different way. Naturally, you’re following a bunch of well-known and generally successful members of the iOS community, because you figure you stand to learn a lot from their experiences, and because it’s a good way to get involved in conversations that might expose you and your work to a bunch of other people in the community. Unfortunately, this can quickly turn into a remarkably efficient way to bombard yourself with reasons to feel bad about your career. “Hold on, that person with 4 great apps in the App Store is &lt;em&gt;how&lt;/em&gt; old?” “Wait, that dude is making &lt;em&gt;how&lt;/em&gt; much money working for that amazing company?” “You’re telling me this person has written several books, hosts 3 podcasts, organizes community meet-ups, regularly speaks at conferences, has sold two companies, has won an Apple Design Award, is politically active, volunteers at a shelter twice a week, is an ultra distance runner, has 4 kids and is &lt;em&gt;really&lt;/em&gt; into sourdough, while I…play a lot of video games, I guess?”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/imposter-3.png&quot; alt=&quot;A failed sourdough experiment&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Suddenly, you’re spiralling. You decide to be self-deprecating about the whole thing — “I have no idea what I’m doing, ha ha” — because it works pretty well as a defence mechanism, but meanwhile you’re more doubtful than ever about the amount of time you’re pouring into your app. What made you think this was worthwhile, anyways? What made you think you’re talented enough to pull this off? What are you missing out on by spending all this time on your silly pet project?&lt;/p&gt;

&lt;p&gt;And then things &lt;em&gt;really&lt;/em&gt; start to go off the rails. Since you feel like you’re playing catch-up with all these successful people you’re trying to get in with, the progress you’re making on your app never feels like enough. It becomes hard to manage your time and balance your priorities, and when you’re not careful, other important aspects of your life fall by the wayside.&lt;/p&gt;

&lt;p&gt;At your worst, you start feeling jealous of all these people who seem broadly to have “achieved” more than you. Why them, and not you? “Oh right,” you remember, “I haven’t actually built anything yet. That might help.” But then: why haven’t you? You’re getting old; what have you been doing all these years? The thought scares you into making more progress on your app, for a while.&lt;/p&gt;

&lt;p&gt;Meanwhile, Twitter starts to feel like a strange and rather disingenuous game. You can’t think of anything worth saying, but you know you can’t grow your audience if you don’t say things, so you spend way too much time scrolling through tweets trying to find one you can leave an intelligent comment on — hopefully just clever enough that a handful of people will click through to your profile and follow you on a whim.&lt;/p&gt;

&lt;p&gt;As you scroll, and scroll, and scroll, you see not only the big fish, but the little fish too. You see what appear to be passionate, talented and well-meaning people doggedly dedicating hundreds or thousands of hours towards projects they’re excited and hopeful about, bravely putting themselves out there, trying every way they know how to drum up interest on Twitter and share the amazing thing they’ve built with the world. Often, you see these attempts fall flat — a handful of likes, an encouraging comment or two, and then nothing — and your heart aches for these people. You see yourself in them, and you shudder.&lt;/p&gt;

&lt;p&gt;You look back at the big fish, only to see them complaining about how they too feel insecure about their work, feel like imposters, feel like they haven’t achieved everything they can or should, and you believe them. Ultimately, it makes you pause and wonder: does the treadmill ever stop? What are you actually chasing here? It’s not just Twitter fame, surely. It’s much bigger and nobler than money, right? But then…what is it, exactly? For a while, you honestly can’t remember, and your motivation tanks.&lt;/p&gt;

&lt;p&gt;Woof.&lt;/p&gt;

&lt;p&gt;And then you’re in the shower at the end of another long and harrowing day on the internet, staring blankly at the tiles, when out of nowhere, a lightbulb goes off: you’ve got a solution to that UX problem that’s been nagging you for weeks! It’s an important piece in the puzzle, and suddenly that bigger feature idea that was bouncing around in the back of your mind fits beautifully into the rest of the app. Your mind is racing with new ideas to explore as you jump out of the shower and clumsily dry your hands before pounding some stream-of-consciousness thoughts into the Notes app on your phone.&lt;/p&gt;

&lt;p&gt;Nothing in your world is more exciting right now than building this feature, and you can’t wait to show it to your partner, your roommate, or, really, anyone who’s willing to listen. Building this thing, and seeing it come to life, is a pure and genuine joy. You excitedly share a sneak peak of your work on Twitter, and suddenly, you remember what this is really all about: what a privilege —  and what magic! — to put every ounce of creative energy you’ve got into something you can truly feel proud of, to use these remarkable machines we call “computers” to build something out of nothing, and to share that something — big or small, polished or half-baked, silly or profound — with other humans. Creativity for creativity’s sake is a noble and timeless endeavour, and, at the risk of sounding a tad melodramatic: you’re just happy to have skills that allow you to participate in this uniquely human tradition.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/imposter-4.png&quot; alt=&quot;An artist celebrates their work&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;The creator creates!&lt;/p&gt;

&lt;p&gt;Two hours later, your tweet has zero (0) likes.&lt;/p&gt;

&lt;p class=&quot;emphasis-text&quot;&gt;“What the actual f—”&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Hey, thanks for making it to the end! Hope you enjoyed my ramblings. Find me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; and let me know what you think 🦆&lt;/p&gt;
</description>
        <pubDate>Thu, 12 Mar 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Mastering view controller transitions, part 3: Make them resizable</title>
        <link>/2020/03/03/vctransitions3.html</link>
        <guid isPermaLink="true">/2020/03/03/vctransitions3.html</guid>
        <description>&lt;p&gt;This week, I’m doing a set of 3 short articles that explore some more advanced, concrete ideas around custom transitions. I give some additional context in the first one, so you may want to take a gander if you haven’t already:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://danielgauthier.me/2020/02/24/vctransitions1.html&quot;&gt;Mastering view controller transitions, part 1: make them reusable&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;&lt;a href=&quot;https://danielgauthier.me/2020/02/27/vctransitions2.html&quot;&gt;Mastering view controller transitions, part 2: make them feel natural&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a quick refresher, here’s an example of the transitions we introduced in the first article, and that all this writing is based on:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/game-demo.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p&gt;Okay, you’re up to speed! Let’s dive into the third and final goal.&lt;/p&gt;

&lt;h2 id=&quot;goal-3-i-want-my-presented-view-controller-to-be-able-to-easily-define-and-adjust-its-own-size&quot;&gt;Goal 3: I want my presented view controller to be able to easily define and adjust its own size&lt;/h2&gt;
&lt;p&gt;As I’ve mentioned in the past couple articles, it was really important to me in building these transitions that they were easy to reuse in different contexts. That means that if I’m building a simple presentation that slides in from the bottom, I certainly don’t want every view controller that uses it to be locked to one specific size — the size should depend on the content! — and I also don’t want to have to think too hard about setting the size appropriately every time I use my custom transition with a new view controller.&lt;/p&gt;

&lt;p&gt;On top of that, I also quickly realized that it was important for me to be able to change the size of my view controllers while they were presented — as the content inside a presented view controller changed based on user input, I wanted the view controller to be able to grow or shrink to accommodate the content updates. (You can see this happening as tags and notes are added to the “Add game” view in the video above.)&lt;/p&gt;

&lt;p&gt;Ultimately, we can accomplish all this (and more!) with the help of Auto Layout, a simple custom presentation controller, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; protocol we’ve been using in the past two articles.&lt;/p&gt;

&lt;p&gt;The first thing we’ll need to make sure of is this: our presented view needs to have its constraints set up such that they fully define the presented view’s height. In other words, there needs to be an unbroken chain of constraints and subviews stretching from the top edge to the bottom edge of the presented view.&lt;/p&gt;

&lt;p&gt;To make sure that point is clear, let’s start by looking at a somewhat contrived example. Imagine we’re just trying to present a view that contains a big rectangle with a smaller button underneath. One way we might set up constraints is like this:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-5-3-1.png&quot; alt=&quot;Missing bottom constraint&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a valid set of constraints — if you installed these in a full-screen view, you’d get a rectangle and button at the top of the view as expected. But, these constraints don’t explicitly define the height of their superview, because there’s no bottom constraint; there could be any amount of space between the bottom of the button and the bottom of its superview, and these constraints would be satisfied.&lt;/p&gt;

&lt;p&gt;Similarly, this is another valid set of constraints:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-5-3-2.png&quot; alt=&quot;Missing height constraint&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you installed these in a full-screen view, the rectangle would fill most of the screen, with the button sitting at the bottom. But these constraints don’t explicitly define the height of their superview either, because the rectangle view has no height constraint; these constraints could be satisfied in a superview of any height, because the height of the rectangle will simply adjust accordingly.&lt;/p&gt;

&lt;p&gt;What we need is this:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;&lt;img src=&quot;/assets/img/indie-5-3-3.png&quot; alt=&quot;All necessary constraints are set&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this situation, the constraints will only be satisfied if the superview is exactly 198 points tall: 100 for the rectangle, 50 for the button, and 16x3 for the top, bottom and in-between spaces. Of course, if we tried to install these constraints in a full-screen view, the constraints would break, since an iPhone screen isn’t 198 points tall. But this is actually exactly what we want: our constraints now define exactly what the height of their superview should be, and we’ll be able to use that fact to ensure that our presented view is sized correctly.&lt;/p&gt;

&lt;p&gt;Next, we’ll set up our custom presentation controller. The presentation controller is responsible for defining what the frame of the presented view should actually be. It’ll do this by defining a constant width that all presented views should have, and then using that to calculate what the presented view’s height should be, based on its content and constraints.&lt;/p&gt;

&lt;p&gt;Luckily, UIKit makes it pretty easy to do this. We’ll need to override a function and a variable in our custom presentation controller to get this to work:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;containerViewWillLayoutSubviews&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;presentedView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frameOfPresentedViewInContainerView&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;frameOfPresentedViewInContainerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGRect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;containerView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presentedView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentedView&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zero&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;safeAreaFrame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;safeAreaInsets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;targetWidth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;safeAreaFrame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inset&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fittingSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layoutFittingCompressedSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;targetHeight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentedView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;systemLayoutSizeFitting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fittingSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;nv&quot;&gt;withHorizontalFittingPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;nv&quot;&gt;verticalFittingPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultLow&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;safeAreaFrame&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inset&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8.0&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetWidth&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetHeight&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notes&quot;&gt;Note: credit for the code above goes to &lt;a href=&quot;https://twitter.com/kylebshr&quot;&gt;Kyle Bashour&lt;/a&gt;, whose &lt;a href=&quot;https://kylebashour.com/posts/custom-view-controller-presentation-tips&quot;&gt;excellent article&lt;/a&gt; helped me figure this out 🙏&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frameOfPresentedViewInContainerView&lt;/code&gt;, we first figure out what the width of the presented view should be, based on our desire to have it inset 16 points from either side of the screen. The next part — setting a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fittingSize&lt;/code&gt; and then calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemLayoutSizeFitting&lt;/code&gt; — essentially amounts to us asking UIKit the following: given our target width, what’s the smallest height that the presented view could have that satisfies all its internal constraints? (In our rectangle and button example above, we’d get 198 as our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetHeight&lt;/code&gt;.) We then position the frame where we want it to be, give it the size we calculated, and return it.&lt;/p&gt;

&lt;p&gt;When our presentation kicks off and the container view is about to layout its subviews, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerViewWillLayoutSubviews&lt;/code&gt; gets called. This gives us the chance to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frameOfPresentedViewInContainerView&lt;/code&gt; to assign a frame with the correct width and height to our presented view before it gets presented. When all is said and done, we get something like this:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-3-eg1.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p&gt;Just like that, we now have the ability to present a view controller of any size, as long as that view controller’s view defines its own height with constraints. However, we’re still missing a big piece of the puzzle: this doesn’t currently account for any changes in the presented view controller’s content &lt;em&gt;after&lt;/em&gt; it’s been presented. A change in the layout of our presented view doesn’t automatically trigger &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerViewWillLayoutSubviews&lt;/code&gt;, which means we don’t have a chance to re-calculate and re-assign &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frameOfPresentedViewInContainerView&lt;/code&gt;. No big deal; we’ll just have to trigger it ourselves.&lt;/p&gt;

&lt;p&gt;One approach we can take to make this kind of dynamic layout update simple and reusable is to add this functionality to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; protocol — a protocol that our presented view controllers are already required to conform to. Let’s update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; to look something like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalHandlingScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updatePresentationLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalHandlingScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	
  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updatePresentationLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setNeedsLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withDuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;usingSpringWithDamping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;initialSpringVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allowUserInteraction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;layoutIfNeeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;layoutIfNeeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we’ve used the new Swift 5 feature that allows protocols to “inherit” from classes — enforcing that any class that conforms to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; is a view controller. This then allows us to provide a default implementation for our new function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updatePresentationLayout(animated:)&lt;/code&gt;, via protocol extension. This function simply marks our presentation controller’s container view as needing a layout update, and then performs the layout update, animating it if requested.&lt;/p&gt;

&lt;p&gt;Now, whenever something happens in our presented view controller’s layout that requires a size change, we can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updatePresentationLayout(animated:)&lt;/code&gt; to update the presented view’s frame. In the case of our simple rectangle example, we can respond to the button tap like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@objc&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;changeSizeButtonTapped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;rectangleHeightConstraint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;constant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;updatePresentationLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we simply change the height of the rectangle to a random value, and then call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updatePresentationLayout(animated: true)&lt;/code&gt; to perform the update. Here’s what that looks like:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-3-eg2.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p&gt;This is a simple example, but the exact same principles are applied in the more complex transitions shown in the video at the top. For example, when the “Notes” button is tapped to reveal the text input view, that text input view’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isHidden&lt;/code&gt; property is set to false; since it’s arranged inside a stack view, the stack view automatically reveals the view and adjusts its own intrinsic size; and then I simply call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updatePresentationLayout(animated: true)&lt;/code&gt; to give my presentation controller a chance to resize the presented view based on these internal changes.&lt;/p&gt;

&lt;p class=&quot;notes&quot;&gt;Note: Sometimes, the Auto Layout engine tries to update the layout of your presented view &lt;em&gt;before&lt;/em&gt; the view’s frame has a chance to update, which will cause constraint conflicts if the views and constraints in your presented view no longer fit properly inside the old frame. A simple way to deal with this is to lower the priority of your bottom-most constraint to 999, which gives Auto Layout a bit of flexibility to properly sort things out when your layout is changing.&lt;/p&gt;

&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;/h3&gt;

&lt;p&gt;Once you wrap your head around what the role of a custom presentation controller should be, there are a whole bunch of interesting things you can start do with it. In my custom transitions, my presentation controller is responsible for detecting the appearance of the keyboard, and adjusting the positioning of the presented view based on the keyboard’s frame. It communicates with the presented view controller (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt;) to ensure that the current first responder is always visible between the top of the screen and the top of the keyboard. It also uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; to hide the status bar whenever the presented view is shifted beyond the top of the screen. You can see all this in action in the video at the top, as I start adding notes to my “Undertale” entry.&lt;/p&gt;

&lt;p&gt;I’d love to share some of those implementation details another day; regardless, my hope at this point is that, having read this little series on custom view controller transitions, you now have all the tools you need to get creative and build your own fun, reusable, well-polished view controller transitions. It’s a tricky little API, but if you put a bit of effort into understanding it up front, it really does open up possibilities that otherwise might have felt overwhelmingly difficult or tedious to build.&lt;/p&gt;

&lt;p&gt;So…go build something awesome! 🦸&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a working example of the techniques described here and in the other two articles in this series on custom view controller transitions, check out &lt;a href=&quot;https://github.com/danielmgauthier/ViewControllerTransitionExample&quot;&gt;this repo on GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thanks a bunch for following along. This is a relatively quick look at a pretty tricky topic, so if you need some help or have any follow-up questions, Let me know! Find me on Twitter &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here&lt;/a&gt; 🦆&lt;/p&gt;
</description>
        <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Mastering view controller transitions, part 2: Make them feel natural</title>
        <link>/2020/02/27/vctransitions2.html</link>
        <guid isPermaLink="true">/2020/02/27/vctransitions2.html</guid>
        <description>&lt;p&gt;This week, I’m doing a set of 3 short articles that explore some more advanced, concrete ideas around custom transitions. I give some additional context in the first one, so you may want to take a gander if you haven’t already:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://danielgauthier.me/2020/02/24/vctransitions1.html&quot;&gt;Mastering view controller transitions, part 1: make them reusable&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a quick refresher, here’s an example of the transitions we introduced in the first article, and that all this writing is based on:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/game-demo.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p&gt;Okay, you’re up to speed! Let’s dive into the second goal.&lt;/p&gt;

&lt;h2 id=&quot;goal-2-i-want-my-interactions-to-feel-natural&quot;&gt;Goal 2: I want my interactions to feel natural&lt;/h2&gt;

&lt;p&gt;Making interactions feel “natural” is a bit of a subjective thing, but for me, there were a few details I knew I wanted to get right when it came to dismissing my view controllers interactively. They’re all fairly obvious best practices when it comes to animating and interacting with views, but some of them can be a bit tricky to get right in the context of view controller transitions.&lt;/p&gt;

&lt;p&gt;Keep in mind that an interactive transition can look like anything, really, and these tips aren’t universally applicable to any and all interactive transitions; rather, for the sake of example, these are focused primarily on “swipe down to dismiss” interactions, and the code samples are derived from my implementation of the “Tags” view being dismissed in the video above. My hope is for these to get you thinking about how you might add similar polish to your own transitions, even if they operate slightly differently than mine.&lt;/p&gt;

&lt;p&gt;I also want to call attention to the fact that, while it might seem like a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt; could get us most of the way towards implementing my “Tags” view’s interactive dismissal, I ended up building my interactive transitions using my own implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIViewControllerInteractiveTransition&lt;/code&gt;, rather than using or subclassing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt;. I had a whole thing written about all the issues I ran into, and why I made this decision, but it got a little bit out of hand and this post is already long enough as it is. So, although most of what’s in this post would apply in either case, just keep in mind that the code examples are based on a custom interaction controller. Maybe I’ll post about the trials and tribulations of adopting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt; one day; for now, let’s start looking at a few tips you might be able to use to make your interactive transitions feel great.&lt;/p&gt;

&lt;h3 id=&quot;use-spring-animations&quot;&gt;Use spring animations&lt;/h3&gt;

&lt;p&gt;When you’re animating the movement of views, don’t do this:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-2-eg1.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Ew, yuck.&lt;/p&gt;

&lt;p&gt;Instead, use spring animations. They make your animated views feel more like physical, real-world objects than they would with linear or easing curves, and are standard across iOS. Generally speaking, you can implement them with something like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewPropertyAnimator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dampingRatio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// animate your thing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, if you’re old-fashioned, like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withDuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;usingSpringWithDamping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;initialSpringVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allowUserInteraction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// animate your thing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next!&lt;/p&gt;

&lt;h3 id=&quot;take-gesture-velocity-into-account&quot;&gt;Take gesture velocity into account&lt;/h3&gt;

&lt;p&gt;For our views to feel like physical, real-world objects, we want them to have some momentum when we fling them around. To do this, we can take advantage of an oft-misunderstood parameter of spring animations: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialVelocity&lt;/code&gt;. This parameter is exactly what’s going to allow us to carry the velocity of our gesture into the animation that immediately follows, so that we don’t end up with something like this:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-2-eg2.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Nope, don’t like this.&lt;/p&gt;

&lt;p&gt;Unfortunately, there’s a reason this parameter is a bit misunderstood: it’s not quite as simple as grabbing the velocity from our gesture recognizer and plugging it in. While the gesture recognizer gives its x and y velocity in points/second, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialVelocity&lt;/code&gt; expects those values to be normalized to the total distance that the animation covers. So, when your gesture ends, and you’ve decided based on that gesture that the transition should be completed, here’s a condensed version of how you might set up your animation:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@objc&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleGesture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPanGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;superview&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ended&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;gestureEnded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interruptedTranslation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureEnded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;initialSpringVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;springVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;distanceToTravel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionDistance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;springVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;distanceToTravel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;distanceToTravel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureVelocity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distanceToTravel&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not so hard!&lt;/p&gt;

&lt;h3 id=&quot;be-smart-about-deciding-whether-to-complete-or-cancel-a-transition&quot;&gt;Be smart about deciding whether to complete or cancel a transition&lt;/h3&gt;

&lt;p&gt;This one is somewhat related to the previous point: much like we should take velocity into account when performing our post-gesture animation, we should also take it into account when deciding which direction to animate in the first place. When the pan gesture is released, it might be tempting to simply evaluate whether the drag extended across a certain threshold: for example, if the gesture has travelled at least half the total dismissal distance, complete the dismissal, and otherwise, cancel it and animate back to the starting position. Unfortunately, this doesn’t end up feeling very natural:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-2-eg3.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;No, this certainly won’t do.&lt;/p&gt;

&lt;p&gt;If the user flings your view down with some speed, they expect it to continue moving in that direction. So, you’ll want to add a bit of logic that looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureEnded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionDistance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;initialSpringVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;springVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;distanceToTravel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionDistance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;initialSpringVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;springVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;distanceToTravel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureVelocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This basically says that if there’s significant velocity in the dismissal direction, dismiss the view controller no matter what. Otherwise, only dismiss if the gesture has moved at least half the total distance, and there isn’t significant velocity in the opposite direction. I’ll note that 300 is a bit of a magic number here that you might want to tweak, but you definitely don’t want to just check whether the velocity is positive or negative; if the user stops their movement and lets go, it’s anyone’s guess whether the gesture’s final velocity will be slightly positive or slightly negative.&lt;/p&gt;

&lt;h3 id=&quot;make-sure-your-interaction-is-always-enabled&quot;&gt;Make sure your interaction is always enabled&lt;/h3&gt;

&lt;p&gt;Nothing breaks the illusion of dragging physical objects around on a screen quite like suddenly and unexpectedly being unable to interact with an object you were just dragging around a second ago. Here’s what we don’t want:&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-2-eg4.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Woof.&lt;/p&gt;

&lt;p&gt;Sure, it might seem like a bit of an edge case, and maybe costing your users a fraction of a second doesn’t seem like the end of the world, but this kind of behaviour can cause tiny moments of frustration and make it feel like there’s a bit of an unpredictable barrier between your users and the objects they’re trying to manipulate on screen. This was a bit tough to get right in my case, and I’m not sure this is the most elegant solution, but my basic approach is this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Keep track of whether an interaction is in progress with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactionInProgress&lt;/code&gt; flag. Set this flag to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; as soon as a gesture begins, and only set it back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; when the cancellation animation finishes completely, i.e. &lt;strong&gt;the dragged view is completely at rest&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Only ever call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;viewController.dismiss()&lt;/code&gt; to begin the transition when the flag is flipped from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;. End the transition only when the flag is flipped from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; (by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transitionContext.cancelInteractiveTransition()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transitionContext.completeTransition(false)&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This set-up effectively keeps your interaction “in transition” for as long as the user is playing with the dragged view — if the user lets go, but then grabs the view again as it’s animating back to its starting position, the interaction remains in transition throughout. This gives us the freedom to maintain the dragged view’s interactivity without having to worry about reporting the start and cancellation of the transition to UIKit over and over again (which can cause weird behaviour and crashes). How exactly you implement this pattern depends a lot on how your interaction controller is structured, so I’ll leave the details up to you!&lt;/p&gt;

&lt;h3 id=&quot;deal-with-scroll-views&quot;&gt;Deal with scroll views&lt;/h3&gt;

&lt;p&gt;If you establish a swipe-down-to-dismiss pattern in your app, you want to make sure you don’t end up with cases where the user expects this pattern to hold, but it doesn’t. A common stumbling block arises when your presented view — the one you want to dismiss with a swipe — is entirely or largely made up of a vertical scroll view. (Remember, table views and collection views are scroll views too.) If the user swipes down on the scroll view, the scroll view consumes the gesture, effectively preventing the user from swiping to dismiss.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/indie-5-2-eg5.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;This app is bad!&lt;/p&gt;

&lt;p&gt;Of course, being able to scroll is kind of important too. So how do we reconcile these two conflicting gestures in a way that feels natural to the user? I think a nice solution is to simply decide which gesture handler to trigger based on whether or not the scroll view is scrolled to the top. In other words: if the scroll view’s vertical content offset is 0 or less, the swipe should be handled by the dismissal gesture; otherwise, it should be handled by the scroll view.&lt;/p&gt;

&lt;p&gt;If you read &lt;a href=&quot;https://danielgauthier.me/2020/02/24/indie5-1.html&quot;&gt;my last post&lt;/a&gt;, you’ll know that a big priority of mine in building these transitions was to make sure it was easy to use them to present any view controller, from anywhere. So, from our interaction controller, how can we manipulate the gestures of a scroll view that may or may not exist inside a view controller that could be of any type?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protocols!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In that last post, in my quest for reusability, I introduced a lightweight protocol that any view controller would have to conform to if it wanted to be presented in a custom way. Let’s add to this protocol:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalHandlingScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalHandlingScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve added a read-only property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dismissalHandlingScrollView&lt;/code&gt;, and we’ve also used a protocol extension to give it a default nil value. This means that view controllers that conform to this protocol don’t &lt;em&gt;have&lt;/em&gt; to specify a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dismissalHandlingScrollView&lt;/code&gt; if they don’t have one, but those that do can specify one as follows:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilterSelectionViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalHandlingScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that default protocol values have their drawbacks — for example, it means that when you’re conforming to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt;, it’s not immediately obvious that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dismissalHandlingScrollView&lt;/code&gt; is a property you can or should implement — but I like that it minimizes the overhead of creating new custom presentable view controllers.&lt;/p&gt;

&lt;p&gt;Since our interaction controller from last post was already initialized with a view controller that conforms to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt;, we now have access to any scroll view that might interfere with our dismissal gesture, and can handle it appropriately:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StandardInteractionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;prepareGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scrollView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dismissalHandlingScrollView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;resolveScrollViewGestures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prepareGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalGestureRecognizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPanGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;#selector(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handleGesture(_:)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dismissalGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;resolveScrollViewGestures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIScrollView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissalGestureRecognizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPanGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;#selector(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handleGesture(_:)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dismissalGestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;scrollView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dismissalGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scrollView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panGestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;toFail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dismissalGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StandardInteractionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIGestureRecognizerDelegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureRecognizerShouldBegin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scrollView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dismissalHandlingScrollView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scrollView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contentOffset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, resolving the scroll and dismissal gestures in our interaction controller is pretty simple. On initialization, we check to see if the view controller being presented has a dismissal handling scroll view. If so, we add a dismissal gesture recognizer to the scroll view, and specify that the scroll view’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;panGestureRecognizer&lt;/code&gt; — the one that actually handles scrolling — should only be recognized if the dismissal gesture recognizer fails first. Finally, via conformance to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIGestureRecognizerDelegate&lt;/code&gt;, we set that dismissal gesture recognizer to fail if the scroll view isn’t currently scrolled to the top.&lt;/p&gt;

&lt;p&gt;That’s all for now, folks. Using a few simple techniques, we now have an interactive transition that is not only highly reusable, but also feels pretty great to interact with. Hopefully a few of these come in handy the next time you’re duelling with interactive transitions! 🤺&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a working example of the techniques described here and in the other two articles in this series on custom view controller transitions, check out &lt;a href=&quot;https://github.com/danielmgauthier/ViewControllerTransitionExample&quot;&gt;this repo on GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thanks so much for reading my weird little blog. This is a relatively quick look at a pretty tricky topic, so if you need some help or have any follow-up questions, Let me know! Find me on Twitter &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here&lt;/a&gt; 🦆&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Mastering view controller transitions, part 1: Make them reusable</title>
        <link>/2020/02/24/vctransitions1.html</link>
        <guid isPermaLink="true">/2020/02/24/vctransitions1.html</guid>
        <description>&lt;p&gt;Last week, I wrote a primer on how custom view controller transitions actually work. If you missed it and are relatively new to this stuff, or want a quick refresher, you might want to check it out &lt;a href=&quot;https://danielgauthier.me/2020/02/19/indie-4.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This week, I’m doing a set of 3 short articles that explore some more advanced, concrete ideas around custom transitions. This was originally intended to be one relatively straightforward article, but apparently I’ve got a lot to say about this stuff (or maybe I’m bad at editing 🤔), and I decided that putting out a monolithic 8000 word piece probably wasn’t the most palatable choice. So, each article will revolve around one goal I had in mind as I built some nice custom transitions into my own app, and will detail the solutions I came up with in trying to achieve that goal.&lt;/p&gt;

&lt;p&gt;To get us all on the same page, let’s take a look at a couple of these transitions in action. You’ll see two different view controllers presented: the “Add game” view controller, and the “Tags” view controller. Both are presented using the same animation and presentation controllers, but they use different interaction controllers on dismissal — “Tags” can be quickly and easily swiped away, while “Add game” is a more deliberate interaction that requires the user to drag a certain distance before releasing, and that includes some decoration views as part of the interaction.&lt;/p&gt;

&lt;p class=&quot;screenshot&quot;&gt;
    &lt;video controls=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;/assets/img/game-demo.mov&quot; type=&quot;video/mp4&quot; /&gt;
    Your browser does not support the video tag.
    &lt;/video&gt;
&lt;/p&gt;

&lt;p&gt;There are some cool things going on behind the scenes here that all add up to what I think is a pretty nifty user experience. Let’s talk about goal number one!&lt;/p&gt;

&lt;h2 id=&quot;goal-1-i-want-a-dead-simple-way-to-use-my-custom-transitions&quot;&gt;Goal 1: I want a dead-simple way to use my custom transitions&lt;/h2&gt;
&lt;p&gt;This first goal actually isn’t directly relevant to the end-user experience, but is VERY relevant to me, the developer, who doesn’t want a total mess on his hands as he starts using his custom transitions in different contexts throughout the app. And, let’s be real: all technical debt seeps into the user experience eventually! So, let’s figure out how to make sure our custom transitions are easy to use and reuse.&lt;/p&gt;

&lt;p&gt;As I mentioned &lt;a href=&quot;https://danielgauthier.me/2020/02/19/indie-4.html&quot;&gt;last week&lt;/a&gt;, the “transitioning delegate” responsibility often falls on the presenting view controller by default. In the most naïve case, this means that every time you want to present something using a custom transition, you need to implement a bunch of delegate methods in your presenting VC, giving it responsibilities it probably shouldn’t really have. Furthermore, if you want to support an interactive dismissal gesture, you might end up implementing a gesture handler inside your presented view controller — its view &lt;em&gt;is&lt;/em&gt; where the gesture is happening, after all — and then figure out some way to hook up your gesture handling code to the interaction controller that your presenting view controller owns.&lt;/p&gt;

&lt;p&gt;There may be some hypothetical cases where this approach is passable — namely, if you know you’re building a very specific custom transition that’s only ever going to be used in one place. In my case, however, I knew I wanted to be able to present any view controller, from anywhere, using my custom transitions. In other words, I didn’t want to have to keep reimplementing gesture handlers and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIViewControllerTransitioningDelegate&lt;/code&gt; methods every time I wanted to use my transitions.&lt;/p&gt;

&lt;p&gt;My solution starts with the idea that we should have a dedicated class to serve as the transitioning delegate for our custom modal presentations — we’ll call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt;. As the transitioning delegate, it’s simply responsible for vending presentation, animation and interaction controllers when asked. It might look something like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalTransitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalTransitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forPresented&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presented&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPresentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalPresentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;presentedViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presented&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;animationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forPresented&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presented&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerAnimatedTransitioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalTransitionAnimator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;animationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forDismissed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dismissed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerAnimatedTransitioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalTransitionAnimator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;presenting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;interactionControllerForDismissal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerAnimatedTransitioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerInteractiveTransitioning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactionController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interactionInProgress&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only tricky thing to notice here is the way the interaction controller is handled. Since UIKit uses the return value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactionControllerForDismissal(using:)&lt;/code&gt; as an indication of whether or not the dismissal should be performed interactively, our modal transition manager can’t simply create and return a new interaction controller here like it does with the animation and presentation controllers; if it did, UIKit would always assume a dismissal was happening interactively, and wait for interactive updates, even if the user tapped a button to dismiss the view non-interactively. This wouldn’t be an issue if the &lt;strong&gt;only&lt;/strong&gt; way to dismiss our presented view controller was via an interaction, but in our case (and probably in most cases), it’s important that our users can dismiss with a gesture &lt;em&gt;or&lt;/em&gt; with the tap of a button.&lt;/p&gt;

&lt;p&gt;So, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt; keeps a reference to an optional interaction controller, and the interaction controller itself knows if a dismissal has been triggered by a gesture via its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactionInProgress&lt;/code&gt; property (more on that in a sec). In this way, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt; has the information it needs to either return an interaction controller, telling UIKit “yep, let’s do an interactive dismissal,” or return nil to say “no, just animate this dismissal non-interactively.”&lt;/p&gt;

&lt;p&gt;Of course, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt; needs to be owned by someone from the beginning of presentation until the end of dismissal so that it can communicate with UIKit as needed. To make sure of this, let’s write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomPresentable&lt;/code&gt; protocol that every custom-presented view controller will conform to, and that ensures that the presented view controller keeps a strong reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this in place, we’ve taken the transitioning delegate responsibility out of our view controllers, and moved it into a separate object that the presented view controller keeps a reference to, but otherwise doesn’t care about.&lt;/p&gt;

&lt;p&gt;The second thing to address is implementing a gesture handler on our presented view that can control the dismissal interaction. Instead of doing it inside the presented view controller, we can move this responsibility into the interaction controller itself. First, we create a simple protocol that inherits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIViewControllerInteractiveTransitioning&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerInteractiveTransitioning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactionInProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This enforces that any interaction controller we build must be able to report when an interaction has started; we used this property in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt; above.&lt;/p&gt;

&lt;p&gt;Now, we can build an interaction controller that’s responsible for handling gestures, and therefore knows when to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactionInProgress&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StandardInteractionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactionInProgress&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;prepareGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prepareGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gesture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPanGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;#selector(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handleGesture(_:)&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gesture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;@objc&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleGesture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPanGestureRecognizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;superview&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;superview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gestureRecognizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;began&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureBegan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interruptedTranslation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cancelled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureCancelled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interruptedTranslation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ended&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureEnded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;translation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interruptedTranslation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;velocity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gestureBegan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interactionInProgress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;interactionInProgress&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, there’s a lot more than that going on in the full implementation, but this gives you the idea: our interaction controller is initialized with the presented view controller, and is responsible for installing and responding to the gesture recognizer, allowing us to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactionInProgress&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; before having UIKit kick off the dismissal, and making it trivial to get subsequent gesture updates through our interaction controller to UIKit.&lt;/p&gt;

&lt;p&gt;Finally, this is all tied together with a simple UIViewController extension:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractiveDismissalType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;none&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                 &lt;span class=&quot;nv&quot;&gt;interactiveDismissalType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractiveDismissalType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                 &lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InteractionControlling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactiveDismissalType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StandardInteractionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;InputInteractionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ModalTransitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interactionController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transitionManager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transitionManager&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transitioningDelegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transitionManager&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modalPresentationStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s where we actually kick off the custom presentation. The caller can specify the type of interactive dismissal, which dictates which interaction controller is created. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ModalTransitionManager&lt;/code&gt; is created and assigned to the view controller we want to present — we need to hold onto it strongly in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transitionManager&lt;/code&gt; property, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transitioningDelegate&lt;/code&gt; is a weak reference — and then we present the view controller.&lt;/p&gt;

&lt;p&gt;What does this all mean? It means that we can now present any view controller, from anywhere, and get all our desired animation and interaction behaviour, by simply making sure our presented view controller conforms to CustomPresentable:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CoolViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomPresentable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transitionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewControllerTransitioningDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and then calling our presentation method:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;coolViewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CoolViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coolViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactiveDismissalType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and everything else works like magic. That’s a whole lot of complexity we no longer have to think about. Pretty cool 😎&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a working example of the techniques described here and in the other two articles in this series on custom view controller transitions, check out &lt;a href=&quot;https://github.com/danielmgauthier/ViewControllerTransitionExample&quot;&gt;this repo on GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! This is a relatively quick look at a pretty tricky topic, so if you need some help or have any follow-up questions, Let me know! Find me on Twitter &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here&lt;/a&gt; 🐦&lt;/p&gt;
</description>
        <pubDate>Mon, 24 Feb 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 4: Wrap your head around custom view controller transitions</title>
        <link>/2020/02/19/indie-4.html</link>
        <guid isPermaLink="true">/2020/02/19/indie-4.html</guid>
        <description>&lt;p&gt;In the first few steps of this series, we’ve talked a bit about the idea that the success of any app — and especially a small indie project — often hinges on the quality of execution. An app that is thoughtfully designed and makes obvious the amount of care and polish that went into building it likely stands a much better chance of capturing some real interest than one that feels same-y or slapped together.&lt;/p&gt;

&lt;p&gt;This week, I want to start looking at an aspect of UIKit development that plays a huge role in creating that feeling of polish and care, but that, in the past, I’ve found to be a bit of a pain to work with: &lt;strong&gt;custom view controller transitions!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-4.png&quot; alt=&quot;Overwhelmed by view controller transition diagram&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;So much fun!&lt;/p&gt;

&lt;p&gt;In building my definitely-soon-to-be-critically-acclaimed indie app, a big goal of mine has been to try not to compromise on design ideas because of complications or inconveniences in implementation. One place UIKit makes it really tempting to compromise is in presenting and dismissing view controllers. Do you really want to have to figure out what makes subclassing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt; different from implementing your own class conforming to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIViewControllerInteractiveTransitioning&lt;/code&gt;…or would you rather just call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;present(_:animated:)&lt;/code&gt; and move onto the fun stuff?&lt;/p&gt;

&lt;p&gt;Well, in the name of lofty design ideals, I decided that custom view controller transitions ARE the “fun stuff”, damn it, and I hunkered down and endeavoured to truly, totally, &lt;em&gt;finally&lt;/em&gt; wrap my head around this whole clustercuss of classes and protocols. Although I’m not sure I’ll ever feel like I’ve quite reached the summit of this treacherous climb…I’ve built some cool stuff and learned a bunch along the way. Let’s get into it!&lt;/p&gt;

&lt;h2 id=&quot;what-this-is-and-what-it-is-not&quot;&gt;What this is, and what it is not&lt;/h2&gt;
&lt;p&gt;This is really just meant to be a primer on the roles of each of the entities involved in a custom view controller transition — there’s a lot to keep track of, and in trying to master this stuff, I personally found it really helpful to take the time to step back and fundamentally understand what each of the relevant classes and protocols are responsible for before jumping into building anything. This isn’t a step-by-step tutorial with code examples — there are plenty of those out there — but rather, an attempt to explain the basic building blocks of custom view controller transitions in a way that’s useful, easy to digest and quick to refer to when things start to get blurry. Basically, it’s what I wrote for myself to make sure I fully understood what I was doing, and wasn’t depending on anything that felt like magic.&lt;/p&gt;

&lt;p class=&quot;notes&quot;&gt;&lt;strong&gt;Note from the future&lt;/strong&gt;: I’ve written a series of 3 articles on some more concrete, advanced topics having to do with custom view controller transitions. If this article is a bit too simple for you, feel free to jump ahead! &lt;a href=&quot;https://danielgauthier.me/2020/02/24/indie-5-1.html&quot;&gt;Here’s the first one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before getting started, I have one more important thing to say:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the documentation! It’s quite good!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because custom transitions can feel pretty overwhelmingly complicated, and the docs are admittedly a bit dense, it can be tempting to skip them and just go hunting for a tutorial that’s going to hand you all the code you need. What I’ve found is that a lot of the tutorials out there do things quite differently from one another, and they often struggle to explain why exactly they’re doing things in a particular way, or how their implementation might be tweaked or extended to do different or more complex things. Apple’s own documentation might not give you a bunch of code you can paste directly into your project, but it will help you understand what the cuss is actually going on.&lt;/p&gt;

&lt;p&gt;So, consider this to be a concise companion guide to Apple’s helpful but heavy documentation. It’s not a replacement, and neither is any tutorial you find on the internet. Actually, that’s true of pretty much any development topic, but I digress. Onwards!&lt;/p&gt;

&lt;h2 id=&quot;the-anatomy-of-a-custom-view-controller-transition&quot;&gt;The anatomy of a custom view controller transition&lt;/h2&gt;

&lt;p class=&quot;notes&quot;&gt;&lt;strong&gt;Note&lt;/strong&gt;: I’m dealing specifically with custom &lt;em&gt;modal&lt;/em&gt; transitions here; a lot of this is also applicable to customizing navigation controller transitions, for example, but that’s not really within the scope of this article.&lt;/p&gt;

&lt;h3 id=&quot;presenting-view-controller--presented-view-controller&quot;&gt;Presenting view controller &amp;amp; presented view controller&lt;/h3&gt;

&lt;p&gt;This terminology isn’t unique to custom transitions, but it is important to get right, so let’s make sure we’re on the same page. Imagine you have a HomeViewController, and from there, you present a WarningViewController. HomeViewController is the &lt;strong&gt;presenting&lt;/strong&gt; VC, and WarningViewController is the &lt;strong&gt;presented&lt;/strong&gt; VC. Easy peasy.&lt;/p&gt;

&lt;h3 id=&quot;container-view&quot;&gt;Container view&lt;/h3&gt;

&lt;p&gt;In the context of a custom transition, the &lt;strong&gt;container view&lt;/strong&gt; is a view that’s created and managed by UIKit, and that contains both the presenting and presented views during the transition. If your transition includes any custom views that aren’t part of the presenting or presented views, you’ll add them to the container view as well.&lt;/p&gt;

&lt;h3 id=&quot;uiviewcontrolleranimatedtransitioning&quot;&gt;UIViewControllerAnimatedTransitioning&lt;/h3&gt;

&lt;p&gt;Your implementation of this protocol is known as an &lt;strong&gt;animation controller&lt;/strong&gt;, and is responsible for actually animating the transition between view controllers. It has access to both the presenting and presented view controllers, which allows it to animate their view frames into place, or to manipulate other properties of the view controllers and their views. It also has access to the transition’s container view if needed. You could implement two animation controllers to handle presentation and dismissal separately, but it often makes sense to have a single animation controller that covers both presentation and dismissal animations, and that knows which direction to perform the animation in based on a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isPresenting&lt;/code&gt; property.&lt;/p&gt;

&lt;h3 id=&quot;uiviewcontrollerinteractivetransitioning&quot;&gt;UIViewControllerInteractiveTransitioning&lt;/h3&gt;

&lt;p&gt;A concrete implementation of this protocol is known as an &lt;strong&gt;interaction controller&lt;/strong&gt;. An interaction controller is only necessary if you want to allow your transition to be driven by some sort of gesture. A simple example of this is a case where you want to be able to dismiss your presented view controller with a swipe, and have the view’s movement correspond with your finger’s movement. UIKit provides a specific implementation of this protocol — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt; — that, given continuous updates about the gesture as it’s performed, can automatically use your existing animation controller to figure out where your animated views should be throughout the gesture. You can also implement your own interaction controller to get more fine-grained control over the interactive transition. Either way, an interaction controller needs to be fed continuous updates on the state of the gesture, and is responsible for translating these into visual updates to the involved views. Like animation controllers, interaction controllers have access to both the presenting and presented view controllers, as well as the transition’s container view, giving you precise control over how the interaction impacts the various views on screen.&lt;/p&gt;

&lt;h3 id=&quot;uipresentationcontroller&quot;&gt;UIPresentationController&lt;/h3&gt;

&lt;p&gt;Every view controller transition — custom or standard — is driven by a &lt;strong&gt;presentation controller&lt;/strong&gt; that’s responsible for managing the whole thing, from the start of presentation to the end of dismissal. A presentation controller has direct access to the presenting and presented view controllers and the transition’s container view, and knows about when presentations and dismissals begin and end. Although a &lt;em&gt;custom&lt;/em&gt; presentation controller isn’t necessarily required to build simple custom view controller transitions, it’s typically needed when you want to add decoration views to the container view during transition (e.g. a dimming view: darkening the screen behind some sort of popup), or when you want some dynamic control over the size and positioning of the presented view (e.g. responding to changes in content or screen size).&lt;/p&gt;

&lt;h3 id=&quot;uiviewcontrollercontexttransitioning&quot;&gt;UIViewControllerContextTransitioning&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;transition context&lt;/strong&gt; provides contextual information about the transition as it occurs. Remember how I said that both animation controllers and interaction controllers have access to the presenting and presented view controllers? This is true thanks to the transition context — both animation and interaction controllers implement methods that are called by UIKit and given a transition context to work with. The transition context provides access to the views and view controllers involved in the transition, along with details about the transition, such as what the frame of the presented view controller should be at the end of the transition, or whether the transition is currently being driven interactively. Interaction controllers also use the transition context to report the interaction’s progress and completion to UIKit.&lt;/p&gt;

&lt;h3 id=&quot;uiviewcontrollertransitioningdelegate&quot;&gt;UIViewControllerTransitioningDelegate&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;transitioning delegate&lt;/strong&gt; is really what ties together everything described above. Your transitioning delegate is responsible for vending the various objects that are needed to perform your custom transition: a custom presentation controller, animation controller(s) and interaction controller(s). In other words, it needs to know how to create or otherwise have access to each of these controllers. In simple examples, you’ll often see the presenting view controller playing the role of transitioning delegate, although creating a dedicated class is often a better approach. With a transitioning delegate in place, we can now instruct UIKit to use our custom transition by simply assigning the transitioning delegate to the VC we want to present, and setting the modal presentation style to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;custom&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;so-how-exactly-does-it-all-fit-together&quot;&gt;So how exactly does it all fit together?&lt;/h2&gt;

&lt;p&gt;It’s tempting to want a single, “correct” answer to this question. How do I know if I’m doing it right? Unfortunately, like pretty much anything in software development, there are lots of different ways to actually piece together your custom transitions, each with their own set of trade-offs. Nonetheless, to provide a bit of clarity, let’s step through some simplified presentation and dismissal cases to start to get a sense for how the various entities behave, and how they communicate with each other.&lt;/p&gt;

&lt;h3 id=&quot;animated-presentation&quot;&gt;Animated presentation&lt;/h3&gt;

&lt;p&gt;We want a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt; to be displayed on-screen using a custom transition when a button is tapped in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HomeViewController&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HomeViewController&lt;/code&gt; conforms to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIViewControllerTransitioningDelegate&lt;/code&gt;, so we can use it as the transitioning delegate for our custom transition.&lt;/p&gt;

&lt;p&gt;Our custom transition begins the moment &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;present(_:animated:)&lt;/code&gt; is called. I’ve written a short, bad play about it, because it’s honestly a much more readable way to describe the flow of information than dryly describing every technical step 🎭&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[Applause, curtains rise]&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Oh hey UIKit, can you help me present this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt;?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Yeah, let’s do it. Okay, says here you’re the transitioning delegate, so…do you have a presentation controller for me?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Yep, here’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningPresentationController&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Perfect, thanks. &lt;em class=&quot;light-text&quot;&gt;[Starts setting things up behind the scenes by creating the container view, transition context, etc.]&lt;/em&gt; Okay, I’m also going to need an animation controller to actually present this thing.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Oh right, here’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningAnimationController&lt;/code&gt; you can use. I’ve made sure it knows to present rather than dismiss.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Good, good. Do you have an interaction controller for me as well?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Nah, let’s just present this one non-interactively.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Sure! Let’s get started then. Hey presentation controller, FYI: we’re about to start the transition.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;FancyWarningPresentationController&lt;/strong&gt;&lt;br /&gt;
Got it. &lt;em class=&quot;light-text&quot;&gt;[Adds a dimming view to the container view.]&lt;/em&gt; Hey UIKit, can you make sure this dimming view fades in at the same time the rest of the presentation animation is happening?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Yep, no problem. Also, where do we want the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt; to end up on screen? The animation controller’s going to want to know.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;FancyWarningPresentationController&lt;/strong&gt;&lt;br /&gt;
Let’s just have it cover the bottom half of the screen.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Works for me. Okay, animation controller? You’re up. Everything you need should be here in this transition context. Let me know when you’re done.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;FancyWarningAnimationController&lt;/strong&gt;&lt;br /&gt;
Roger that. &lt;em class=&quot;light-text&quot;&gt;[Beautifully animates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt; into view.]&lt;/em&gt; And….done.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Nailed it. Thanks everyone!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[End scene]&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;interactive-dismissal&quot;&gt;Interactive dismissal&lt;/h3&gt;

&lt;p&gt;Let’s assume a pan gesture recognizer is set up on our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt;’s view to handle the dismissal interaction. Where the gesture handler is actually implemented isn’t terribly important, as long as it has a direct line to the interaction controller. As soon as the gesture begins, we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dismiss(animated:)&lt;/code&gt;, and thus begins our second short play about an interactive dismissal.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[A clear, crisp fall morning. The old Mac startup sound plays off in the distance.]&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;WarningViewController&lt;/strong&gt;&lt;br /&gt;
Hey, uhh…UIKit, is it? Weird question, but: can you dismiss me?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Yeah, no problem. I see here that your transitioning delegate is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HomeViewController&lt;/code&gt; — oh yeah, I remember helping them present you a while back. I’ll get in touch with them and we’ll have you dismissed in no time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[UIKit picks up the phone and calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HomeViewController&lt;/code&gt;.]&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Hello?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Hey, me again. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt; needs to be dismissed, and since you’re their transitioning delegate, I’ll need a few things from you again.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Oh yeah no problem! You still have that presentation controller I gave you, right?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Yep, I’ve been holding onto that.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;HomeViewController&lt;/strong&gt;&lt;br /&gt;
Okay, here’s another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningAnimationController&lt;/code&gt; for you to use — this one’s set to dismiss. And, I know &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt; only ever dismisses interactively, so you should take this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt; as well — there’s already a gesture handler set up who knows how to use this.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Brilliant! Okay, let’s make it happen. Presentation controller, we’re about to start the transition.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;FancyWarningPresentationController&lt;/strong&gt;&lt;br /&gt;
Sounds good. I’m going to fade out this dimming view — can you make sure the timing lines up nicely with the gesture?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;UIKit&lt;/strong&gt;&lt;br /&gt;
Will do. Now, animation controller, interaction controller, here are your transition contexts. Do your thing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningAnimationController&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt;, and the gesture handler all huddle together. As the pan gesture progresses, the gesture handler reports progress to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIPercentDrivenInteractiveTransition&lt;/code&gt;, who works with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningAnimationController&lt;/code&gt; to figure out what the views involved in the transition should look like at the given progress level. As a team, they continuously update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WarningViewController&lt;/code&gt;’s position to reflect the progress of the dismissal. It’s a sight to behold. Eventually…]&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Gesture handler&lt;/strong&gt;: And……DONE!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em class=&quot;light-text&quot;&gt;[&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FancyWarningAnimationController&lt;/code&gt; completes the dismissal animation and gives UIKit the good news. They all look at each other contentedly. Their work here is done.]&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;/h2&gt;

&lt;p&gt;That may have gotten a bit silly and oversimplified, but honestly, when you’re new to this stuff, it’s already a lot to take in, and I needed to wrap this up. There are a lot of moving parts here, they all interact with each other in less-than-obvious ways, and it can be hard to figure out exactly who should be responsible for what. As someone who is in the midst of building what could very well be my final full-fledged UIKit project, I wanted to make sure I had a really firm understanding of this whole view controller transitioning API, and could properly make use of it to build exactly what I wanted. I hope sharing some of that knowledge in a lighthearted way helped clarify some things for a few folks out there! If, on the other hand, you:&lt;/p&gt;

&lt;p&gt;a) think this whole thing still seems wildly overcomplicated, and&lt;br /&gt;
b) are able and willing to build whatever it is you’re building with a promising, but young and somewhat janky new set of tools…&lt;/p&gt;

&lt;p&gt;SwiftUI might be looking pretty good to you right about now 😏&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! This is a relatively quick look at a pretty big topic, so if you need some help or have any follow-up questions, Let me know! Find me on Twitter &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here&lt;/a&gt; 🐦&lt;/p&gt;
</description>
        <pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 3: Build your core interaction loop</title>
        <link>/2020/02/11/indie-3.html</link>
        <guid isPermaLink="true">/2020/02/11/indie-3.html</guid>
        <description>&lt;p&gt;We’ve got a &lt;a href=&quot;https://danielgauthier.me/2020/01/27/indie-1.html&quot;&gt;great little idea for an app&lt;/a&gt;. Our project is &lt;a href=&quot;https://danielgauthier.me/2020/02/03/indie-2.html&quot;&gt;all set up and ready to go&lt;/a&gt;. Get excited: the time has finally come to start building this thing!&lt;/p&gt;

&lt;p&gt;…So where do we start?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-3.png&quot; alt=&quot;Staring at blank screen all day&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is actually kind of a tricky question. Here are a few options that might spring to mind:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We could just sort of…start from the beginning. We could simply imagine the first thing a user will encounter — account creation? — and build out organically from there.&lt;/li&gt;
  &lt;li&gt;We could start by architecting and building some of the underlying systems we know we’ll need in the app — networking, data persistence, etc — before working our way up to the app’s business logic and UI.&lt;/li&gt;
  &lt;li&gt;We could hold off on writing code, and instead start by diving deep into developing plans and designs describing exactly what the app should do and how it’s all going to fit together.&lt;/li&gt;
  &lt;li&gt;We could go full TDD and start by writing tests for the app’s core business logic! 🧐&lt;/li&gt;
  &lt;li&gt;We could start by building a quick and ugly prototype of our basic app idea, allowing us to test it out early and evaluate what might or might not work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s probably some sort of argument to be made in favour of each of these — some stronger than others! However, none of the above feel quite right to me. When starting something new, I have three priorities in mind:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Validate&lt;/strong&gt;. If there’s some reason this app idea &lt;em&gt;isn’t&lt;/em&gt; going to pan out in the way I expect it to, I want to know as soon as possible. That means I want to spend most of my time initially exploring what’s essential about that idea and proving that it works, rather than on the supporting features I’ll eventually build around it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid wasted effort&lt;/strong&gt;. I’ve got other important work on the go, and I’m getting paid exactly $0/hr to noodle away on my indie project, so I personally don’t have much of an appetite for work that I’ll ultimately throw away. As much as possible, I want the work I do from the outset to be high-quality and potentially shippable. It also means I want to avoid over-engineering solutions to problems I may not yet fully understand in the context of this particular app.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build momentum&lt;/strong&gt;. On day one, I want to be working on the stuff that really excites me. Keeping momentum up throughout an app development cycle is tough — I’m sure lots of us have folders full of half-baked, abandoned ideas we were once really jazzed about. By starting off doing work I know I love, I’ll feel creatively energized and make progress quickly. With any luck, this will lead me to new ideas I’m excited to explore and help keep the ball rolling in the future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Guided by these priorities, I’ve taken a very particular approach to writing my first few hundred lines of code. While I don’t think there’s anything outlandish about it, it is a little bit at odds with some of the more typical approaches to building products I’ve taken with teams in the past. Regardless, it’s worked for me, and it might serve as a good starting point for you too. Let’s break it down into three parts.&lt;/p&gt;

&lt;h2 id=&quot;part-1-identify-your-core-interaction-loop&quot;&gt;Part 1: Identify your core interaction loop&lt;/h2&gt;
&lt;p&gt;If you google “core interaction loop”, you’ll see…nothing, really, because it turns out I may have accidentally made it up. Let’s talk about it anyways!&lt;/p&gt;

&lt;p&gt;In game design [&lt;em&gt;disclaimer: I am not actually a game designer and have no idea what I’m talking about&lt;/em&gt;], there’s this concept of a &lt;em&gt;core gameplay loop&lt;/em&gt;: it basically describes the primary system or mechanic that players will interact with repeatedly, and that the rest of the game is built around. It’s a bit of a loosey-goosey term, but one way to understand it is as the thing that you really have to “get right” to keep people interested — if the core loop isn’t fun and engaging, no amount of world-building or tacked-on subsystems will make the game enjoyable.&lt;/p&gt;

&lt;p&gt;When I say “core interaction loop,” I guess I’m just adapting this idea to the broader world of app development. Your core interaction loop is the fundamental thing that users will be coming into your app to do the majority of the time. It’s the basic, repetitive use case that props up the rest of the app, and that you know you need to really nail if you want your users to stick around.&lt;/p&gt;

&lt;p&gt;As a dead-simple example, consider a weather app. Weather apps tend to have a handful of features outside of the core interaction loop: tracking multiple locations, sharing forecasts, advanced radar maps, etc. But unquestionably, the thing that any decent weather app has to get right is providing a simple answer to the question: “What’s the weather like today?”&lt;/p&gt;

&lt;p&gt;Of course, not every app has such a clear and concise use case to zero in on. But, if you feel your app has a few candidates, consider which of them users will likely be performing most often, and which of them is closest to the heart of what you think makes your app unique and valuable. If you’re building a to-do list app, and you’ve invented some wacky, never-before-seen way to add new items to a list, consider that to be your core interaction loop.&lt;/p&gt;

&lt;h2 id=&quot;part-2-identify-whats-going-to-make-it-great&quot;&gt;Part 2: Identify what’s going to make it great&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://danielgauthier.me/2020/01/27/indie-1.html&quot;&gt;I’ve mentioned it before&lt;/a&gt;, but in case you missed it: there are a whole lot of apps on the App Store. Whatever your brilliant app idea is, how you execute it is going to play a huge role in whether you’re able to stand out from the crowd. And, nowhere is that execution going to be more impactful than within your core interaction loop.&lt;/p&gt;

&lt;p&gt;So, what is it about your particular take on this interaction that’s going to make it a great experience for your users? Are there ways in which you want to allow your users to accomplish some task and get back to real life as quickly as possible? Do you have ideas on how to make a relatively complicated interaction surprisingly intuitive? Are you focused on crafting a delightful experience that makes your users smile? Figure out what your priorities are, and start thinking about ways you might design and implement your core interaction loop to reflect these priorities.&lt;/p&gt;

&lt;h2 id=&quot;part-3-build-it-and-then-polish-it-and-then-tweak-it-and-polish-it-some-more&quot;&gt;Part 3: Build it, and then polish it, and then tweak it and polish it some more&lt;/h2&gt;
&lt;p&gt;There’s this idea I hear bouncing around a lot that goes something like this: if the basic idea of your product is compelling and worth building, users will use it &lt;em&gt;despite&lt;/em&gt; any flaws or broken pieces or ugliness. A similar approach sometimes comes out of “agile” development processes, especially in teams that tend to be more business-driven: “Let’s get to a basic MVP as quickly as possible, ship it, get feedback and iterate.” This suggests that maybe our goal from the outset should be to build the simplest possible version of our app, get it into people’s hands as soon as we can, and save the polish for later.&lt;/p&gt;

&lt;p&gt;There’s definitely some wisdom in that kind of approach, and I’m not really willing to argue with it directly. However, I’ll say a couple things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Call me an artsy idealist, but I’m personally always a bit bummed about building and shipping minimum viable products that, by definition, are incomplete, include disappointing omissions and are just…kind of hard to love.&lt;/li&gt;
  &lt;li&gt;Often, especially in smaller indie projects, what makes a product compelling and worth building actually isn’t the idea at all, but the impressive attention to detail, the personal touches, or the singular design. Rushing to build basic functionality might lead you to lose sight of the stuff that’s actually going to set you apart and attract users in the first place.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, now that you’re &lt;em&gt;finally&lt;/em&gt; ready to sit down and start creating your &lt;em&gt;chef d’oeuvre&lt;/em&gt;, here’s my arguably unorthodox suggestion for what to do first: design and build the core interaction loop we’ve identified — that small but oh-so-crucial sliver of your app — and polish the cuss out of it. Build something that checks all the boxes you identified in part 2. Build something that looks and feels great, and that you’d be thrilled to release into the world tomorrow morning if the rest of the app magically appeared overnight. &lt;strong&gt;Build something you love!&lt;/strong&gt; In my experience, the indie iOS community truly values the most thoughtful, polished and well-crafted apps; why not strive for just that from the get-go?&lt;/p&gt;

&lt;p&gt;It’ll take time, and that’s okay. If you’re anything like me, you’ll be energized by diving headfirst into the heart of your app and seeing something special come to life early on. And crucially, by nature of this being a central piece of your app, you’ll come up with &lt;em&gt;tons&lt;/em&gt; of peripheral ideas along the way as you gain clarity on how the rest of the app is going to fit into what you’re building.&lt;/p&gt;

&lt;p&gt;There will be plenty of time to iterate and make changes later — what you build at this early stage almost definitely won’t ship exactly as-is. But with the care and effort you put into it right off the bat, much of what you build here should stick around over time and help define the structure and design of the rest of the app. In the meantime, what you’re building now acts as a high-fidelity prototype that should give you some real insight into how your basic idea feels to use, and help you decide whether you want to keep investing time and effort into it.&lt;/p&gt;

&lt;p class=&quot;notes&quot;&gt;Note: something I’ve left open-ended here is the question of whether you should be building a complete vertical feature, i.e. whether, where applicable, you should be implementing networking or data persistence layers to support the core interaction loop you’re building. I’ll leave it open-ended for now — I think it depends somewhat on what exactly you’re building, and what you think is important or valuable about it — but I will say that in my particular case, I’ve stubbed out those lower-level layers, because they’re ultimately meant to be largely invisible to the user during the core interaction loop anyway. My suggestion: focus on user experience, and keep the scope of your implementation as narrow as you can.&lt;/p&gt;

&lt;p&gt;So go make yourself a coffee, clean up your desk, put on some tunes and start building your dang thing. Get lost in it for a little while, have some fun, and build yourself a small but essential slice of app that’ll get you excited to build more. There’s no better feeling than seeing creative work you’re proud of come to life!&lt;/p&gt;

&lt;p&gt;✨🧑‍💻✨&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;If you: a) think this article is full of great ideas, or b) think this article is full of terrible ideas, I’d love to hear from you! Find me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; if you have any questions or thoughts 🐦&lt;/p&gt;
</description>
        <pubDate>Tue, 11 Feb 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 2: Act like a chef</title>
        <link>/2020/02/03/indie-2.html</link>
        <guid isPermaLink="true">/2020/02/03/indie-2.html</guid>
        <description>&lt;p&gt;Last week, we figured out &lt;a href=&quot;https://danielgauthier.me/2020/01/27/indie-1.html&quot;&gt;what it is we actually want to build&lt;/a&gt;. Now, armed with a killer idea, we’ve got one more step to take before we can really start thwacking away at the keyboard and creating our indie masterpiece.&lt;/p&gt;

&lt;p&gt;There’s a great concept in cooking called &lt;em&gt;mise en place&lt;/em&gt;, which basically says that it’s crucial to have all the required tools and ingredients neatly laid out in your workspace &lt;em&gt;before&lt;/em&gt; you start making any food. By keeping things clean and decluttered, and by ensuring that everything you need is within arm’s reach, you give yourself the mental space to focus on the creative and skillful bits of the job and avoid dumb mistakes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-2.png&quot; alt=&quot;Mise en place&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I love this concept. It makes cooking a whole lot more pleasant, and it can be usefully applied to a bunch of things that have nothing to do with cooking. Think of the difference between a clean, decluttered desk on top of which everything you need is neatly arranged, and one that’s full of old junk, where you can’t find the notebook you need, the desk lamp is flickering and &lt;a href=&quot;https://twitter.com/danielmgauthier/status/1220721390812368896?s=20&quot;&gt;your mouse’s battery level is at 3%&lt;/a&gt;. Which are you going to do better work at?&lt;/p&gt;

&lt;p&gt;This post isn’t about cleaning up your desk. (Although, hey, never a bad idea.) Rather, I think there’s a simple but useful way to borrow this idea from the world of French cuisine and apply it to a brand new Xcode project.&lt;/p&gt;

&lt;p&gt;Creating a new project is exciting as heck. Think of the unexplored possibilities! The boundless potential! Zero technical debt! It’s tempting to jump right in and starting pounding out what is, surely, the best code you’ve ever written. But if you ever want to become the Michelin star chef of indie app development you’ve always dreamed of becoming…consider first devoting a few minutes to your &lt;em&gt;mise en place&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Of course, building an app is nothing like following a recipe. Unlike most cooks, we rarely know every tool and ingredient we’re going to need ahead of time. Nonetheless, putting some thought into setting up a project that feels tidy and includes a few go-to tools can help get the mundane and annoying parts of app development out of the way, and allow you to focus on doing your best creative work right off the bat.&lt;/p&gt;

&lt;p&gt;What exactly your setup should look like is entirely up to you, and depends on what makes you feel comfortable and efficient. But, as an example, here are a few simple things I do with any new project I start to help me hit the ground running.&lt;/p&gt;

&lt;h3 id=&quot;tweak-the-template&quot;&gt;Tweak the template&lt;/h3&gt;
&lt;p&gt;When you create a new project, the files that are created automatically are full of comments. They’re helpful if you’re new to this stuff; otherwise, they just take up a bunch of space. Delete! I’m also not a huge fan of storyboards — not a value judgment, just a personal preference when I’m working solo — so I typically get rid of the storyboard file, and instead get my view controller hooked up by adding it to the window in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SceneDelegate&lt;/code&gt;. You do you.&lt;/p&gt;

&lt;h3 id=&quot;add-swiftlint&quot;&gt;Add SwiftLint&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint&quot;&gt;SwiftLint&lt;/a&gt; is a simple but indispensable tool for maintaining good habits when it comes to code style and convention. It’ll encourage you to keep your code clean and consistent, which I think is a crucial thing to strive for from the very first line of code you write — getting into bad habits early on will always come back to bite you down the road, even if the code is all yours. That said, it’s also important to be a bit pragmatic about style guides, so don’t be afraid to use SwiftLint’s many configuration options to fit it to your needs. I’ve got my own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; file that I use as a starting point for any new project.&lt;/p&gt;

&lt;h3 id=&quot;set-up-third-party-libraries&quot;&gt;Set up third-party libraries&lt;/h3&gt;
&lt;p&gt;I’ll start this section with a caution: there are a lot of good reasons not to overdo it with third party dependencies, and I’d be especially careful not to prematurely add libraries you think &lt;em&gt;might&lt;/em&gt; come in handy. That said, maybe there’s a simple library that you love using and that‘s generally useful to you in all your projects; if so, now’s the time to get it set up. At the moment, for me, that library is &lt;a href=&quot;https://github.com/robb/Cartography&quot;&gt;Cartography&lt;/a&gt;: a beautiful little DSL for setting up Auto Layout constraints programmatically. A lot of fun people have a lot of fun opinions on whether Auto Layout is bad, whether it belongs in code or in Interface Builder, and whether using a 3rd party library for constraints is misguided. Personally, I love Auto Layout, I’ve been building good chunks of my UI programmatically of late, and I’m a big fan of Cartography’s simple declarative syntax. In my own personal projects, it’s a no-brainer. Your mileage may vary; use the tools that make you feel comfortable and productive. (And, uh, maybe best saved for another discussion, but do try to make sure your dependencies are actively maintained, and that their discontinued existence won’t submarine your app.)&lt;/p&gt;

&lt;p class=&quot;notes&quot;&gt;Note: For the first time ever, I’m using &lt;a href=&quot;https://swift.org/package-manager/&quot;&gt;Swift Package Manager&lt;/a&gt; to manage dependencies instead of CocoaPods. And speaking of &lt;em&gt;mise en place&lt;/em&gt;: as much as CocoaPods has been a fantastic tool for many years, I must say it’s a joy to be rid of some of the clutter that CocoaPods introduces into an Xcode project. Worth checking out if you haven’t already 👀&lt;/p&gt;

&lt;h3 id=&quot;add-any-personal-tools&quot;&gt;Add any personal tools&lt;/h3&gt;
&lt;p&gt;I also have a few of my own bits of code — mostly simple UIKit and Foundation extensions — that I tend to import into any new project I start. These are largely straightforward things like factory methods for creating commonly used UIKit objects, methods for applying good-looking shadows to views, shortcuts for adding and removing child view controllers to containers, etc. It’s nice to build up your own personal library of these sorts of tools and helpers that you can then modularize and easily take with you into any new project you start. If this isn’t something you’re already doing, consider this the next time you find yourself extending functionality, creating a new type or solving some sort of general problem: might some version of this piece of code be useful in a future project? If so, it might be worth spending a few extra minutes polishing it up for general use and popping it out into your own personal toolkit.&lt;/p&gt;

&lt;h3 id=&quot;get-git-going&quot;&gt;Get git going&lt;/h3&gt;
&lt;p&gt;Finally, once all this stuff is set up and ready to go, it’s time to commit your changes and push them to Github (or wherever you happen to be hosting your repo). Make sure you’ve got a good &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; file (&lt;a href=&quot;https://github.com/github/gitignore/blob/master/Swift.gitignore&quot;&gt;here’s a good template&lt;/a&gt;) to avoid pushing unnecessary junk. Write the perfect commit message, and revel in the satisfaction of the first of many squeaky clean commits!&lt;/p&gt;

&lt;p&gt;Admittedly, everything I listed above is, at least for more experienced folks, pretty straightforward. And to be clear, the point here isn’t that adding SwiftLint three commits in makes you tangibly worse off than adding it right away. The point is simply that on the whole, I think there’s real value in spending a bit of time up front thinking about what you’re building, and setting up your environment in a way that neatly accommodates the work you’re about to do. It’s only going to get harder to keep things tidy and intentional as your project grows in complexity; might as well start off on the right foot 🏃&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;That’s step 2 in the books. Isn’t this easy? We’ll have our indie hit shipped in no time. I’d love to hear your thoughts on this one: got any tools or tips for starting up a brand new project? Do you think any of this even matters? Find me on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; if you have any questions or thoughts 🐦&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Feb 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie, step 1: Come up with an idea</title>
        <link>/2020/01/27/indie-1.html</link>
        <guid isPermaLink="true">/2020/01/27/indie-1.html</guid>
        <description>&lt;p&gt;Back in the early days of the App Store, a prevailing sense quickly grew that all you needed to become an overnight success was a great new idea for an app. I’m not sure that was ever really true, but certainly, it was easier then to stand out on the strength of a unique idea, and build a successful app around it, than it is today.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-1.png&quot; alt=&quot;Have idea, profit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I think that idea of the App Store as a get-rich-quick machine still lingers to this day — I still often run into people who, upon learning what it is I do for a living, excitedly tell me about their great idea for an app, convinced that if only they could get someone to build it for them, it’d be an instant hit. The reality, of course, is that the App Store that launched in 2008 with 500 apps now has close to 3 million. It’s crowded out there, it’s hard to get noticed, and the way in which the idea is executed is a whole lot more important than the idea itself.&lt;/p&gt;

&lt;p&gt;So then, what does make for a good “app idea” in 2020? As an indie app developer, how can you make sure you’re pursuing an idea that actually has a shot at gaining some traction in a crowded space?&lt;/p&gt;

&lt;p&gt;😐&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually, I really don’t know&lt;/strong&gt;. Nevertheless, in starting up my current project, I’ve tried to stick to a few guidelines that I think will help increase the app’s chance of finding an audience, should it ever find its way onto the App Store. These aren’t scientific or foolproof; they’re just a handful of simple ideas I’ve chosen to keep in mind based on past experience, and on what I’ve seen in the iOS community recently.&lt;/p&gt;

&lt;h3 id=&quot;dont-try-to-take-over-the-world&quot;&gt;Don’t try to take over the world&lt;/h3&gt;
&lt;p&gt;This one feels pretty obvious to me, but is worth mentioning nonetheless. You’re one person; aim small. You’re better off aiming to build a simple product that a handful of people will love, rather than some sort of globe-conquering social media platform. Focus on quality within a niche, rather than spreading yourself too thin building something overly ambitious.&lt;/p&gt;

&lt;h3 id=&quot;solve-a-problem-youre-personally-facing&quot;&gt;Solve a problem you’re personally facing&lt;/h3&gt;
&lt;p&gt;There are two big benefits to building something that directly solves an issue you’re facing, however small that issue might be. One, you inherently have a great understanding of the problem you’re trying to solve, and therefore, what types of functionality are going to provide a lot of value. Two, the desire to solve a problem for yourself gives you an extra layer of motivation to help you keep moving forward when things get tough. Even if no one else ultimately uses the thing you built, at least you’re left with an app that’s useful to you.&lt;/p&gt;

&lt;h3 id=&quot;cut-scope-ruthlessly&quot;&gt;Cut scope ruthlessly&lt;/h3&gt;
&lt;p&gt;If there’s one thing that always seems to hold true about software, it’s that it always takes waaayyy longer to build than you intuitively think it will. Even if you’ve got what feels like a pretty manageable, niche idea, always be thinking about how you can reduce the scope of what you’re doing and minimize effort — even if your instinct is actually to add more and more cool features as you explore the idea further. The nice thing about software is that you can always add those cool features via updates later. Getting the app across the finish line is always going to be hard, and you probably don’t want it to drag on months later than expected — especially since getting your app into users’ hands is the best way to figure out what kinds of additional features the app needs.&lt;/p&gt;

&lt;h3 id=&quot;make-sure-the-core-concept-is-easy-to-explain&quot;&gt;Make sure the core concept is easy to explain&lt;/h3&gt;
&lt;p&gt;At some point, in some way, you’ll have to convince people to give your app a shot. People are busy, and attention spans are short; being able to explain what your app does — and why it’s useful or different — in a quick and compelling way will help get users in the door. This can also help you gain clarity around what to actually focus on building initially — if it feels like the only way to explain your app’s value is to list off six loosely related features, you might want to consider narrowing your focus. (see “Cut scope ruthlessly” above!)&lt;/p&gt;

&lt;h3 id=&quot;keep-monetization-in-mind&quot;&gt;Keep monetization in mind&lt;/h3&gt;
&lt;p&gt;This is an area where I’ve still got a lot to learn, but I feel fairly confident saying this: if you intend to try to make some money with your app, it’s worth putting some thought up front into how you might go about it. Will it be a simple paid app? Free with in-app purchases? Will it use a subscription model? Will it be ad-supported? These are important to start thinking about early, because they can shape decisions around what kinds of features the app needs. For example, if you want to distribute your app for free, but provide a premium subscription option — a model that’s become quite common over the past few years — you’ll need to think carefully about which features are free, and which are premium; how are you going to make the premium subscription compelling, while also ensuring the free version is useful?&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;And there you have it: step 1 of my step-by-step guide to becoming a &lt;strong&gt;guaranteed overnight indie darling&lt;/strong&gt; is complete. Is anything in here wildly off-base? Did I miss anything obvious? I’d genuinely love to hear from you on &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;Twitter&lt;/a&gt; if you have any questions or thoughts 🐦&lt;/p&gt;
</description>
        <pubDate>Mon, 27 Jan 2020 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Going indie: Introduction</title>
        <link>/2020/01/26/indie-intro.html</link>
        <guid isPermaLink="true">/2020/01/26/indie-intro.html</guid>
        <description>&lt;p&gt;I’ve got a big idea for a series of articles I’d like to start writing. On its surface, the pitch is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I’m going to show you, step by step, how to build a successful app as an independent app developer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-intro-1.png&quot; alt=&quot;A map to fame and fortune&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;large-caption&quot;&gt;As you can see from this map, this is going to be quite easy.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;The reality, of course, is that I have absolutely no idea how to build a successful indie app. I’ve never done it, and actually, if I’m being honest, it seems pretty hard.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-intro-2.png&quot; alt=&quot;Uncharted territory&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But, that’s kind of the point. I’ll write these articles in real-time, as I attempt to build and ship my own &lt;em&gt;wildly&lt;/em&gt; successful independent app, in the hopes that I’ll end up with a fun and compelling chunk of writing regardless of how that app turns out. If I defy the odds and manage to actually build something that finds some version of even moderate success, then I’ll have documented many of the steps (and missteps) I took to get there, which will hopefully be interesting and helpful to anyone else aspiring to do something similar. If I jump ship halfway through development, or release an app into the void and never get so much as a dime out of it, then my hope for this series is that it becomes a funny and honest look at how hard it can be to find success as an independent app developer in 2020.&lt;/p&gt;

&lt;p&gt;More than anything, it’ll be a fun outlet for me to write about the ups and downs of setting out on my own and trying to build something worthwhile.&lt;/p&gt;

&lt;p&gt;Here are a few initial goals I’ve got in mind for this thing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I generally want to keep the articles pretty short and easy to read.&lt;/li&gt;
  &lt;li&gt;I want to write something about once a week.&lt;/li&gt;
  &lt;li&gt;I want a good mix of technical and non-technical stuff.&lt;/li&gt;
  &lt;li&gt;I want to keep things light and self-deprecating. You can go ahead and imagine that every article has this image featured prominently:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/indie-intro-3.jpg&quot; alt=&quot;A dog at a computer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;…because, despite now having built apps professionally for something like 8 years, I still feel like a dog in a tie on a day-to-day basis. I think that’s normal, especially when embarking* on a new project like this, and I think that’s okay — it means I’m still pushing myself and improving at the things I do.&lt;/p&gt;

&lt;p&gt;Come along for the ride! I’ll post updates on Twitter. You can find me &lt;a href=&quot;https://twitter.com/danielmgauthier&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p class=&quot;notes&quot;&gt;* Just wanted to call attention to this dog pun. I’m off to a good start.&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Jan 2020 00:00:00 +0000</pubDate>
      </item>
    
  </channel>
</rss>
