Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 9362 сообщений
Cообщения с меткой

ios - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

AppCode 2016.2: новые рефакторинги и инспекции, live templates, улучшения автодополнения кода, и все это — про Swift

Пятница, 29 Июля 2016 г. 17:46 (ссылка)

Привет, Хабр!



Недавно вышел AppCode 2016.2, новый релиз нашей IDE для разработки под iOS/OSX. Под катом много гифок и размышлений об автоматизированных рефакторингах в Objective-C и Swift.







Swift



Introduce Variable



Introduce Variable (он же Extract Variable) — это локальный рефакторинг, который позволяет автоматически извлечь часть сложного выражения в локальную переменную. Конечно, это можно сделать и вручную. Напечатать название переменной, проставить ее тип, присвоить ей нужное выражение, вручную поправить все в коде, проконтролировать, что не забыл исправить каждое вхождение… Хорошая привычка, и по собственному опыту помню, при разработке под iOS она вырабатывается довольно быстро.



Правда в том, что эта работа рутинна, а рутинные задачи всегда лучше автоматизировать. Для Objective-C этот рефакторинг был реализован в AppCode достаточно давно. Кстати, интересным эффектом его частого использования становится нежелание самому писать названия переменных и вообще начинать строку кода с привычного “тип переменной — название переменной”.



Ведь можно сначала написать выражение — а потом автоматически выделить его в переменную, выбрав подсказку для ее названия или напечатав свое:







Для реализации Introduce Variable для Swift пришлось научиться обрабатывать огромное количество конструкций языка — ведь для извлечения переменной нам необходимо знать ее тип, и это всего лишь одно из условий.



По умолчанию всегда извлекается константа, а изменить let на var можно во всплывающем окне:







Ну и чтобы стало еще проще — опционально можно автоматически подставить тип переменной:







К слову, и руками заменять все вхождения в коде не нужно — AppCode это сделает сам:







Ошибки и предупреждения в окне редактора кода



Когда пишешь код, все ошибки и предупреждения хочется видеть сразу же. И в любом случае не после компиляции. В этой версии мы решили интегрировать SourceKit для отображения ошибок и предупреждений в редакторе:







А также дать возможность нашим пользователям использовать те же самые быстрые исправления, которые выдает Xcode:







Фактически, в AppCode SourceKit используется как внешний линтер для кода, а мы тем временем продолжаем работать над новыми возможностями кодогенерации, использующими платформу IntelliJ.



Проверка правописания



Казалось бы, опечатки в исходном коде или комментариях не так важны — ведь на работоспособность библиотеки или приложения они не оказывают абсолютно никакого влияния. Но что если проект становится публичным и все эти опечатки попадают в автоматически сгенерированную на основе исходного кода документацию? В этом случае возникает необходимость избегать их, а проверять вручную большой объем кода — неблагодарное занятие.



Теперь AppCode ищет опечатки как в комментариях к коду на Swift, так и в самом исходном коде — названиях переменных, классов и даже их частях, помогая быстро внести исправления:







В Objective-C, как и в C++, такая возможность тоже есть. Более того — это один из механизмов платформы IntelliJ, адаптированный под конкретный язык программирования. Подробнее про то, как в AppCode устроена работа с инспекциями кода, можно прочитать здесь.



Live Templates



Live Templates — это наши шаблоны исходного кода. Суть — дать возможность быстро вставить фрагмент исходного кода по аббревиатуре, аналог сниппетов в XCode. В чем различие? Например, с помощью Live Templates можно сделать вот так:







Кстати, для Objective-C аналогом является встроенный шаблон с аббревиатурой each:







В чем магия? В том, что поля ввода значений в AppCode можно сприптовать. Например, скопировать значение вводимого значения из одного поля в другое. Или — как в шаблоне for — сделать так, чтобы поле ввода искало в своей окрестности массивы, по которым можно проитерировать. Возможности не безграничны, но они есть — а подробнее про них можно прочитать вот тут.



Второе базовое отличие — возможность обернуть выделенный фрагмент исходного кода в какое-либо выражение (Surround With templates):







В AppCode 2016.2 мы добавили поддержку всех этих механизмов для Swift и сделали базовый набор шаблонов, аналогичный сниппетам в Xcode.



Поля ввода значений при автодополнении



В коде на Objective-C и Swift есть названия параметров — такова специфика языка. Вручную их вставлять неудобно, поэтому еще одна базовая возможность, добавленная в этот релиз, — это автоматическая вставка названий параметров и полей для ввода значений при автодополнении функций и методов в Swift:







