1

While learning Swift via Paul Hudsons's tutorial, I came across something strange.

The initializer of UICollectionViewDiffableDataSource is defined as:

public init(collectionView: UICollectionView, cellProvider: @escaping UICollectionViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider)

There is no other initializer as far as I can tell. However, Paul successfully initializes it like this, leaving out the cellProvider argument:

dataSource = UICollectionViewDiffableDataSource<Section, App>(collectionView: collectionView) { collectionView, indexPath, app in
    switch self.sections[indexPath.section].type {
    case "mediumTable":
        return self.configure(MediumTableCell.self, with: app, for: indexPath)
    case "smallTable":
        return self.configure(SmallTableCell.self, with: app, for: indexPath)
    default:
        return self.configure(FeaturedCell.self, with: app, for: indexPath)
    }
}

Meanwhile, Ray Wenderlich's tutorial would do it like this:

dataSource = UICollectionViewDiffableDataSource<Section, App>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, app) -> UICollectionViewCell? in
    switch self.sections[indexPath.section].type {
    case "mediumTable":
        return self.configure(MediumTableCell.self, with: app, for: indexPath)
    case "smallTable":
        return self.configure(SmallTableCell.self, with: app, for: indexPath)
    default:
        return self.configure(FeaturedCell.self, with: app, for: indexPath)
    }
})

I'm trying to understand what kind of Swift "magic" is going on behind Paul's way, as he seems to be getting away with dropping the cellProvider argument and instead doing some funky closure thing. Which Swift rules has he been applying here exactly?

codingChicken
  • 191
  • 12
  • 1
    I recommend reading [this section](https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID102) of the Swift guide. – Sweeper Mar 08 '21 at 10:04

2 Answers2

1

In your case you see two ways how you can write the same with a different syntax.

Here you are using all parameters in expected way with two parameters collectionView and cellProvider.

dataSource = UICollectionViewDiffableDataSource<Section, App>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, app) -> UICollectionViewCell? in
    switch self.sections[indexPath.section].type {
    case "mediumTable":
        return self.configure(MediumTableCell.self, with: app, for: indexPath)
    case "smallTable":
        return self.configure(SmallTableCell.self, with: app, for: indexPath)
    default:
        return self.configure(FeaturedCell.self, with: app, for: indexPath)
    }
})

And here you are using the second parametr (cellProvider:) as a trailing closure after the brackets with parameters except the last one.

dataSource = UICollectionViewDiffableDataSource<Section, App>(collectionView: collectionView) { collectionView, indexPath, app in
    switch self.sections[indexPath.section].type {
    case "mediumTable":
        return self.configure(MediumTableCell.self, with: app, for: indexPath)
    case "smallTable":
        return self.configure(SmallTableCell.self, with: app, for: indexPath)
    default:
        return self.configure(FeaturedCell.self, with: app, for: indexPath)
    }
}

See that this is the same in both examples:

{ collectionView, indexPath, app in
    switch self.sections[indexPath.section].type {
    case "mediumTable":
        return self.configure(MediumTableCell.self, with: app, for: indexPath)
    case "smallTable":
        return self.configure(SmallTableCell.self, with: app, for: indexPath)
    default:
        return self.configure(FeaturedCell.self, with: app, for: indexPath)
    }

More about the Trailing Closures or What is trailing closure syntax?

Carrione
  • 55
  • 1
  • 5
0

Thanks to everyone who's pointed me in the right direction. It may help to see additional more condensed examples:

func executeThis(info: String, completion: ((String) -> Void)) {
    completion(info)
}

let completion: ((String) -> Void) = { value in 
    print("Done: " + value)
}

// Pass the closure variable to the completion param:
executeThis(info:"Some Activity", completion:completion)

// Provide the closure directly as an argument:
executeThis(info:"Some Activity 2", completion: { value in
    print("Done: " + value)
})

// Trailing closure: the last param, which is a closure, is implicitly provided via "{ value in"
executeThis(info:"Some Activity 3") { value in
    print("Trailing closure done: " + value)
}
codingChicken
  • 191
  • 12