Thursday, February 26, 2026

Why Not Objective-C

Brent Simmons:

I led the effort to port our remaining Objective-C to Swift. When I started that project, Objective-C was about 25% of the code; when I retired it was in the low single digits (and has gone even lower since, I’ve heard).

[…]

Objective-C code was where a lot of our crashing bugs and future crashing bugs (and bugs of all kinds) lived.

[…]

A second thing we knew was that having to interoperate between Swift and Objective-C is a huge pain.

[…]

And a third thing we knew was that very few of our engineers had a background writing Objective-C. Maintaining that code — fixing bugs, adding features — was more expensive than it was for Swift code.

I prefer Swift for new code, but I don’t really want to rewrite working code. I haven’t found my old Objective-C to be a source of bugs. However, I always seem to end up rewriting more than I initially planned because the interop is such a pain. I like using Swift for the main data types in my app, but of course those get used everywhere, including from Objective-C code. I’m also convinced that Objective-C interop is a source of some of the weird compiler behavior I see with crashes and dependency tracking. The part I don’t like rewriting in Swift is stuff that calls plain C APIs. The Swift version often feels less readable, and I’m less sure that it’s correct—both the opposite of my normal experience with the language.

Update (2026-03-03): Brent Simmons (Mastodon):

Then of course I wrote some Objective-C code recently and really, really loved it.

[…]

Once you get past that, which takes a day or two given a good-faith effort, you’ll realize how small a language it is, how easy to hold in your palm and turn around and understand all sides of it. And you’ll appreciate how easy it is to make good decisions when you don’t have a surplus of language features to choose from.

[…]

It’s a cliché to call Objective-C a more elegant weapon for a more civilized age. It’s better thought of, these days, as a loaded footgun.

But I did absolutely love writing this code! So much fun.

Brent Simmons:

Two things I didn’t note in my post on Objective-C but could have: the compiler is crazy fast, and the debugger is wonderfully smooth, without all the latency we’ve become used to the last bunch of years. The whole experience feels so different — so light and easy.

smitty825:

I have an old Objective-C Mac app that I have to go back and rebuild on occasion. There are two things that blow my mind each time I rebuild:

  1. No matter what version of Xcode I use…it always compiles successfully the first time.
  2. Holy hell…does it compile fast! It feels like I press the build and deploy button, and it’s deploying almost immediately.

I was recently working on some Objective-C code, while my Mac was on battery in Low Power Mode. I made some changes to the app code and pressed the keyboard shortcut for Test Again. At first I thought nothing had happened, but actually it had recompiled and run the test in the blink of an eye. It feels totally different from writing Swift, where even a tiny change in test code (so that app doesn’t need to be recompiled) takes 5–10 seconds.

Update (2026-03-04): Manton Reece:

I still prefer Objective-C and plan to continue to use it. One change I’m thinking about making, though… Going all code for UI instead of XIBs or Storyboards, to make it easier to diff when working with AI.

Previously:

11 Comments RSS · Twitter · Mastodon


Pierre Lebeaupin

Even in my exploratory, Swift-only projects, I end up reaching for Objective-C whenever I need to call some C API (like a setsockopt not supported by the existing Swift network libraries, or an obscure sysctl), by first wrapping it in an Objective-C class: even with the added context switch it is still clearer and more readable than using Swft's support for plain old C interop. But maybe I haven't hit into the limitations of calling Objective-C from Swift.


Curious how the bug described in this post is Objective-C’s fault? Seems like a server side bug but I get that it may be convenient to blame a language instead of people


@Objc4Life My read is that the app was sending the wrong value to the server API, so I see that as a client bug. I guess every bug is ultimately a person’s fault, but some languages make it easier or harder to accidentally use the wrong type or NULL (or have a bad numeric conversion, overflow, etc.).


> My read is that the app was sending the wrong value to the server API, so I see that as a client bug.

I guess it depends on how we interpret. But if a server API is something you'd express with a BOOL (true or false) I would expect anything less than or equal to 0 to be treated as FALSE and anything greater than zero to be interpreted as TRUE.

@(YES) and @(NO) create a NSNumber with an .integerValue of 1 or 0 so but even if you passed 200 to the server and that shouldn't be all it takes to cause a widespread outage? If it is I think the server code is too brittle? Would be interested to know how the number was actually created.

I just took a quick look at the blog post again:

> because the original code was using NSNumber and it wasn’t obvious which it should be.

