https://www.jetbrains.com/lp/devecosystem-2019/swift-objc/
How about HN devs?
https://www.jetbrains.com/lp/devecosystem-2019/swift-objc/
How about HN devs?
Some crypto code is also still Objective-C due to easier linking, but that'll probably change soon enough.
I don't consider ObjC to be a bad language in the slightest; in fact, I'll offer the (probably contrarian) opinion that it's one of the greatest languages of the past few decades.
I've never used it, but have seen many code snippets in API documentations, and it just looks so...unnecessarily obfuscated and verbose, and with a syntax so wholly unique to Obj-C that my brain can't make sense of it like I would looking at, say, Java code from a C++ background.
- As malleable as JavaScript when you need it to be
- As typed as your favorite language when you need it to be
- Able to drop to much more arcane levels if you need to for certain performance-intensive places, without requiring some crazy setup (e.g, C/C++ are right there if you need them)
- ARC is IMO one of the best approaches to memory management out there
- The verbosity can be annoying at first, but it forces you to think hard about everything, and when I come back to Objective-C code years later, I've no issue remembering what the hell I was doing there
- People complain about brackets, but they just... don't matter. It's a syntax. You either deal with it or don't - you don't see me writing Lisp because I find the syntax annoying, which is fine.
- Message passing in ObjC is so optimized that it probably can't get much faster, and anyone acting like it's slow has a potentially skewed understanding of this
Swift is nicer for me in only two distinct ways:
- No more header files, because man was that annoying
- The stricter nil handling is overall better, if not a mental shift from some ObjC counterparts
The problem with Objc message passing is not the absolute execution time involved in passing a message. The problem is that the dynamism involved completely disables the compiler from performing any code-inlining. Typically inlining small functions is what enables the compiler to unlock further optimisations. From simple things such as merging duplicate loads from same address, to auto-vectorization.
These types of speedups just aren't necessary in that world. You'd drop to C or C++ if you needed it, which is trading convenience for complexity.
You could want it to be faster, but it's not going to materially show itself in common workloads.
And it shows, Swift gets slammed on the ixy paper by all tracing GC languages, spending an huge amount handling counters.
It makes sense for Swift though, because it is the easiest way to integrate with Objective-C, given the failure of its GC experience , having to deal with C semantics and non-GC enabled frameworks, that lead to constant crashes.
This this this. I love how it forces the verbosity. It makes it so easy to understand.
> - People complain about brackets, but they just... don't matter. It's a syntax. You either deal with it or don't - you don't see me writing Lisp because I find the syntax annoying, which is fine.
I really don't mind the brackets. If you structure your code right, is not even that big of a deal.
> - Message passing in ObjC is so optimized that it probably can't get much faster, and anyone acting like it's slow has a potentially skewed understanding of this
Not just this, but I love how you can send messages to nil and it doesn't crash, or you can define a method in code and it runs.
Then much later Apple added the . syntax, which while more familiar to a lot of modern developers kind of broke the cleanliness that the syntax used to have.
I absolutely agree. Its such a beautiful easy to read and code language. Its got its quirks for sure, but I absolutely love it.
It wasn't viable as v1, but now it's solid & clean.
Seems like it was a good opportunity to take a well understood language (C/C++/Obj-C) and after 30 years rebuild it ground-up into what it should be. Some constructs & workarounds just weren't going away without a clean-slate industry-wide fully-compatible restart.
Top problem now is getting developers to not force-unwrap optionals unless unless able to prove it won't cause a crash.
Crashes & exits are not acceptable in production code, and I’ve had to fix too many of them.
If a thing might be null/nothing but can't be used when it is, then you should write code that indicates your understanding of that case. Catching an error doesn't imply that your program should proceed, it communicates that you the programmer understood your system and anticipated its failure states. Proceeding or aborting is a secondary decision.
There are cases where the programmer cannot express this to the compiler without a bottom type.
Obviously in F# or Haskell or C#8 or Rust this isn't a problem. But what we are talking about is: how do you handle, at runtime, in a language such as C#7 or Java, an null parameter being passed to a method that must return a value based on its parameter, but was passed null? It's a choice between very bad options where the least bad is usually to throw an ArgumentNullException or similar. Because there is nothing better to do.
I don't disagree with this, but I can point out gobs of places in AppKit where Apple has arguably 'made an error' and I can't do anything about it. Or in third-party C libraries, which hold 99% of the functionality I ultimately need, and were not designed for having nice Swift programming interfaces.
I feel like everyone saying "never force unwrap!" must be writing pure-Swift standalone functions, with no dependencies, including the operating system. The API for writing items to the Mac clipboard still documents that it can throw exceptions, which are impossible to catch in Swift!
Right, hence why I'm suggesting that they can be useful if you don't put them everywhere and aren't using them as a band-aid to make your code compile :)
Is it a hard coded string that will never change? Force unwrapping is probably appropriate.
Is it based on user input? Then you should probably tell the user the string they entered isn't a URL.
But if you're creating it dynamically you should be guarding and throwing an error.
- abort and unwind the current action
- retry something
- log something
- abort the app explicitly
- fallback to a different value
- comment why this is a known-possible failure state
- present a message to the user
...
guard let data = notification.userInfo?["updatedItem"] as? Data else {
assertionFailure("Could not retrieve updated item")
return false
}
I am really being helpful here? If I see a crash on this line: let data = notification.userInfo?["updatedItem"] as! Data
I get essentially the same information, except it's done in a less verbose and easier-to-discover way. Whereas the first one is like adding this kind of useless comment: let x = 5 // Assign 5 to x1. ! used here is hard to spot, it's just few pixels of difference from ?.
2. assertionFailure is not the same as force unwrap, because it crashes only in debug mode. Ideally you would log it with some analytics so developers know if problem occured, but doing nothing is usually better than crashing the app.
Adding a ! suppresses an error from the compiler, it doesn't fix an error in your code!
At a given point, you can handle errors, but there's nothing you can do about them
It's good when languages (and developers) realize there are errors you should handle but there are errors you "shouldn't" because there's nothing you can do but bail out.
Even in those cases where you can't recover from a failure of those expectations, your code will be 1000x more legible if you demonstrate that you understand that the error could exist and what it might represent about the system as a whole. If you want to explicitly fire a fatal error after that because there's no other form of recovery, so be it.
Trying to capture all that in a "!" saves you a few LOC (or worse: minutes of reasoning) now but suggests somebody else might be pulling their hair out in frustration six months later. Please don't do that to someone.
Same with Android, I would choose Kotlin before Java if it was my choice.
People still working with Objective C might be like I recently was: maintaining a sizable codebase which, unless Apple breaks something, porting to Swift is not justifiable to management.
You could do it with a while loop (if it exists in Swift) but I'm at a loss as to why you would remove a for loop from a programming language.
for i in 0..<10 {
print(i)
}As an also Lisp programmer, I find Swift's limited (and fixed!) set of control flow constructs downright ancient. They added for-each loops, but that's it. We had more "modern control flow" in the 1980's.
Besides, both of those examples are essentially non-local jumps, and I'm not sure I'd describe them as "modern".
In a sense, Swift already allows diversity in control flow constructs, via closures and the trailing closure syntax. It's just somewhat awkward, and not flexible enough to implement, say, most of Lisp's ITERATE library. That's packed full of exactly the kinds of control flow constructs that I have to write out by hand in Swift every day.
for (value, index) in arr.enumerated() { // Loop body }By the way, what is painful in terms of project fragility?
We also have several legacy apps that are all ObjC, or 50% ObjC. There's not a ton of new feature development, but still updates.
If I had my druthers, I'd be Swift exclusive.
…how are you interacting with UIKit?!
I rarely see that annotation or class name in my codebases too.
A quick search through one of my apps shows that about 4% of functions are marked as @objc (and we're not using the old compatibility mode where more methods were implicitly @objc either).