Swiftjective-C: Swift, iOS, SwiftUI, and Indie Development

Swiftjective-C is Jordan Morgan's independently published blog about building excellent software for Apple platforms. It includes practical Swift, SwiftUI, UIKit, accessibility, App Intents, architecture, design, and indie development articles, plus complete code samples and long-running notes from shipping iPhone, iPad, and Mac apps.

// Written by // // Read it in about 5 minutes // RE: Siri

Domains were never going to work1. And Siri was, unfortunately, always going to remain Siri.

Nobody wanted that, especially developers. Before 27.3, unless your app fit into a blessed domain, there weren’t many opportunities we could take advantage of in our apps. Why? It’s simple:

Apps do not fit into pretty little boxes.

Apple tried to do two things. They wanted a Siri that understood what our apps could do, and a system broad enough to include apps of every shape and size. But that was never going to happen; we still had to squeeze our apps into boxes Apple picked.

For apps like mine, they tried to achieve both and ended up accomplishing neither.

But lo, with a random post on X, I saw that Apple is finally freeing us from domains. You can supply your arbitrary app intent definitions to the system, and even supply skill files in Markdown to help Siri know how, and when, to use them.

Here’s a concrete example from Elite Hoops. I already have app intents to:

  1. Create a player
  2. Open a team

This is how it was before:

Hey Siri, add a player to a team in Elite Hoops.

That’s a real one you can try in Elite Hoops through App Shortcuts today. Siri could run that action and ask for the missing details: team, name, position and jersey number. I could also build a shortcut that added the player and then opened the team.

But I had to wire those steps together. Blegh.

This is what we can do now:

Hey Siri, add Maya to the Tigers in Elite Hoops. She’s a point guard, number 12. Then open that team.

Once I tack on the new macro to expose those actions as tools for Siri, this is the type of stuff the new system is designed to handle: find the Tigers, fill in Maya’s details, add her, then open the team. The coach says what they want, and Siri can choose and combine the actions. And, best of all…

…Apple didn't have to invent a basketball domain first!

And the conversation could keep going — yes, the promises of that commercial are coming to fruition. Say I’ve diagrammed plays with “press break” in their names or notes:

Okay, find my press break plays. We’re running those tonight.

Then, once Siri has found them:

Now email Coach Jones those play notes using Mail.

I wouldn’t need to repeat “Elite Hoops,” rely on fickle phrasing that has to be just right, or even bother listing the plays again. Of course, the context has to come from somewhere: here, I told Siri which plays we’re running tonight. It wouldn’t magically know our game plan just because I added Maya to the roster. And, again, I could already build a shortcut to find and email content before; the new opportunity for us is that now, Siri can put together that workflow conversationally.

And then there’s App Skills, which let us give Siri a Markdown file explaining how to use my app’s actions together. For me, that’d be things like:

  1. What a coach means
  2. When to ask a follow-up
  3. Which actions to use together

For working with a coach’s library, I could write a SKILL.md that looks something like this:

---
name: coaching-library
description: Help coaches find, review, add notes to, and share plays, drills, and practice plans in Elite Hoops.
---
Start with the coach's goal. They may describe a basketball
concept, a practice focus, or a change they want to make
rather than name a specific item or action.

Use the request, conversation, and available onscreen context
to identify the relevant content. Find matches using names,
notes, and other available properties. Carry established
selections into follow-ups; ask when the target is unclear.

Use saved content when reviewing or summarizing the library.
To record coaching feedback, use the edit coaching notes
action. Add to existing notes unless replacement is requested.
Distinguish a reusable drill from its instance in a practice
so feedback goes on the item the coach means.

When sharing, use the selected content's available transferable
representation with the requested destination. Clarify an
ambiguous recipient or selection before proceeding.

Add a Skills Bundle to your app target in Xcode, then put the file here:

Skills.skillsbundle/
└── Skills/
    └── coaching-library/
        └── SKILL.md

Putting it all together, Siri reads the description to decide when the skill applies, then reads the instructions. The app intents still do the work, but now the skill is added guidance. As God intended.

But…dude…..FINALLY! Finally! A direction that makes absolute sense for Siri.

Until next time ✌️

  1. They were never going to work for the app ecosystem at large; the variety is too large to fit into existing definitions. ↩

···
// Written by // // Read it in about 2 minutes // RE: The Indie Dev Diaries

Everybody can make an app now, right? Apple’s slogan, “There’s an app for that,” holds more true than ever. There’s an app for everything. In fact, there are multiple.

