Почему можно написать инициализатор UICollectionViewDiffableDataSource таким странным способом?

#swift #closures #uicollectionviewdiffabledatasource

Вопрос:

Изучая Swift с помощью учебника Пола Хадсонса, я столкнулся с чем-то странным.

Инициализатор UICollectionViewDiffableDataSource определяется как:

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

Насколько я могу судить, другого инициализатора нет. Однако Пол успешно инициализирует его таким образом, опуская аргумент cellProvider:

 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)
    }
}
 

Между тем, учебник Рэя Вендерлиха сделал бы это так:

 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)
    }
})
 

Я пытаюсь понять, какая быстрая «магия» происходит за спиной Пола, поскольку ему, похоже, сходит с рук отказ от аргумента cellProvider и вместо этого он делает какую-то странную вещь с закрытием. Какие именно правила Swift он здесь применяет?

Комментарии:

1. Я рекомендую прочитать этот раздел руководства Swift.

Ответ №1:

В вашем случае вы видите два способа, как вы можете написать то же самое с другим синтаксисом.

Здесь вы используете все параметры ожидаемым образом с двумя параметрами CollectionView и 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)
    }
})
 

И здесь вы используете второй параметр (cellProvider:) в качестве конечного закрытия после скобок с параметрами, кроме последнего.

 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)
    }
}
 

Видите, что это одно и то же в обоих примерах:

 { 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)
    }
 

Подробнее о конечных замыканиях или о том, что такое синтаксис конечных замыканий?

Ответ №2:

Спасибо всем, кто направил меня в правильном направлении. Это может помочь увидеть дополнительные более сжатые примеры:

 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)
}
 

Комментарии:

1. Отличная работа. Еще одна маленькая вещь, удалите точки с запятой из вашего закрытия завершения. 🙂