Паттерн Fabric Method
Фабричный метод
Фабричный метод порождает объекты одного и того же типа.
import Foundation import UIKit // Protocol for "Product" protocol AppleProduct { var name: String {get set} var screenSize: Double {get set} var price: Double {get set} func getProduct() -> String } // Protocl for "Creator" protocol AppleProductCreator { func createProduct() -> AppleProduct } // Class for "ConcreteProduct" class IPhoneProduct: AppleProduct { var name: String = "iPhone 6" var screenSize: Double = 4.7 var price: Double = 199.00 func getProduct() -> String { return "Apple product "(name)", with screen size in (screenSize) inch, at proce: $(price)" } } class IPadProduct: AppleProduct { var name: String = "iPad Air 3" var screenSize: Double = 10.1 var price: Double = 499.00 func getProduct() -> String { return "Apple product "(name)", with screen size in (screenSize) inch, at proce: $(price)" } } // Class for "ConcreteCreator" class IPhoneProductCreator: AppleProductCreator { static let sharedInstance:IPhoneProductCreator = IPhoneProductCreator() func createProduct() -> AppleProduct { return IPhoneProduct() } } class IPadProductCreator: AppleProductCreator { static let sharedInstance:IPadProductCreator = IPadProductCreator() func createProduct() -> AppleProduct { return IPadProduct() } }
Применение
let product1: AppleProduct = IPhoneProductCreator.sharedInstance.createProduct() print("Got a new device: " + product1.getProduct()) let product2: AppleProduct = IPadProductCreator.sharedInstance.createProduct() print("Got a new device: " + product2.getProduct())
Пример 2
protocol Currency { func symbol() -> String func code() -> String } class Euro : Currency { func symbol() -> String { return "€" } func code() -> String { return "EUR" } } class UnitedStatesDolar : Currency { func symbol() -> String { return "$" } func code() -> String { return "USD" } } enum Country { case unitedStates, spain, uk, greece } enum CurrencyFactory { static func currency(for country:Country) -> Currency? { switch country { case .spain, .greece : return Euro() case .unitedStates : return UnitedStatesDolar() default: return nil } } }
Применение
let noCurrencyCode = "No Currency Code Available" CurrencyFactory.currency(for: .greece)?.code() ?? noCurrencyCode CurrencyFactory.currency(for: .spain)?.code() ?? noCurrencyCode CurrencyFactory.currency(for: .unitedStates)?.code() ?? noCurrencyCode CurrencyFactory.currency(for: .uk)?.code() ?? noCurrencyCode
Pattern: Abstract Factory
Паттерн Абстрактная фабрика используется для обеспечения клиента набором родственных или зависимых объектов. «Семья» объектов, созданных на заводе-изготовителе определяются во время выполнения.
CoreData: Получение контекста
Получение контекста managedObjectContext в CoreData
Каждый объект хранимый в CoreData имеет ссылку на контекст
let context = someCoreDataItem.managedObjectContext
Паттерн Iterator
Пример
//: Playground - noun: Паттерн Iterator import UIKit struct BanksLevel{ let price:Double let time:UInt32 let comment:String } struct Levels { let arLevels: [BanksLevel] } struct LevelsIterator: IteratorProtocol { private var current = 0 private let levels: [BanksLevel] init(levels: [BanksLevel]) { self.levels = levels } mutating func next() -> BanksLevel? { defer { current += 1 } return levels.count > current ? levels[current] : nil } } extension Levels: Sequence { func makeIterator() -> LevelsIterator { return LevelsIterator(levels: arLevels) } }
Применение
let arLevels = Levels(arLevels: [ BanksLevel(price: 1.06740, time: 1, comment: "Первый банковский уровень"), BanksLevel(price: 1.06210, time: 2, comment: "Второй банковский уровень") ] ) for level in arLevels { print("Определён: \(level)") }
Пример 2
import UIKit class Item{ let name:String init(_ name:String){ self.name = name } } class IteratableItems{ var arItems: [Item] = [] init(items:[Item]){ self.arItems = items } } class ItemIterator: IteratorProtocol{ private var current = 0 private let arItems: [Item] init(items:[Item]){ self.arItems = items } func next()-> Item?{ defer { current += 1 } return arItems.count > current ? arItems[current] : nil } } extension IteratableItems: Sequence { func makeIterator() -> ItemIterator { return ItemIterator(items: arItems) } }
Используем:
let arItems = IteratableItems(items: [Item("One"),Item("Two"),Item("Three")]) for item in arItems{ print("item: \(item.name)") }
iOS: VIPER архитектура
Сделай настолько просто, насколько это возможно, но не проще.
(с)Альберт Эйнштейн
признаки хорошей архитектуры:
- сбалансированное распределение обязанностей между сущностями с жесткими ролями;
- тестируемость. Обычно вытекает из первого признака (без паники, это осуществимо при соответствующей архитектуре);
- простота использования и низкая стоимость обслуживания.
VIPER
Разделение обязанностей — это очень хорошо
View: отвечает за отображение данных на экране и оповещает Presenter о действиях
пользователя. Пассивен, сам никогда не запрашивает данные, только получает их от
презентера.
Interactor: содержит всю бизнес-логику, необходимую для работы текущего модуля.
Presenter: получает от View информацию о действиях пользователя и преображает ее
в запросы к Router’у, Interactor’у, а также получает данные от Interactor’a,
подготавливает их и отправляет View для отображения.
Entity: объекты модели, не содержащие никакой бизнес-логики.
Router: отвечает за навигацию между модулями.
Carthage: менеджер зависимостей
Carthage: менеджер зависимостей, как о нём говорят «простой и безжалостный»
carthage version
Cartfile — простой текстовый файл, который описывает зависимости Вашего проекта для Carthage, таким образом, он определяет, что устанавливать. Каждая строка в Cartfile указывает, откуда извлечь зависимость, имя зависимости, и при необходимости, какая версия зависимости используется. Cartfile аналогичный файлу Podfile в CocoaPods.
| Cartfile — создаём любым блокнотом
touch Cartfile
open -a Xcode Cartfile
github "CocoaLumberjack/CocoaLumberjack"
carthage update --platform iOS
iOS: как эмитировать неполадки с сетью в Simulator'e на XCode
Swift: Проверяем интернет соединение
Всегда в приложениях работающих с сетью стоит проверять наличие сети.
import Foundation import SystemConfiguration public class CheckInternet { class func isConnectedToNetwork() -> Bool { var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress)) zeroAddress.sin_family = sa_family_t(AF_INET) let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) } } var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0) if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false { return false } let isReachable = flags == .reachable let needsConnection = flags == .connectionRequired return isReachable && !needsConnection } }
if (CheckInternet.isConnectedToNetwork() == true) { ... // есть интернет } else{ ... // у вас нет интернета }
Swift: Animation
Анимацию элементов сделать довольно не сложно
Скрытие элементов
let step1 = 0.5 let step2 = 1.0 let step3 = 1.5 UIView.animate(withDuration: step1, animations: { self.labelDate.alpha = 0 self.view.layoutIfNeeded() }) UIView.animate(withDuration: step2, animations: { self.labelLevelUP.alpha = 0 self.view.layoutIfNeeded() }) UIView.animate(withDuration: step3, animations: { self.labelLevelDN.alpha = 0 self.view.layoutIfNeeded() })
Показ элементов
func showUI(){ let step1 = 1.0 let step2 = 2.0 let step3 = 3.5 UIView.animate(withDuration: step3, animations: { self.labelDate.alpha = 1 self.view.layoutIfNeeded() }) UIView.animate(withDuration: step2, animations: { self.labelLevelUP.alpha = 1 self.view.layoutIfNeeded() }) UIView.animate(withDuration: step1, animations: { self.labelLevelDN.alpha = 1 self.view.layoutIfNeeded() }) }
просто изменяем прозрачность, иногда можно использовать задержку, в основном потоке чтобы не заморачиваться с дочерними, вызвав функцию sleep(в секундах)
sleep(3)
Перемещение объектов
uiViewedObject.view.center = CGPoint(x,y)
Swift: Загружем из XIB файла
UIView
let cell = Bundle.main.loadNibNamed("MenuCellDefault", owner: self, options: nil)?.first as! MenuCellDefault
MenuCellDefault — имя xib файла
загрузка nib файла
такой вариант подходит для UIView производных, но не годится для UIViewController
UIViewController
let globalTasksVC = GlobalTasksVC(nibName: "GlobalTasksVC", bundle: nil) self.present(globalTasksVC, animated: true, completion: nil)
Swft: uiAlertView
Пример 1
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil)) self.present(alert, animated: true, completion: nil)
Пример 2
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default){ UIAlertAction in print("close") }) self.present(alert, animated: true, completion: { print("show AlertMessage") })
Swift: Unit Test
Тесты
Правила написания тестов:
- Тестируем одно поведение (один тест тестирует одно поведение)
для того чтобы протестировать свои классы приложения нужно добавить в начало файла с тестами
@testable import PrjoectNameApp
где PrjoectNameApp — имя вашего iOS приложения/проекта
Памятка по методам
Всегда ошибка
XCTFail(@"always failed")
Равенство базовых типов
let primitive1:Int = 5 let primitive2:Int = 5 XCTAssertEqual(primitive1, primitive2) }
Равенство с погрешностью базовых типов
let constOne:Float = 5.012 let constTwo:Float = 5.014 let accuracy:Float = 0.005 XCTAssertEqualWithAccuracy(constOne, constTwo, accuracy: accuracy)
—-
Проверка на nil
id foo = nil; XCTAssertNil(foo, @"pointer:%p", foo);
—-
Проверка с задержкой по времени
XCTestExpectation *expectation = [self expectationWithDescription:@"block not call"]; NSTimeInterval timeout = 1.0f; [expectation performSelector:@selector(fulfill) withObject:nil afterDelay:0.3f]; [self waitForExpectationsWithTimeout:timeout handler:nil];
Тест асинхронного взаимодействия
func testNetModel(){ var items:[Post] = [] let expect = expectation(description: "Test net Model") let model = NetModel() model.npGetList({ posts in items = posts expect.fulfill() }) waitForExpectations(timeout: 4.0, handler: { error in if let error = error { XCTFail(error.localizedDescription) } let isLoaded:Bool = (items.count > 1) XCTAssertTrue(isLoaded) if isLoaded == false { print("posts: \(items)") } }) }
—-
Swift: читаем куки
просто читаем куки
func readCookies(){ if let cookies = HTTPCookieStorage.shared.cookies { for cookie in cookies { print("\n\n\(cookie)\n\n") } } }
ставим куки к запросу
... let request = NSMutableURLRequest(url: url! as URL) let cookies = HTTPCookieStorage.shared.cookies for cookie in cookies! { request.setValue(cookie.value, forHTTPHeaderField: cookie.name) } webView.delegate = self webView.loadRequest(request as URLRequest)
Swift: выполнить код в основном потоке
DispatchQueue.main.async { let urlSmallImage = "https:" + smallImage.string! let url = NSURL(string: urlSmallImage) let data = NSData(contentsOf:url! as URL) self.profilePhoto.image = UIImage(data:data! as Data) }
Swift: преобразование опционального типа к строгому
(imageOption?.description)!
let imageOption = value[0].dictionary!["photo"]?.dictionary?["src"]?.dictionary?["small"] let imageSmall = "https:" + (imageOption?.description)!
Swift: Изменить цвет векторной картинки pdf
Часто требуется изменить цвет отрисовки контурной иконки, в Swift 3 это делается так
let logo = UIImage(named: "Home") let imageView = UIImageView(image:logo) imageView.image = imageView.image!.withRenderingMode(.alwaysTemplate) imageView.tintColor = UIColor.white self.navigationItem.titleView = imageView
Получить минимальную цену товара в разделе
function getMinPriceBySectionID($sectionID){ $CATALOG_ID = 4; $rsProducts = CIBlockElement::GetList( Array('CATALOG_GROUP_1' => 'ASC'), Array('IBLOCK_ID' => $CATALOG_ID, 'SECTION_ID' => $sectionID), false, Array('nTopCount' => 1), Array('IBLOCK_ID', 'ID', 'NAME', 'CATALOG_GROUP_1') ); $arProducts = $rsProducts->Fetch(); $price = $arProducts["CATALOG_PRICE_1"]; if (!empty( $price)) return App::priceFormat($price); else return false; }
UIButton: скруглёные края
layer.cornerRadius Number 10 layer.masksToBounds Boolean Yes