Objective-C



Complete Statement



В числе типов автодополнения кода в продуктах JetBrains есть возможность быстро завершить конкретную конструкцию в коде: вставить точку с запятой (даже если курсор стоит отнюдь не в конце выражения), круглые или фигурные скобки и сразу же перейти к набору следующего выражения. В этой версии мы основательно доработали этот тип автодополнения, и теперь он работает вот так:







Документация



Нас очень давно просили сделать генерацию документационных комментариев для методов и функций в Objective-C, и большинство запросов ограничивалось просьбой дать функциональность, аналогичную VVDocumenter. И если бы в нашем понимании “правильно сделанная поддержка документации для Objective-C в AppCode” ограничивалась бы действием “вставить шаблон комментария с предзаполненным параметрами”, то мы бы давно это сделали — вот так:







Кстати, автодополнение названий параметров и документационных тэгов тоже работает:







Но этого мало. Документацию необходимо обновлять, и каждый, кто хоть однажды документировал свой проект целиком, знает, что заставить себя обновить однажды написанный комментарий значительно сложнее, чем написать его в первый раз. Поэтому мы реализовали обработку комментариев в коде так, чтобы Rename для названий параметров (как нам кажется, наиболее частый случай изменений) работал корректно:







Дополнительный бонус — сильно улучшилось отображение таких комментариев в окне Quick Documentation.



Рефакторинг Rename в смешанном коде



Swift — новый, активно развивающийся язык. Но еще долгое время он будет существовать бок о бок с Objective-C, и разработчики будут использовать их вместе. Поэтому мы говорим “рефакторинги в Swift”, а в реальности в большинстве случаев имеем в виду “рефаторинги в смешанном коде”. В AppCode уже достаточно давно есть Rename и для Swift, и для сущностей Objective-C, используемых из Swift. В этой версии мы работали над Rename для методов классов Swift, используемых из Objective-C.



Когда метод класса в Swift не содержит никаких дополнительных аттрибутов, переименовать его просто, т. к. название в Objective-C совпадает с названием метода в Swift:







Все становится намного интереснее, если у метода есть псевдоним, сделанный с помощью аннотации @objc. В этом случае при вызове Rename из Objective-C AppCode определит имя функции в Swift и менять будет именно его:







Другой интересный случай — наличие внешнего названия у параметра функции в Swift. Например, для функции function(extParam param:String) соответствующий метод в Objective-C будет называться functionWithExtParam. И такой случай мы тоже обрабатываем корректно:







Пока эти возможности реализованы только для методов и классов, в будущем планируем сделать то же самое для полей классов.



Форматирование кода



Про настройки форматирования кода в AppCode можно написать отдельную статью, но мы просто кратко отметим, что для Objective-C/C/C++ добавлены два новых предопределенных стиля форматирования — LLVM и LLDB.



Демо



Небольшое демо (на английском) с демонстрацией новых возможностей:







Все платформенные изменения отлично описаны в посте anastasiak2512, в нем же можно прочитать про новые возможности генерации кода для C++ (Generate operators и Generate definitions). Традиционно все они доступны в AppCode.



На этом все. Читайте о других возможностях продукта у нас на сайте, следите за обновлениями в нашем англоязычном блоге, и задавайте любые возникшие вопросы в комментариях к этому посту.
Original source: habrahabr.ru.

https://habrahabr.ru/post/306738/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Отзывчивый поиск для UITableView

Пятница, 29 Июля 2016 г. 17:31 (ссылка)

В этой статье я поделюсь с вами подходом к реализации поиска в DataSource UITableView при быстром вводе запроса пользователем, когда необходимо динамически формировать результат поиска на основании введенного текста в поисковую строку, не дожидаясь нажатия кнопки “Найти”.



Итак, у нас есть таблица с UISearchBar для поиска. DataSource’ом в данном примере будет выступать БД SQLite (но это также может быть внешний источник данных с обращением по API, например). БД содержит много записей (несколько тысяч), поиск по ней может идти порядка 0,5 секунд.



Для того, чтобы динамически формировать поисковую выдачу по мере ввода пользователем запроса, нужно реализовать метод -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText делегата UISearchBar:



- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
__weak ProductPickerTableViewController *weakSelf = self;

const char *label = "ru.rinatabidullin.unique.search";
dispatch_queue_t queue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
if ([searchText length]) {
weakSelf.searchProducts = [self.food productsBySearchPhrase:searchText];
weakSelf.isSearchNowProducts = YES;
}
else {
weakSelf.isSearchNowProducts = NO;
}

dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
});
});
}




DataSource нашего контроллера заполняем не на главном потоке, иначе получим притормаживание интерфейса. После того, как данные получены, обновляем табличное представление (всегда на главном потоке).



На первый взгляд выглядит все нормально. Но при быстром вводе запроса пользователем можно добиться падения приложения:









Ошибка, как правило, следующего содержания:



Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 3 beyond bounds [0 .. 2]'


Т. е. пока таблица перерисовывается, пользователь успевает ввести новый символ в строку поиска и DataSource обновляется.



Первое решение, какое мне пришло в голову — вызывать методы поиска (обновление DataSource) только если между вводами символов проходит, к примеру, 0,3 секунды. На просторах гитхаба была найдена реализация отменяемого блока:



//
// dispatch_cancelable_block.h
// sebastienthiebaud.us
//
// Created by Sebastien Thiebaud on 4/9/14.
// Copyright (c) 2014 Sebastien Thiebaud. All rights reserved.
//

typedef void(^dispatch_cancelable_block_t)(BOOL cancel);

NS_INLINE dispatch_cancelable_block_t dispatch_after_delay(NSTimeInterval delay, dispatch_block_t block) {
if (block == nil) {
return nil;
}

// First we have to create a new dispatch_cancelable_block_t and we also need to copy the block given (if you want more explanations about the __block storage type, read this: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6
__block dispatch_cancelable_block_t cancelableBlock = nil;
__block dispatch_block_t originalBlock = [block copy];

// This block will be executed in NOW() + delay
dispatch_cancelable_block_t delayBlock = ^(BOOL cancel){
if (cancel == NO && originalBlock) {
originalBlock();
}

// We don't want to hold any objects in the memory
originalBlock = nil;
};

cancelableBlock = [delayBlock copy];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
// We are now in the future (NOW() + delay). It means the block hasn't been canceled so we can execute it
if (cancelableBlock) {
cancelableBlock(NO);
cancelableBlock = nil;
}
});

return cancelableBlock;
}

NS_INLINE void cancel_block(dispatch_cancelable_block_t block) {
if (block == nil) {
return;
}

block(YES);
block = nil;
}




Используя эту реализацию отменяемого блока, можно переписать метод делегата UISearchBar в следующем виде:



- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
__weak ProductPickerTableViewController *weakSelf = self;

double searchDelay = 0.3;

if (self.searchBlock != nil) {
//We cancel the currently scheduled block
cancel_block(self.searchBlock);
}
self.searchBlock = dispatch_after_delay(searchDelay, ^{
//We "enqueue" this block with a certain delay. It will be canceled if the user types faster than the delay, otherwise it will be executed after the specified delay
if ([searchText length]) {
weakSelf.searchProducts = [weakSelf.food productsBySearchPhrase:searchText];
weakSelf.isSearchNowProducts = YES;
}
else {
weakSelf.isSearchNowProducts = NO;
}

dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
});
});
}




Теперь поиск работает без сбоев и падений приложения. Но есть небольшое подвисание пользовательского интерфейса:









Исправить этот недостаток поможет использование NSOperation:



- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
__weak ProductPickerTableViewController *weakSelf = self;

double searchDelay = 0.3;

if (self.searchBlock != nil) {
//We cancel the currently scheduled block
cancel_block(self.searchBlock);
}
self.searchBlock = dispatch_after_delay(searchDelay, ^{
//We "enqueue" this block with a certain delay. It will be canceled if the user types faster than the delay, otherwise it will be executed after the specified delay

[weakSelf.currentSearchOperation cancel];

weakSelf.currentSearchOperation = [NSBlockOperation blockOperationWithBlock:^{
if ([searchText length]) {
weakSelf.searchProducts = [weakSelf.food productsBySearchPhrase:searchText];
weakSelf.isSearchNowProducts = YES;
}
else {
weakSelf.isSearchNowProducts = NO;
}

dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
weakSelf.currentSearchOperation = nil;
});
}];

const char *label = "ru.rinatabidullin.unique.search";
dispatch_queue_t queue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[weakSelf.currentSearchOperation start];
});
});
}




В результате получаем отзывчивый по мнению пользователя поиск:






Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/306742/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Открыта регистрация на MBLTdev 16 — Международную конференцию мобильных разработчиков

Вторник, 26 Июля 2016 г. 12:43 (ссылка)





