
It's Coming: The Great Swift API Transformation - ceeK
https://swift.org/blog/swift-api-transformation/
======
TillE
I hope they do adopt that suggested change to method calls. Swift is a nice
language, but there's still some awkwardness due to its required compatibility
with Objective C. I think if you don't come from that background, then having
all named arguments _except_ the first one seems really arbitrary and
nonsensical.

~~~
ceeK
To be honest, even as a fairly seasoned iOS developer I still prefer to have a
non-optional first argument in my APIs.

Whereas the post's example compares:

    
    
      path.addLineToPoint(CGPoint(x: 100, y: 0))
      -- to -- 
      path.addLineTo(CGPoint(x: 100, y: 0))
    

I've been doing it as so:

    
    
      path.addLine(toPoint: CGPoint(x: 100, y: 0))
    

...requiring the "toPoint", which can swapped out true method overloading
style:

    
    
      path.addLine(toArc:...)
    

In your internal method implementation, Swift allows you to replace the
external method name with an internal one, so that it's still nice to work
with:

    
    
      func addLine(toPoint point: CGPoint) {
      ...
      }
    

I personally think it's a lot more readable. Otherwise the first argument has
to have a really descriptive class name (recommended for sure, but often not
the case).

~~~
nardi
The point here (no pun intended) is to stop repeating noun phrases at the call
site. Your call site says "point" twice for no reason:

    
    
        path.addLine(toPoint: CGPoint(x: 100, y: 0))
    

See the "toPoint: CGPoint". It's useless repetition. The new form eliminates
it:

    
    
        path.addLineTo(CGPoint(x: 100, y: 0))
    

It's clear you're adding a line to a point, because it says "Point" right
there. Even if you have a variable for this point already, it works great:

    
    
        path.addLineTo(point)
    

Or maybe this is a more specifically named point?

    
    
        path.addLineTo(centerPoint)
    

It's clear from all that you're adding a line to a point. Swift has an
emphasis on concise syntax, and removing the repetition, IMO, is a nice win in
readability.

~~~
ceeK
You do have a good point, and something I didn't explicitly notice before.
This does seem to have the indirect consequence of enforcing some sort of type
information in the variable name, but I expect many iOS developers do that
regardless.

It'd also be interesting to see the impact on readability when you have longer
variable names. `point` definitely makes it more concise here. But if you had
several points within the same scope, readability may suffer given type
information is typically at the end of a variable name?

    
    
      path.addLineTo(childViewTopLeftPoint)
      path.addCurveTo(childViewTopRightPoint, withControlPoint1: arbitrary1Point, andControlPoint2: arbitrary2Point)
    
      path.addLine(toPoint: childViewTopLeftPoint)
      path.addCurve(toPoint: childViewTopRightPoint, withControlPoint1: arbitrary1Point, andControlPoint2: arbitrary2Point)
    

I feel the second version here allows me to bypass obtaining the type
information "..Point" from the variable name when reading.

Interestingly, I wonder how this type of method would be converted:

    
    
      addArcWithCenter(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
    

You'd perhaps think of addArcWith(center: CGPoint), but then you'd need to
have 'center' in the variable name to convey meaning. Keeping addArcWithCenter
maintains obj-c status quo. addArc(withCenter: CGPoint) is more Swifty, but
you may have repetition if your variable is named centerPoint, or similar. I
have a feeling it would be kept as is.

------
fleshweasel
The title they chose implies that they're actually changing the APIs to take
full advantage of Swift language features. Perhaps they should have named the
article "We're renaming some classes and methods."

~~~
e28eta
They're actually changing the way Obj-C APIs are "imported" into Swift. This
doesn't rename the method calls, it does more invasive name changes as part of
the bridging.

So the class UIBezierPath and it's method addLineToPoint: haven't changed, and
are still written in Obj-C. The difference is in how they're called from Swift
code.

Presumably it'll operate the same way in reverse, and this isn't specific to
Apple frameworks/classes.

------
bjourne
If it is one that that _should_ be known about Apple software by now, is that
things _will_ change from under your feet. :)

~~~
pjmlp
And yet only Microsoft gets criticised for doing it. :)

~~~
breakingcups
Microsoft is actually the king of backwards compatibility in my view, and
anyone who claims otherwise should peruse Raymond Chen's blog.

------
Benjammer
Could they just go all the way and use a mandatory first argument name to make
things even more concise? That would get away from something that annoys a lot
of people about Swift, the implicitly unnamed first arguments.