There are probably too many apps for “that” now.

How did we get here? Let’s look at how our superpowers as developers have become…what’s the right phrase here…less of a competitive advantage than they used to be? I think there are two distinct events:

  • SwiftUI: “Designers should code.” That whole movement got started around here. SwiftUI made it trivial to kick off real, native iOS designs. And so designers started going nuts, creating their own fine-tuned UI since they no longer had to rely on developers.
  • Then agents: The obvious one now. We all can build stuff because writing code is no longer the thing keeping you from doing it.

So if there’s an app for everything, and designers who can design better than you are shipping — where does that leave crusty ol’ developers? The answer, I think, is simple. We simply have to do one thing:

Stay motivated!

We have battle-tested experience, and we should use it. I think developers are still the best-positioned people around to ship and succeed with software. But a lot of us make the same mistakes that stop progress before it ever starts. And it’s because we don’t want to market.

You cannot afford to do that anymore. Some dropshipper, some designer, some vibecoder, or some other experienced developer will come along and straight up obliterate your niche. Simply put, you cannot ship to an empty room in 2026.1 You have to bring people in, talk to others about your app, or generally gain some fans pre-launch.

Momentum is at a premium, marketing is paramount, and retention is going to be the only semblance of a moat we can really have going forward.

Here’s how I think you can avoid empty room syndrome, straight to the point in a few sentences:

  1. Grab some emails: Stand up a landing page that lets people drop their email if they’re interested in your app. This is NOT for a TestFlight.2 Boost an Instagram post or something, show off your app, and use a “Let me know when it launches” type of approach.
  2. Find at least three places to share: Think outside of the typical box. Not X. Not Reddit. Find Facebook groups, or a newsletter, or a Discord, or something where people who are your ICP might be. They are usually open to hearing about stuff a bit more. Get them to step 1.
  3. Then, stand up preorders: Now, once your app is approved, drive your mailing list to preorder. You get that nice chart boost on launch day, and it’s so nice as a dev to see an app charting out of the gate.

That’s it.

Final thoughts

And that’s how I would do it. I mean, that’s how I’ve been doing it! You have to give yourself a boost. The days of building it and having users just show up are long gone, and even though we have to sing for our supper a bit — you can still have a strong launch. If you’re fine with launching in stealth and slowly building up your user base, then I applaud you. I can’t stomach that anymore. I need the endorphins right out of the gate!

Until next time ✌️

  1. Yes, it’s possible and some do it this way. For me, though, I just don’t see a way forward to release apps like this anymore. ↩

  2. This might completely just be me, and it’s also a little bit unreasonable, but the whole “Join my TestFlight!” post that leads to a landing page asking for an email completely turns me off. Either let me in or don’t. Don’t say you have a TestFlight and then gate me. There are simply too many other apps out there. I’m out. In my example, you aren’t letting people in; you’re gauging interest. ↩

···
// Written by // // Read it in about 1 minutes // RE: The Indie Dev Diaries

A bit ago, Elite Hoops hit $10k MRR. That’s a mark I would’ve been on the edge of my seat, eagerly waiting for, not even a year ago. In reality, the milestone passed me by. I didn’t happen to notice it until I was triaging a customer issue earlier this week.

Make no mistake, I’m proud! It’s super awesome! But, here’s what I’ve learned:

You hit the fabled #TenKMRRClub before you actually hit the revenue mark. Because, if you’re somewhat close, then you’ve probably already figured out the hard parts of getting there. And for most of us (and there are several, rocketship exceptions), it’s the most boring process in the world:

  • You find a pain point. You solve it.
  • The first few versions aren’t fantastic, but if people are paying for it, then BAM! You’re onto something.
  • Then, as the years go by, you simply don’t quit.
  • With a gut intuition, and keeping a sharp eye on your sector, and the people in it, you make the thing better.

And I’m telling you, that’s really it. It’s boring. It’s a process of waking up, and making the product slightly better than it was the day before. I could go on about marketing tips, customer acquisition, measuring ARPU, the lot of it.

But, when I sat down to write this, and I thought to myself, “What would Jordan, three years ago just getting started with Elite Hoops, want to know?”

And the answer? Man, it kinda sucks.

You mean to tell me you simply just keep making the thing better!?