Дата третьей международной конференции для мобильных разработчиков MBLTdev назначена. Регистрация открыта!



Организаторы e-Legion и РАЭК, как и прежде, соберут российских и зарубежных гуру мобильной разработки, от Австралии до США. Программа будет поделена на несколько потоков: хардкорные доклады и семинары для mid+ разработчиков.



Как это было в прошлом году




  • 650 участников,

  • 40 спикеров из Wunderlist, Runkeeper, Spotify, Tinkoff, Rambler&Cо, Parallels, Microsoft и многих других компаний,

  • 2 потока докладов и семинаров для iOS, Android и Windows разработчиков,

  • бизнес-поток для собственников и руководителей.



Видеозапись конференции MBLTdev 15 доступна здесь.



Программа



Набор спикеров и наполнение треков в самом разгаре. Участвуйте в создании программы конференции — пишите в комментариях, доклады на какие темы вы хотели бы услышать.



Следите за обновлениями программы и новостями конференции на официальных страничках VK.com, Facebook и Twitter.



Заявки на доклады



Если у вас есть интересный опыт в мобильной разработке, о котором вы хотите рассказать публично — присылайте ваши заявки на np@e-legion.com В письме укажите предварительную тему и ключевые тезисы выступления.



Билеты



До 1 сентября вы можете приобрести Early Bird билет по самой низкой цене — 2500 руб. Подробности на официальном сайте.



До встречи на MBLTdev 16!



Организаторы конференции: e-Legion и РАЭК
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/306138/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Swift Features

Понедельник, 25 Июля 2016 г. 14:59 (ссылка)

В этой статье хотелось рассказать об особенностях и трудностях Swift, с которыми я столкнулся при первом знакомстве. Для написания статьи использовалась версия языка 2.0. Предполагается, что вы уже читали документацию и обладаете базовыми знаниями для разработки мобильного приложения.



Generic протоколы



Под этим термином я подразумеваю любые протоколы, в которых есть открытые typealias (associatedtype в Swift 2.2). В моем первом приложении на Swift было два таких протокола: (для примера я немного упростил их)



public protocol DataObserver {
typealias DataType
func didDataChangedNotification(data: DataType)
}

public protocol DataObservable {
typealias DataType
func observeData (observer: TObserver)
}


DataObservable отвечает за отслеживание изменения данных. При этом не важно, где эти данные хранятся (на сервере, локально или еще как). DataObserver получает оповещения о том, что данные изменились. В первую очередь нас будет интересовать протокол DataObservable, и вот его простейшая реализация.



public class SimpleDataObservable : DataObservable {
public typealias DataType = TData

private var observer: DataObserver?

public var data: DataType {
didSet {
observer?.didDataChangedNotification(data)
}
}

public init(data: TData) {
self.data = data
}

public func observeData(observer: TObserver) {
self.observer = observer
}
}


Тут все просто: сохраняем ссылку на последний observer, и вызываем у него метод didDataChangedNotification, когда данные по какой-то причине изменяются. Но погодите… этот код не компилируется. Компилятор выдает ошибку «Protocol 'DataObserver' can only be used as a generic constraint because it has Self or associated type requirements». Все потому, что generic-протоколы могут использоваться только для накладывания ограничений на generic-параметры. Т.е. объявить переменную типа DataObserver не получится. Меня такое положение дел не устроило. Немного покопавшись в сети, я нашел решение, которое помогает разобраться со сложившейся проблемой, и имя ему Type Erasure.



Это паттерн, который представляет собой небольшой обертку над заданным протоколом. Для начала введем новый класс AnyDataObserver, который реализует протокол DataObserver.



public class AnyDataObserver : DataObserver {
public typealias DataType = TData

public func didDataChangedNotification(data: DataType) {

}
}


Тело метода didDataChangedNotification пока оставим пустым. Идем дальше. Вводим в класс generic init (для чего он нужен расскажу чуть ниже):



public class AnyDataObserver : DataObserver {
public typealias DataType = TData

public func didDataChangedNotification(data: DataType) {

}

public init(sourceObserver: TObserver) {

}
}


В него передается параметр sourceObserver типа TObserver. Видно, что на TObserver накладываются ограничения: во-первых он должен реализовать протокол DataObserver, во-вторых его DataType должен в точности соответствовать DataType нашего класса. Собственно sourceObserver это и есть исходный observer-объект, который мы хотим обернуть. И наконец финальный код класса:



public class AnyDataObserver : DataObserver {
public typealias DataType = TData

private let observerHandler: TData -> Void

public func didDataChangedNotification(data: DataType) {
observerHandler(data)
}

public init(sourceObserver: TObserver) {
observerHandler = sourceObserver.didDataChangedNotification
}
}


Собственно тут и происходит вся «магия». В класс добавляется закрытое поле observerHandler, в котором хранится реализация метода didDataChangedNotification объекта sourceObserver. В самом методе didDataChangedNotification нашего класса мы просто вызываем эту реализацию.



Теперь перепишем SimpleDataObservable:



public class SimpleDataObservable : DataObservable {
public typealias DataType = TData

private var observer: AnyDataObserver?

public var data: DataType {
didSet {
observer?.didDataChangedNotification(data)
}
}

public init(data: TData) {
self.data = data
}

public func observeData(observer: TObserver) {
self.observer = AnyDataObserver(sourceObserver: observer)
}
}


Теперь код компилируется и прекрасно работает. Могу отметить, что некоторые классы из стандартной библиотеки Swift работают по схожему принципу (например AnySequence).



Тип Self



В определенный момент мне потребовалось ввести в проект протокол копирования:



public protocol CopyableType {
func copy() -> ???
}




Но что же должен возвращать метод copy? Any? CopyableType? Тогда при каждом вызове пришлось бы писать let copyObject = someObject.copy as! SomeClass, что не очень хорошо. В добавок к тому же этот код небезопасен. На помощь приходит ключевое слово Self.



public protocol CopyableType {

func copy() -> Self

}



Таким образом мы сообщаем компилятору, что реализация этого метода обязана вернуть объект того же типа, что и объект, для которого он был вызван. Тут можно провести аналогию с instancetype из Objective-C.



Рассмотрим реализацию этого протокола:



public class CopyableClass: CopyableType {
public var fieldA = 0
public var fieldB = "Field"

public required init() {

}

public func copy() -> Self {
let copy = self.dynamicType.init()

copy.fieldA = fieldA
copy.fieldB = fieldB

return copy
}
}




Для создание нового экземпляра используется ключевое слово dynamicType (получение ссылки на динамический объект-тип) и вызывается метод init (для гарантии того, что init без параметров действительно есть в классе, мы вводим его с ключевым словом required). После чего копируем в созданный экземпляр все нужные поля и возвращаем его из нашей функции.



Как только я закончил с копированием, возникла необходимость использовать Self еще в одном месте. Мне потребовалось написать протокол для View Controller, в котором бы был статический метод создания нового экземпляра этого самого View Controller.



Так как этот протокол никак напрямую не был связан с классом UIViewController, то я его сделал достаточно общим и назвал AutofactoryType:



public protocol AutofactoryType {
static func createInstance() -> Self
}




Попробуем использовать его для создания View Conotroller:



public class ViewController: UIViewController, AutofactoryType {
public static func createInstance() -> Self {
let newInstance = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")
return newInstance as! ViewController
}
}


Все бы хорошо, но этот код не скомпилируется: “Cannot convert return expression of type ViewController to return type 'Self'” Дело в том, что компилятор не может преобразовать ViewController к Self. В данном случае ViewController и Self — это одно и то же, но в общем случае это не так (например, при использовании наследования).



Как же заставить этот код работать? Для этого есть не совсем честный (по отношению к строгой типизации), но вполне рабочий способ. Добавим функцию:



public func unsafeCast(sourceValue: T) -> E {
if let castedValue = sourceValue as? E {
return castedValue
}

fatalError("Unsafe casting value \(sourceValue) to type \(E.self) failed")
}


Ее назначение — это преобразование объекта одного типа к другому типу. Если преобразование не удается, то функция просто завершается с ошибкой.



Используем эту функцию в createInstance:



public class ViewController: UIViewController, AutofactoryType {
public static func createInstance() -> Self {
let newInstance = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")
return unsafeCast(newInstance)
}
}


Благодаря автоматическому выводу типов, newInstance теперь преобразуется к Self (чего нельзя было сделать напрямую). Этот код компилируется и работает.



Специфичные расширения



Расширения типов в Swift не были бы такими полезными, если бы нельзя было писать специфичный код для разных типов. Возьмем, к примеру, протокол SequenceType из стандартной библиотеки и напишем для него такое расширение:



extension SequenceType where Generator.Element == String {

public func concat() -> String {
var result = String()

for value in self {
result += value
}

return result
}
}


В расширении введено ограничение на элемент последовательности, он должен быть типа String. Таким образом для любой последовательности, состоящей из строк (и только для них), можно будет вызвать функцию concat.