Something like this:

    
    
        class BezierPath: NSObject {
          func add(LineTo lineTo: CGPoint) {}
          func add(ArcWithCenter center: CGPoint, radius: CGFloat, 
                              startAngle: CGFloat, endAngle: CGFloat, 
                              clockwise: Bool) {}
          func add(CurveTo endPoint: CGPoint, controlPoint1 point1: CGPoint, 
                              controlPoint2 point2: CGPoint) {}
          func add(QuadCurveTo endPoint: CGPoint, controlPoint: CGPoint) {}
        }
    
        class main {
            func run() {
                let path = BezierPath()
                let point1 = CGPoint()
                let point2 = CGPoint()
    
                path.add(LineTo: CGPoint(x: 100, y: 0))
                path.add(ArcWithCenter: CGPointZero, radius: 20.0, 
                                 startAngle: 0.0, endAngle: CGFloat(M_PI) * 2.0, 
                                 clockwise: true)
                path.add(CurveTo: CGPoint(x: 100, y: 0), controlPoint1: point1, 
                                 controlPoint2: point2)
                path.add(QuadCurveTo: point2, controlPoint: point1)
            }
        }
    

The difference is mostly that it looks even more concise when using code
completion. You would see:

    
    
        path.add(LineTo: CGPoint)
    

Instead of:

    
    
        path.addLineTo(point: CGPoint)

~~~
nostrademons
It gets really annoying when you have short single-arg functions. Imagine what
swap(_:_) or Collection.suffix(_) would look like with mandatory first
arguments.

------
protomyth
I do wish the Swift developers had allowed actual Objective-C calls in Swift
to make it look the same.

Objective-C

    
    
      [aPath addLineToPoint:CGPointMake(200.0, 40.0)];
    

proposed

    
    
      aPath.(addLineToPoint:CGPoint(200.0, 40.0));
    

the more complicated example would be:

Objective-C

    
    
      [object selector1:item1 selector2:item2];
    

to

    
    
      object.(selector1:item1, selector2:item2);
    

although I'm still not sure about the love of commas

~~~
ricardobeat
Why would that be better than this, which still 'looks the same'?

    
    
         aPath.addLineToPoint(CGPoint(200.0, 40.0))
    

I don't see how your proposal would fit with Swift's syntax.

~~~
protomyth
I'm more concerned about multiple selectors and what a mess it becomes, and
yes, I wish they'd change the syntax.

~~~
sadawi
To me, something like your syntax makes sense when there isn't a clear
hierarchy among selectorPart1, selectorPart2, selectorPart3, etc. However, in
every case where a method can be thought of as having a single primary action
that is refined by arguments, I believe the action(argument1:value1,
argument2:value2) syntax makes more sense.

I wouldn't want to see, for example:

    
    
      collectionView.(dequeueReusableCellWithReuseIdentifier:identifier, forIndexPath: indexPath)
    

because that obscures the primary purpose of the method, which is to dequeue a
cell. To me, this is preferable:

    
    
      collectionView.dequeueReusableCell(reuseIdentifier: "ImageCell", îndexPath: indexPath)
    

The long first component of the Objective C selector
("dequeueReusableCellWithReuseIdentifier") is just an artifact of ObjC's lack
of distinction between method names and parameter names, which forces the API
creator to use words like "With" to accomplish what Swift can do natively.

What are some examples of methods you think make more sense with your syntax?

~~~
pjene
Objective-C has no object methods, just pure multiple-dispatch for a generic
"select" operation? that's amazing.

~~~
pcwalton
Objective-C does have object methods, but they're dynamically typed: methods
can come and go at runtime, method names can be constructed from strings, and
there's no need to downcast an object of a superclass type to the subclass
type before calling a method on that subclass. This design choice basically
forces Objective-C into doing a hash table lookup and indirect dispatch for
all method calls as a base case. This logic is encapsulated in the
"objc_msgSend" function, which the compiler compiles all method invocations
into a call to.

(If you're thinking "wow, that must be slow", you're right: objc_msgSend is
very heavily optimized, but you can't beat one load from a fixed offset and an
indirect jump as in C++ and Java. This is why Swift made the decision to
abandon this model from the get-go. Other dynamic languages have ways to
optimize this and eliminate the hash table lookup in most cases, but these
techniques require self-modifying code, which Apple doesn't want to use; the
only feasible solution for Apple's native language was to switch to a
different semantics.)

~~~
LeoNatan25
Swift has not abandoned this dispatch method "from the get-go", it is still
very much one of the supported dispatch methods in the Swift runtime.

[https://news.ycombinator.com/item?id=10725707](https://news.ycombinator.com/item?id=10725707)
[https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mo...](https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mon-20151207/001948.html)

~~~
pcwalton
Well, for Objective-C compatibility, and maybe for interfaces/protocols
(depending on whether they use fat pointers or not), they need that
capability. But Swift tries to push you into C++-like vtables when possible.

