Swifty Tips ⚡️ (Перевод)
Когда я начал разрабатывать на IOS, мне всегда было интересно узнать о передовых методах, используемых гигантскими компаниями. Как выглядит их структура проекта? Какую архитектуру они используют? Какие библиотеки сторонних разработчиков наиболее популярны? Это моё желание опираться на опыт других людей и не тратить время на проблемы, которые уже решены.
Прошло 4 года. Я работал со многими клиентами и имел много умных людей в своей команде, чтобы обсуждать эти методы. Поэтому в этой статье я хочу рассказать о не очень очевидных практиках, которые я использую сейчас для IOS разработки.
Вы также можете их использовать, критиковать или улучшать.
Давайте начнем. 🚀
1 - Избегать чрезмерного использования ссылочных типов
Вы должны использовать только ссылочные типы для живых объектов. Что я подразумеваю под «живыми»? Давайте рассмотрим ниже пример:
struct Car {
let model: String
}
class CarManager {
private(set) var cars: [Car]
func fetchCars()
func registerCar(_ car: Car)
}
Это мертвый код, который предоставляет данные и имеет просто значение. Он ничем не управляет. Так что это не должно жить и, поэтому, его не надо определять как ссылочный тип.
С другой стороны, CarManager должен быть активным объектом. Поскольку это объект, который запускает сетевой запрос, ждёт для ответа и сохраняет все загруженные машины. Вы не можете выполнить какое-либо асинхронное действие для типа значения, потому что, опять же, он мертв. Сам объект CarManager, который живёт в сфере действия, забирает автомобили с сервера и регистрирует новые автомобили, как настоящий менеджер в реальной компании.
Эта тема заслуживает собственного блога, поэтому я не буду углубляться. Но я рекомендую посмотреть эту ссылку автора Andy Matuschak или презентацию на WWDC, чтобы понять, почему это так важно для создания приложений, защищенных от вирусов.
2 - Никогда не использовать не явно развернутые свойства
Вы не должны использовать не явно развернутые свойства по умолчанию. Вы даже можете забыть их в большинстве случаев. Но могут быть некоторые особые случаи, когда вам нужно это решение, чтобы порадовать компилятор. И важно понять логику, лежащую в основе этого.
В принципе, если свойство должно быть nil во время инициализации, то позже ему будет присвоено значение. Вы можете определить это свойство как не явно развернутое и никогда не получите доступ к нему до того, как код будет установлен. Если не хотите, чтобы компилятор предупреждал вас о том, что он равен nil.
Предположим, у нас есть nameLabel outlet.
class SomeView: UIView {
@IBOutlet let nameLabel: UILabel
}
Если вы напишете это так, компилятор попросит вас определить инициатор и присвоить none-nil значение к nameLabel. Это совершенно нормально, потому что вы утверждаете, что SomeView всегда имеет nameLabel. Но вы не можете этого сделать, потому что привязка будет выполняться за кулисами в initWithCoder. Вы уверены, он не будет nil, так что нет необходимости делать проверку на nil. Но в тоже время, вы не можете (или не должны) его заполнять.
В этом случае вы определяете его как не явно развернутое свойство. Это похоже на подписание контракта с компилятором:
Вы: «Это никогда не будет nil, так что перестать предупреждать меня об этом». Компилятор: «ОК».
class SomeView: UIView {
@IBOutlet var nameLabel: UILabel!
}
Популярный вопрос: Должен ли я использовать не явно развертывание при извлечении ячейки из table view?
Не очень популярный ответ: Нет. Или по крайне мере выводить сообщение об ошибке:
guard let cell = tableView.dequeueCell(...) else {
fatalError("Cannot dequeue cell with identifier \(cellID)")
}
3 - Избегайте чрезмерного использования AppDelegate
AppDelegate — это не место для хранения в PersistentStoreCoordinator ваших глобальных объектов, вспомогательных функций и т.д. Это как любой класс, который реализует протокол. Забудьте об этом.
Я понимаю, что у вас есть важные дела в applicationDidFinishLaunching, но слишком легко выйти из-под контроля, когда проект растет. Всегда старайтесь создавать отдельные классы (и файлы) для управления различными видами обязанностей.
👎 Не стоит делать так:
let persistentStoreCoordinator: NSPersistentStoreCoordinator
func rgb(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor { ... }
func appDidFinishLaunching... {
Firebase.setup("3KDSF-234JDF-234D")
Firebase.logLevel = .verbose
AnotherSDK.start()
AnotherSDK.enableSomething()
AnotherSDK.disableSomething()
AnotherSDK.anotherConfiguration()
persistentStoreCoordinator = ...
return true
}
Разработчик в AppDelegate.swift
👍 Лучше сделать так:
func appDidFinishLaunching... {
DependencyManager.configure()
CoreDataStack.setup()
return true
}
4 - Избегайте чрезмерного использования параметров по умолчанию
Вы можете установить значение по умолчанию для параметров в функции. Это очень удобно, потому что иначе вы, в конечном итоге, создадите разные версии той же функции, что и ниже, просто для добавления синтаксиса.
func print(_ string: String, options: String?) { ... }
func print(_ string: String) {
print(string, options: nil)
}
С параметрами по умолчанию это становится:
func print(_ string: String, options: String? = nil) { ... }
Легко правда? Очень просто установить цвет по умолчанию для вашего пользовательского интерфейса, чтобы предоставить параметры по умолчанию для вашей функции или назначить тайм-аут по умолчанию для вашего сетевого компонента. Но, вы должны быть осторожны, когда дело доходит до зависимостей.
Давайте рассмотрим следующий пример:
class TicketsViewModel {
let service: TicketService
let database: TicketDatabase
init(service: TicketService,
database: TicketDatabase) { ... }
}
Использование цели в приложении:
let model = TicketsViewModel(
service: LiveTicketService()
database: LiveTicketDatabase()
)
Использование цели в тесте:
let model = TicketsViewModel(
service: MockTicketService()
database: MockTicketDatabase()
)
Несколько причин, по которым у вас есть протоколы для сервиса (TicketService) и базы данных (TicketDatabase), — это абстрагирование от любых конкретных типов. Это позволяет вам внедрять любую реализацию, которая вам нравится в TicketsViewModel. Поэтому, если вы введете LiveTicketService в качестве параметра по умолчанию в TicketsViewModel, это фактически сделает TicketsViewModel зависимым от LiveTicketService, который является конкретным типом. Это противоречит тому, что мы пытаемся достичь в первую очередь, верно?
Еще не уверены?
Изображение, в котором указаны цели приложений и тестов. TicketsViewModel обычно добавляется к обеим целям. Затем вы добавили бы реализацию LiveTicketService в целевое приложение, а реализацию MockTicketService — в тестовую. Если вы создадите зависимость между и MockTicketService и LiveTicketService, ваш тестовый объект не будет компилироваться, потому что он не должен знать о LiveTicketService!
5 - Использование переменных параметов
Потому что это круто, супер легко реализовать и мощно.
func sum(_ numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
sum(1, 2) // Returns 3
sum(1, 2, 3) // Returns 6
sum(1, 2, 3, 4) // Returns 10
6 - Использование нужных типов
Swift поддерживает внутренние типы, поэтому вы можете(должны) вставлять типы везде, где это имеет смысл.
👎 Не стоит делать так:
enum PhotoCollectionViewCellStyle {
case default
case photoOnly
case photoAndDescription
}
Вы никогда не будете использовать это перечисление вне PhotoCollectionViewCellStyle, поэтому нет смысла помещать его в глобальную область видимости.
👍 Лучше сделать так:
class PhotoCollectionViewCell {
enum Style {
case default
case photoOnly
case photoAndDescription
}
let style: Style = .default
// Implementation...
}
Это имеет больше смысла, потому что Style является частью PhotoCollectionViewCell и на 23 символа короче, чем PhotoCollectionViewCellStyle.
7 - По умолчанию идет финальная версия 🏁
По умолчанию классы должны быть окончательными, поскольку обычно они не предназначены для расширения.
8 - Больше имен в ваших константах
Знаете ли вы, что можете правильно называть свои глобальные константы вместо использования уродливых префиксов, таких как PFX или k?
👎 Не стоит делать так:
static let kAnimationDuration: TimeInterval = 0.3
static let kLowAlpha = 0.2
static let kAPIKey = "13511-5234-5234-59234"
👍 Лучше сделать так:
enum Constant {
enum UI {
static let animationDuration: TimeInterval = 0.3
static let lowAlpha: CGFloat = 0.2
}
enum Analytics {
static let apiKey = "13511-5234-5234-59234"
}
}
Мое личное предпочтение — использовать только С вместо Constant, потому что это достаточно очевидно. Вы можете выбрать, как пожелаете.
9 - Избегайте не правильного использования
_ является переменной заполнителем, которая содержит не используемые значения. Это способ сообщить компилятору «я не забочусь о значении», чтобы он не стал жаловаться.
👎 Не стоит делать так:
if let _ = name {
print("Name is not nil.")
}
Не обязательный элемент, вы можете проверить, пуст ли он. Вам не нужно вынимать все, если вам ничего не нужно.
👍 Лучше сделать так:
Nil-контроль:
if name != nil {
print("Name is not nil.")
}
Неиспользуемый возврат:
_ = manager.removeCar(car) // Returns true if successful.
Блоки завершения:
service.fetchItems { data, error, _ in
// Hey, I don't care about the 3rd parameter to this block.
}
10 - Избегайте двусмысленных имен методов
Это действительно относится к любому языку программирования, который должен понимать человек. Люди не должны прилагать дополнительные усилия для понимания того, что вы имеете в виду, уже трудно понять язык программирования!
Например, проверьте этот вызов метода:
driver.driving()
Что это на самом деле? Мои догадки были бы:
- Он обозначает водителя как движущую силу.
- Он проверяет, водит ли водитель, и возвращает true, если это так.
Если кому-то нужно увидеть реализацию, чтобы понять, что делает метод. Особенно, если вы работаете в команде, передавая старые проекты, вы будете читать больше, чем пишете код. Так что будьте предельно ясны, когда называете вещи, чтобы люди не страдали, понимая ваш код.
11 - Избегайте большого вывода информации в журнале
Прекратите печатать каждую маленькую ошибку или ответ, который вы получите. Шутки в сторону. Это эквивалентно тому, что не печатается вообще. Потому что в какой-то момент вы увидите, что ваше окно журнала скроллится от ненужной информации.
👍 Лучше сделать так:
- Используйте уровень журнала ошибок в используемых вами фреймворках.
- Используйте фреймворки ведения журнала (или реализуйте его самостоятельно), которые позволяют вам устанавливать уровни журналов. Некоторые популярные фреймворки: XCGLogger, SwiftyBeaver
- Прекратите использовать журнал в качестве основного источника для отладки. Xcode предоставляет мощные инструменты для этого. Проверьте этот пост в блоге, чтобы узнать больше.
12 - Избегайте отключения неиспользуемого кода
Прекратите комментировать фрагменты кода. Если вам это не нужно, просто удалите его! Так просто. Я никогда не решал проблему путем включения устаревшего кода. Поэтому очищайте свой беспорядок и делайте свой код читаемым.
Что, если я скажу тебе …
… что вы можете достичь большинства из них с помощью автоматизации? См. Статью Candost на «Использование SwiftLint и опасность для стремительных лучших практик».
Спасибо за прокрутку полностью!
Автор оригинала: Göksel Köksal Ссылка на оригинальную статью
- Колличество слов
- 2206
- Дата создания
- 06 мая 2017