If the API is so poorly documented that you don't know the meaning of a parameter (is it a BOOL or is it a number) so you just guess? Seems like a bit of a stretch to tie that to ObjC. There was no to ask? I imagine you can send the wrong type to a server API in Swift if you just write the wrong type in your client... the type checker isn't going to do anything for you if you prepare data incorrectly on the client and send it off to the server?

but also I don't have all the informations so I guess I could just be wrong


Also on the subject of types - ObjC is WAY better with types than languages like Javascript and Python. I'd say Objc is really good with type checking as long as you don't use id everywhere -- and the compiler never gives up because it can't type check in a reasonable amount of time.


@ObjC4Life Yeah, it’s not totally clear to me from the description what happened, so I guess I’m assuming an interpretation where the writer’s point makes sense. Like, the API officially takes an integer, because larger values are legitimate in other cases, but the client was meant to be sending a 1. The NSNumber was not created from a boolean constant, so it had some other true value like 126 that should have been converted down to a 1 but wasn’t.

I agree that there’s also a documentation issue, and your interpretation that the server only wanted a boolean and failed to validate its input is also possible.

I think Objective-C type checking is fine except when it comes to collections. Lightweight generics are unwieldy and not useful for much beyond documentation, IMO, and they wouldn’t help with this sort of boolean/integer problem.


> I think Objective-C type checking is fine except when it comes to collections. Lightweight generics are unwieldy and not useful for much beyond documentation, IMO, and they wouldn’t help with this sort of boolean/integer problem.

I find type checking in ObjC to be pretty good. In collections good enough for me I guess. Sometimes I’m bagging three different types in separate arrays in the same loop and generics can be helpful catching cases where you accidentally add an object in the wrong array. pretty good imo not perfect but strikes a good balance —catches most common issues and the compiler never chokes. Perfect can be the enemy of good.

But yea it wouldn’t catch an integer/bool issue but neither would the Swift compiler if you just encode the data in the “wrong type” on the client and send it off to the server. If the API could legit accept more than true or false an enum rather than a Bool should be used to describe all possibilities. I don’t really see how this can be an example of Swift safety. Sounds like a bug you could write in any language including Swift in that case. But If they sent 256 to a server that truly accepted a bool…how’d you create the nsnumber and why couldn’t the server handle it gracefully? So much misinformation out there about ObjC in the developer community.

Also
I find it somewhat amusing that type checking in Objc gets criticized. Meanwhile server side people continue to write their stuff in PHP, JavaScript, Python, etc.


@Objc4life Yeah, I guess it is kind of a historical accident because those languages offered other advantages? They are all moving in the direction of more typing (or switching to TypeScript).


Bryan Feeney

> ObjC is WAY better with types than languages like Javascript and Python.

Modern Python looks like the code below, and can be checked at "build" (i.e. packaging) time by tools like mypy or ty.

def concatenate[A: (str, bytes)](x: A, y: A) -> A:
    """Add two strings or bytes objects together."""
    return x + y

def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
    return (*tup[1:], tup[0])

def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]:
    '''A type-safe decorator to add logging to a function.'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        logging.info(f'{f.__name__} was called')
        return f(*args, **kwargs)
    return inner

@add_logging
def add_two(x: float, y: float) -> float:
    '''Add two numbers together.'''
    return x + y

Admittedly these are extreme cases, but as Michael said, most languages -- even hitherto dynamic languages -- are moving towards more rigorous type-checking just to reduce the possibility of developers misusing APIs, or making APIs that are easy to misuse.

As someone who occasionally works with Python code it's support for "truthiness" -- casting things to bool automatically -- costs more time in bugs than it saves in (keyboard) typing. I'm a whole-hearted convert to the idea that floats aren't ints and ints aren't bools.


@Bryan Feeney I think type checking is a good thing. What I was saying is ObjC is pretty good at type checking and I always find the criticism about ObjC type checking interesting while so many people continue to use languages on the server that don’t do type checking. In server side development I'd think type checking is probably even more important than client side dev.

While you can install packages in Python it is not part of the language is it? in Objective-C you better type checking by default. If you pass in a string to a method that takes an array it’s really hard to not to notice that kind of issue before you send your app in the wild. In Javascript, Python, and PHP it's really easy to make those kind of mistakes.


10 years later its clear I made the right choice to continue to write in Objective-C. It's better than Swift is so many ways, and I don't regret sticking with it.

Leave a Comment