Well, yeah. I do. It’s the best advice I can give, and so many people don’t do it. If people pay for your solution, then where there is smoke, there is fire. So just keep feeding the fire, and you’ll survive:

  • The highs and lows.
  • The copycats.
  • The occasional one star review or refund request.
  • The App Store submission that goes nowhere.
  • The Reddit comment that swears, with the utmost righteous, unwavering conviction, that there is no possible way your app warrants a…gasp!…SubScriPtiOn!
  • The realizations that there’s always a bigger fish, and your success seems diminutive to someone else who may have started last week.
  • The fight to constantly resist gilding the lily, and focus on the things that actually might move the needle.

And, eventually, one day you wake up and it just…all kinda works? And, that’s boring, and it’s also true. Three years, tons of iteration, tons of tiny little improvements, the occasional big improvements, and lots of love.

That’s the most boring way possible to 10k MRR.

Until next time ✌️

Epilogue: As an aside, this marks the end of publicly sharing revenue. It started, and has always been, about feeding into the community which has given so much to me. I wanted to give back. Though, as Elite Hoops starts entering big boy “Uh, oh - this is a thing now” territory, I think it’s best to keep the books to myself now. However, being helpful and sharing stuff is something I want to always embody. I plan on sharing more on how to run this type of business, in lieu of raw numbers, and I think that’s just as useful.

···
// Written by // // Read it in about 1 minutes // RE: The Indie Dev Diaries

DHH’s recent keynote talk, given at Rails World, has been all over social media:

I’ve gotta say, I agree with him. It is over. The whole writing code by hand thing. And, probably like many of you, I’ve had a cavalcade of feelings about it.

I vividly remember when I realized it, too. It was summer of 2025 when Claude Code first was making waves. At that time, I was developing Alyx, trying to hit the iOS 26 release. After carefully reviewing some documentation and watching a W.W.D.C. session, I handcrafted Alyx’s first app intent.

And that was it.

I pointed Claude at what I made, and it proceeded to develop the other 49 intents. I played product manager and code reviewer. Claude did the grunt work.

Initially, this made me feel useless. The craft I’d spent years honing? A large language model could not only do it, but in several cases, it could do it better. Let alone faster.

Then, over the months, I learned to embrace it. Figure out its nuances, little levers to pull and buttons to push. As time went on, I became a bit more comfortable. All the while feeling a bit uneasy about it, like I was giving up my automony and expertise to this mythical CLI tool. But I didn’t want to give it up, either. It was an odd stockholm syndrome type of situation.

Still later — acceptance. Software Engineering? It is irreversibly different. There is no going back, and the toothpaste is not going back in the bottle.

Which brings me to today. And, now? Joy. I think that’s something a lot of us are missing in this whole thing. We were there when it was done by hand, and now we’re literally living in, and experiencing in real-time, the next tipping point in our craft. How awesome is that!

Has our industry always not been about dealing with change: the new tools, APIs, languages, models, architecture, the way we do things? The best engineers I’ve ever known have always embraced it, and they are right now.

I’ve always leaned on being an optimist, it feels like a waste of time and a life to really be anything else. And, I feel optimistic as a builder of things. That’s what we are now more than ever, and the tools and the means to get to the “thing” have dramatically changed.

So yeah — it’s over. And when things are over, something else begins.

Until next time ✌️

···
// Written by // // Read it in about 3 minutes // RE: Swift

Now that we’ve seen Apple’s new flagship device, and have the simulator to boot (🥁!), I’ve been diving in on adapting Elite Hoops for the new form factor. As Cupertino & Friends™️ note in their tech talks, we’ve been here before. Over its ~20-year life, we’ve seen the iPhone grow taller, become shorter, lose a home button, gain some ears, and then some. Those of us who’ve been around likely already have developed a precocious ability to listen to Apple to handle these changes, and use Auto Layout/safe area/etc. etc. etc.

And so, after playing around with the device and coming up with a design plan, there’s one word I keep returning to: expressivity. This device allows you to express what you already offer in new and, in many cases, better ways. Much like when adapting an app for iPad, you don’t gate features by device, you let them grow and mature with the form factor.

Here’s a preview of what I’ve done. But first, you need to set your codebase up a little. Let’s start with a few things that may screw you if you’ve put these types of things off:

  • Using device idiom checks.
  • Relying on UIScreen.main — it’s going to be deprecated soon.
  • Not supporting landscape orientation.
  • Using .leading / .trailing toolbar placements for close buttons. Use ToolbarItem(placement: .cancellationAction).
  • Not relying on size classes.

All of those will torch you here, but luckily, there’s a built-in skill to help with all of this: app-resizability. It’ll help you address each issue listed above.

The App Resizability skill in Xcode helping prepare an app for iPhone Duo.

Now, let’s get to the design changes, pose by pose:

Closed

The closed pose will be easiest for most of us. It’s basically a chonky iPhone:

Elite Hoops running in iPhone Duo's closed pose.

Here, you’re mainly looking at bars: navigation bars, tab bars, toolbars — that kind of thing. That’s because of the nascent vertical bar setup, and the fact that the system will sometimes need to overflow items. The question you need to ask is: what’s most important, is there anything that should always show, and what can be over…flown (is that a word?).

Things that need to stick around? Use the .topBarPinnedTrailing placement. Do you want it to win out over other things when an overflow is needed? Reach for .visibilityPriority(.high). Or, overflow ahead of time with ToolbarOverflowMenu.

Opened

This is where the fun starts, and we get an opportunity to express existing features differently. Here, someone is viewing a public play. But — I’ve got more room to play with! So, let’s make use of it:

Elite Hoops adapting a public play view to iPhone Duo's opened pose.

Using the new ArrangementView, along with @Environment(\.toolbarVerticalEdge), there’s an opportunity for more data density. And, that’s what you’re seeing here: let’s show the play notes or its attachments — stuff that supports the primary view. Because now, there’s more room for it.

I think this is where the bulk of your work comes for iPhone Duo. Don’t simply aim for making your app work on it, make it freakin’ baller! And, I think this is one way you can do it. Another example:

Elite Hoops play designer with larger, more widely spaced tools on iPhone Duo.

The play designer in Elite Hoops is where the bulk of coaches spend their time. It has several tools, but needs to keep the focus on the court, and where players should be. With Duo, I’m able to make two critical improvements: space out those tools in the toolbox, and make them bigger.

Continuing the theme of data density, and the ability to spread it out — let’s look at the changes for when a coach is running practice:

Elite Hoops practice screen using iPhone Duo's extra space to emphasize drill information.

Here, I’m able to make the important things stand out a little more. How much time is left in this drill? What’s next? What are the drill’s steps? It’s all made a bit easier to parse on Duo.

The part that I am currently undecided on, however, is how liberally to deploy these arrangement view refactors. Do I always leave them in, and let iOS decide? That’s one part I’ve not yet worked out.

Rotated

For rotations, I’ve found that my existing size-class-based decisions (in terms of how to present things) held up remarkably well. For example, for the digital whiteboard, it now takes advantage of Duo’s real estate to expand and have a richer area for coaches to mark up with their Apple Pencils:

Elite Hoops digital whiteboard expanded across iPhone Duo in the rotated pose.

Not much to say here, I’ve found that it essentially mirrors what your iPad implementation does today. As it should.

Folded

For folded situations, there wasn’t much to address. It leverages the arrangement view in most cases, and in some cases, it doesn’t, on purpose. I’m in love with how it looks, though. For example, here’s the play designer from earlier:

Elite Hoops play designer adapting to iPhone Duo's folded pose.

One thing I want to emphasize is that the system does the right thing nearly every time, as long as you didn’t reinvent the wheel. Things like the new reserved area and staying away from the fold in these folded situations — the system does all the work. Let it.

Revisiting old assumptions

Designing for iPhone Duo already pushed me to make some navigational improvements for Elite Hoops that I’ve wanted since launch: using a split view. I’ve put this off more times than I can count, but it fit in with all of the other work I was already doing:

Elite Hoops using a split view on iPhone Duo.

Plus, I had to revisit UI that looked great on modern iPhones but was no longer at home on Duo. For example, these bottom rounded corners look quite nice and are concentric with iPhones, but they look out of place on Duo in my opinion:

Elite Hoops UI with rounded bottom corners reconsidered for iPhone Duo.

You’ll quickly find places where you should’ve used a Label with an image and title. I cleaned up all such instances so that they show correctly in overflow menus with a more descriptive string supporting what the action will do.

Final thoughts

And that’s a wrap. If there’s one thing the iPhone Duo has been for me, it’s fun. I have to get one of these! It’s a business expense, after all. Sometimes there’s a serendipitous moment where what you want and what you need end up being the same thing, I guess. The Duo is an exciting little device to design for, and it’s not every day we get new form factors to bring about some fresh new ideas or new angles.

Until next time ✌️

···
Visit Archive