Всем привет! Нет, это не очередной пост в стиле «знакомьтесь со Swift и его возможностями», а, скорее, небольшой экскурс в практические приложения и тонкости, где протоколоориентированная природа нового языка Apple позволяет делать красивые и удобные вещи.
Особый привет тем, кто заглянул под хабра-кат. В последнее время мне пришлось много разрабатывать в Swift, в то же время у меня большой опыт работы с объектом C и некоторое желание выражать некоторые вещи в коде, что, как я понял, можно реализовать гораздо проще и элегантнее на новом языке.
.
Внимание : статья не представляет собой подробные обучающие материалы, а представляет практические моменты, которые лично меня впечатлили и я решил ими поделиться; они, вероятно, покажутся знакомыми большинству читателей, поэтому, если кто-то хочет что-то добавить, прокомментируйте.
Чего ожидать в этой статье?
- Пара вводных предложений (плюс немного полезной библиотеки)
- Украшение дополнительного поведения класса с помощью расширения (небольшой код)
- Создаем повторно используемый элемент, используя протокол и реализацию по умолчанию (много кода)
- Протоколы и перечисления — могут быть удобны (средний код)
В чем сила протоколов?
Во-первых, как всем известно, механизм протоколов позволяет реализовать множественное наследование разных типов протоколов одним объектом.Наследование ограничивает нас тем, что в цепочку наследников на n-м шаге невозможно «внести» или «добавить» новое поведение, общее для какого-то другого объекта.
Во-вторых, Swift имеет возможность добавлять реализацию по умолчанию для указанного протокола.
Более того, протокол может иметь несколько реализаций по умолчанию в зависимости от класса или типа объекта, который его наследует. В-третьих, протокол может быть унаследован от протокола.
В-четвертых, протоколы могут наследоваться не только классами (Class), но и структурами (Struct) и перечислениями (Enum).
В-пятых, протоколы могут добавлять свойства.
В-шестых, вы можете добавить реализацию системных протоколов по умолчанию и при желании переопределить ее в определенном классе.
В заключение добавлю, что протоколы позволяют сделать код повторно используемым в разных классах и структурах.
В них можно реализовать общие задачи и включать их в качестве декораторов в те файлы, где они нужны.
Например, в каждом проекте есть необходимость обработать клик по UIView чтобы не писать каждый раз лишний код, сделайте свой класс Тапабельный (код - здесь ) Лично мне не хватает некоторых соглашений при наследовании протокола, чтобы унаследованные методы и свойства были четко видны (я слышал, что это существует в Ruby):
Я бы хотел, чтобы это было кнопка действия И шоуДействиеВью() были подставлены в автоматически созданную область.protocol FCActionProtocol { var actionButton: UIButton! {get set} func showActionView() } class FCController: FCActionProtocol { var actionButton: UIButton! // FCActionProtocol convenience func showActionView() {} }
Я подожду со Swift 3.0
Украшение дополнительного поведения класса с помощью Extension
Итак, от теории к практике: жизненный случай №1. Представим, что у нас есть логика цикла просмотра контроллера и логика передачи модели во представление.Неожиданно у нас появилось новое расширение контроллера, в котором нам нужно реализовать логику отображения почтового клиента.
С протоколами это легко: class MyViewController: UIViewController {
// a lot of code here
}
extension MyViewController: MFMailComposeViewControllerDelegate {
func showMailController() {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
let controller = MFMailComposeViewController()
controller.mailComposeDelegate = self
return controller // customize and set it here
}
// MARK: - MFMailComposeViewControllerDelegate
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {}
}
Я очень рад, что в Swift, в отличие от obj-c, можно расширить класс Мойвиевконтроллер указать новые наследуемые протоколы и реализовать их поведение.
Создаем повторно используемый элемент, используя протокол и реализацию по умолчанию.
Случай №2: недавно в приложении была одна и та же кнопка на двух экранах, что привело к одному и тому же сценарию — показу листа действий с действиями, на одном из которых показывался почтовый клиент. Технической задачей было реализовать протокол с реализацией и всей логикой внутри, чтобы степень сложности его подключения и зависимостей была минимальной.
Вот как выглядит код в проекте: protocol FCActionProtocol {
var actionButton: UIButton! {get set}
var delegateHandler: FCActionProtocolDelegateHandler! {get set}
mutating func showActionSheet()
func showMailController()
}
class FCActionProtocolDelegateHandler : NSObject, MFMailComposeViewControllerDelegate {
var delegate: FCActionProtocol!
init(delegate: FCActionProtocol) {
super.init()
self.delegate = delegate
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
}
extension FCActionProtocol {
mutating func showActionSheet() {
delegateHandler = FCActionProtocolDelegateHandler(delegate: self)
let actionController = UIAlertController(title: nil, message: nil, preferredStyle: .
ActionSheet) actionController.addAction(UIAlertAction(title: NSLocalizedString("ActionClear", comment: ""), style: .
Default) { (action) in }) actionController.addAction(UIAlertAction(title: NSLocalizedString("ActionWriteBack", comment: ""), style: .
Default) { (action) in self.showMailController() }) if let controller = self as? UIViewController { controller.presentViewController(actionController, animated: true) {} } } func showMailController() { if MFMailComposeViewController.canSendMail() { let controller = MFMailComposeViewController() controller.mailComposeDelegate = delegateHandler (self as! UIViewController).
navigationController!.
presentViewController(controller, animated: true, completion: nil)
}
}
}
Внимание! Идея кода заключается в том, что существует протокол FCAActionProtocol , который включает кнопку ( кнопка действия ) при нажатии отображается лист с действиями ( показатьActionSheet ).
Внутри при нажатии на элемент листа должен появиться почтовый клиент ( шоумэйлконтроллер ).
Чтобы не реализовывать логику и обработку этого вызова в классе, наследующем наш протокол, мы делаем реализацию по умолчанию внутри, используя некую абстрактную сущность.
делегатобработчик , который создается внутри нашего расширения, а методы делегата почтового клиента обрабатываются экземпляром класса FCActionProtocolDelegateHandler .
В результате сложность добавления этого многоразового листа действий такова: class FCMyController: FCActionProtocol {
var actionButton: UIButton! // convenience FCActionProtocol
var delegateHandler: FCActionProtocolDelegateHandler! // convenience FCActionProtocol
}
Вся логика внутри.
Нам нужно только инициализировать и добавить кнопку.
На мой взгляд, получилось красиво и лаконично.
Протоколы и перечисления — может быть удобно
Кейс №3: наша команда создала сервис по продаже авиабилетов онлайн.Мобильный клиент тесно взаимодействует с сервером, и существуют различные сценарии выполнения вызовов API. Разделим их условно на поиск, бронирование билетов и оплату.
Ошибка может возникнуть в каждом из этих процессов (на стороне сервера, на стороне клиента, протоколе связи, проверке данных и т. д.).
Если при бронировании или поиске 500-й с сервера еще ничего страшного не означает, то, например, при оплате данные с внутреннего сервера уже могли уйти на платежный шлюз и клиент не может просто так показывают ошибку, хотя его деньги могли быть списаны с банковского счета.
карты.
Здесь протоколы могут позволить создать довольно элегантный код: protocol Critical {
func criticalStatus() -> (critical: Bool, message: String)
}
enum Error {
case Search(code: Int)
case Booking(code: Int)
case Payment(code: Int)
}
extension Error : Critical {
func criticalStatus() -> (critical: Bool, message: String) {
switch self {
case .
Payment(let code) where code == 500:
return (true, "Please contact us, because your payment could proceed")
default:
return (false, "Something went wrong. Please try later.")
}
}
}
Теперь давайте попробуем наш код и оценим, насколько он плох: let error = Error.Payment(code: 500)
if error.criticalStatus().
critical {
print("callcenter will solve it")
}
Жаль, что на самом деле проект представлял собой огромный пласт Object-C с кучей хаков для совместимости со Swift.
Надеюсь, что следующие проекты удастся реализовать, используя все возможности языка.
P.s. Я надеюсь, что кто-то новый заинтересуется Swift и подходом к разработке на основе протоколов.
Возможно, кто-то из среднего звена отметит пару приемов, которые он не использовал.
А старшие не будут критиковать и поделятся в комментариях парочкой своих секретов и наработок.
Спасибо всем.
Теги: #Swift #протоколы #расширение #iOS #разработка мобильных устройств #Разработка мобильных приложений #Swift
-
Основные Моменты Shadowshopper
19 Oct, 24 -
Ария2С + Openwrt
19 Oct, 24 -
Знакомьтесь, Бадди
19 Oct, 24 -
Превью Max Payne 3 (Часть Вторая)
19 Oct, 24