func test() {
let strings = [“Alpha”, “Beta”, “Gamma”]

//printing “AlphaBetaGamma”
print("Strings concat: \(strings.concat())")
}


Это позволяет значительную часть кода выносить в расширения, и вызывать его в нужном контексте, получая при этом все плюсы повторного использования.



Реализация методов протокола по умолчанию.



Реализация методов протокола по умолчанию.



public protocol UniqueIdentifierProvider {
static var uniqueId: String { get }
}


Как следует из описания, любой тип реализующий этот протокол, должен обладать уникальным идентификатором uniqueId типа String. Но если немного подумать, то становится понятно, что в рамках одного модуля для любого типа уникальным идентификатором является его название. Так давайте напишем расширение для нашего нового протокола:



extension UniqueIdentifierProvider where Self: UIViewController {
static var uniqueId: String {
get {
return String(self)
}
}
}


В данном случае ключевое слово Self используется для того, чтобы накладывать ограничения на объект-тип. Логика этого кода примерно следующая: «если этот протокол будет реализован классом UIViewController (или его наследником), то можно использовать следующую реализацию uniqueId». Это и есть реализация протокола по-умолчанию. На самом деле можно написать это расширение и без каких-либо ограничений:



extension UniqueIdentifierProvider {
static var uniqueId: String {
get {
return String(self)
}
}
}


И тогда все типы, реализующие UniqueIdentifierProvider, получат uniqueId “из коробки”.



extension ViewController: UniqueIdentifierProvider {
//Nothing
}

func test() {
//printing "ViewController"
print(ViewController.uniqueId)
}


Прелесть в том, что в классе может быть своя реализация этого метода. И в этом случае реализация по-умолчанию будет игнорироваться:



extension ViewController: UniqueIdentifierProvider {
static var uniqueId: String {
get {
return "I’m ViewController”
}
}
}

func test() {
//printing "I’m ViewController"
print(ViewController.uniqueId)
}


Явное указание Generic аргумента



В своем проекте я использовал MVVM, и за создание ViewModel отвечал метод:



public func createViewModel() -> TViewModel {
let viewModel = TViewModel.createIntsance()

//View model configurate

return viewModel
}




Соответственно, так он использовался:



func test() {
let viewModel: MyViewModel = createViewModel()
}


В данном случае в функцию createViewModel в качестве generic аргумента будет поставляться MyViewModel. Все благодаря тому, что Swift сам выводит типы из контекста. Но всегда ли это хорошо? На мой взгляд, это не так. В некоторых случаях может даже привести к ошибкам:



func test(mode: FactoryMode) -> ViewModelBase {
switch mode {
case NormalMode:
return createViewModel() as NormalViewModel
case PreviewMode:
return createViewModel() //забыли as PreviewViewModel
}
}




В первом case в метод createViewModel подставляется NormalViewModel.

Во втором мы забыли написать «as PreviewViewModel», из-за чего в метод createViewModel подставляется тип ViewModelBase (что в лучшем случае приведет к ошибке в runtime).



Значит, необходимо сделать указание типа явным. Для этого в createViewModel мы добавим новый параметр viewModelType типа TViewModel.Type. Type тут означает, что метод принимает в качестве параметра не экземпляр типа, а сам объект-тип.



public func createViewModel(viewModelType: TViewModel.Type) -> TViewModel {
let viewModel = viewModelType.createIntsance()

//View model configurate

return viewModel
}


После этого наш switch-case выглядит так:



func test(mode: FactoryMode) {
let viewModel: ViewModelBase?

switch mode {
case NormalMode:
return createViewModel(NormalViewModel.self)
case PreviewMode:
return createViewModel(PreviewViewModel.self)
}
}


Теперь В функцию createViewModel передается аргументы NormalViewModel.self и PreviewViewModel.self. Это объекты-типы NormalViewModel и PreviewViewModel. В Swift есть довольно странная особенность: если у функции один параметр, можно не писать self.



func test(mode: FactoryMode) {
let viewModel: ViewModelBase?

switch mode {
case NormalMode:
return createViewModel(NormalViewModel)
case PreviewMode:
return createViewModel(PreviewViewModel)
}
}




Но если аргументов два или больше, ключевое слово self необходимо.



P. S.



Надеюсь что данная статья окажется кому-то полезной. Так же планируется продолжение про Swift (и не только).
Original source: habrahabr.ru.

https://habrahabr.ru/post/306350/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<ios - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda