5

I don't understand why for creating UIImage I just pass String in constructor but for creating NSImage I have to pass NSImage.Name in constructor. What idea is laying behind this solution?

Code:

iOS

let image = UIImage(named: "name")

Mac OS

let image = NSImage(named: NSImage.Name("name"))

Thanks.

Alexander
  • 59,041
  • 12
  • 98
  • 151
Bohdan Savych
  • 3,310
  • 4
  • 28
  • 47
  • https://stackoverflow.com/questions/49052950/loading-image-from-assets-to-nsimage-keep-getting-error-expecting-nsimage-name – Martin R Jul 10 '18 at 16:56
  • To those voting to close this as primarily opinion based, please note that this actually has a good, objective answer. – rmaddy Jul 10 '18 at 17:10
  • why unify and make it easy if you can make developers suffer? – Duck Dec 16 '19 at 08:39

2 Answers2

7

The expectation is that you have a centralized spot where you extend NSImage.Name with static constants that define all your image names. Then, all your code references these single members, rather than repeating magic strings.

extension NSImage.Name {
    static let square = NSImage.Name("square")
    static let triangle = NSImage.Name("triangle")
    static let circle = NSImage.Name("circle")
}

//...

let image = NSImage(named: .square)
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Why they are not using the same approach in `UIImage`? – Bohdan Savych Jul 10 '18 at 16:59
  • 2
    @BohdanSavych you'd have to ask Apple – EmilioPelaez Jul 10 '18 at 17:17
  • 1
    As I have said in other places relating to `NSNib.Name`, etc., this isn't actually true as `Name` is just an alias for `String` so when you extend `NSImage.Name`, you're really extending (I'd argue cluttering) the API for strings. Had they made `Name` a concrete type like they did with the struct `Identifier` with segues, this would make sense, but without it, *anywhere* that currently takes a string can now also take `.square` because again, you have extended `String` via an alias. It's not scoped to this usage only. IMHO, this doesn't add anything and as @EmilioPelaez said, ask Apple why. – Mark A. Donohoe Feb 24 '20 at 00:10
6

Apple doesn't always stick with decisions like this. In Swift 4.2 (Xcode 10), NSImage.Name is now just a typealias for String — it's no longer a special struct. This way you still get some benefits compared to the parameter type just being String, and don't have to wrap every call site in an initializer.

The remaining lightweight difference in type is a communication from the API designer to the developer, even if it’s not enforced by the compiler. It tells you what kind of values, out of the universe permitted by the type system, make sense for realistic use. It’s like how a parameter of type TimeInterval permits any Double, but when you see TimeInterval there you get the hint that passing, say, the aspect ratio of your screen won’t be anything more than coincidentally meaningful.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • But you can still pass a string literal, completely unobstructed, right? How does that "suggest that you shouldn't just pass any arbitrary string"? This is where I really wish Swift had an equivalent to Haskell's `Newdata`. Similar to a type alias, but constructs a new, non-equivalent type, that can be bridged back/forth, but only via explicit coercion. – Alexander Jul 11 '18 at 05:48
  • 4.2 reverts back to normal. NSNib.Name was a waste IMHO. You can still make image names static strings, but no need for NSImage.Name(rawValue:) – Klajd Deda Sep 22 '18 at 01:51