SlideShare a Scribd company logo
Advanced CoreData
CocoaHeads Moscow
Сергей Пронин
Full-stack developer
CTO, Senior Dev, Co-Founder

App in the Air
Senior Developer

Empatika
Преподаватель

НИУ-ВШЭ

Департамент Программной Инженерии
App in the Air
http://appintheair.mobi/
Advanced CoreData
• CoreData stack set-ups
• High-performance Concurrency
• Migration
• Runtime models
CoreData stack set-ups
Default
• Предлагается Xcode при
создании проекта
• Самый простой в понимании
• Вся работа проходит через
Main (UI) thread
• Подходит для маленьких
проектов с примитивным
хранением Store
Coordinator
Context
Main
Nested Simple
• I/O операции делаются
асинхронно через
PrivateConcurrency context
• Mid-level проекты, где данные
нужны в момент доступа
• Стандартный MagicalRecord
работает таким образом
• Main thread страдает при
больших объемах данных
Context
Private
Store
Coordinator
Context
Main
Nested Workers
• I/O операции делаются
асинхронно через
PrivateConcurrency context
• Одна из самых популярных
сборок вложенных контекстов
асинхронности
• Ведет себя лучше, чем Nested
Simple на больших объемах
• Изменения из workers все
равно проходят через Main
workers
Context
Private
Store
Coordinator
Context
Main
Context
Private
Context
Private
High-Performance
Concurrent Classic
• I/O операции делаются асинхронно
через PrivateConcurrency context
• Ведет себя почти идеально на
больших объемах
• Изменения из worker не касаются
Main
• Нужно “затягивать” изменения
• Записи из Main синхронны — нужно
сохранять большие объемы отдельно
worker
Store
Coordinator
Context
Private
Context
Main
High-Performance
Concurrent Modified
• I/O операции делаются асинхронно
через PrivateConcurrency
context
• Ведет себя почти идеально на
больших объемах
• Изменения из worker не касаются
Main
• Нужно “затягивать” изменения через 

NSManagedObjectContextDidSaveNotification
• Записи Main асинхронны — удобство
использования
worker
Context
Private
Store
Coordinator
Context
Private
Context
Main
Performance
All Off-main Main
Default 4.2s 0.0s 4.2s
Nested

simple
18.647s 15.637s 3.010s
Nested

workers
18.927s 15.85s 3.077s
HP Classic 21.05s 20.9s 0.158s
Материалы
1. The concurrent Core Data Stack
2. Backstage with Nested Managed Object
Contexts
3. Concurrent Core Data Stacks – Performance
Shootout
4. Не нужно бояться CoreData
Migration
Light-weight migration
NSMigratePersistentStoresAutomaticallyOption: true

NSInferMappingModelAutomaticallyOption: true
• Удаление entity, отношения или атрибута
• Переименование с renamingIdentifier
• Добавление атрибута
• Изменение иерархии
Heavy-weight migration
• Все, что не подходит под light-weight миграцию
• Изменение типа атрибута

NSMigrationManager — отвечает за миграцию,
принимает две модели (“из” и “в”)
NSMappingModel — хранит набор NSEntityMapping
NSEntityMapping — соотношение двух
NSEntityDescription между которыми будет маппинг
NSEntityMigrationPolicy — “политика” миграции,
обрабатывает преобразование каждого объекта
NSEntityMapping
let eMapping = NSEntityMapping()
eMapping.mappingType = .TransformEntityMappingType
eMapping.entityMigrationPolicyClassName = “MyEntityMigrationPolicy"
eMapping.sourceEntityName = fromEntity.name
eMapping.sourceEntityVersionHash = fromEntity
eMapping.sourceExpression = NSExpression(format: MANTRA)
eMapping.destinationEntityVersionHash = toEntity.versionHash
eMapping.destinationEntityName = toEntity.name
let idMapping = NSPropertyMapping()
idMapping.name = "_id"
idMapping.valueExpression = NSExpression(format: 

“FUNCTION($source, "valueForKey:", ”_id")")
eMapping.attributeMappings = [idMapping]
mappings.append(entityMapping)
MANTRA
”FETCH(

FUNCTION($manager, "fetchRequestForSourceEntityNamed:predicateString:",

"(fromEntity.name)", "TRUEPREDICATE"),

FUNCTION($manager, "sourceContext"), 

NO)"
NSMappingModel
let mappingModel = NSMappingModel()
//...
mappingModel.entityMapping = mappings
NSMigrationManager
let migrationManager = NSMigrationManager(

sourceModel: sourceModel,

destinationModel: destinationModel)
//...
migrationManager.migrateStoreFromURL(store1URL,

type: NSSQLiteStoreType, options: nil,

withMappingModel: mappingModel,

toDestinationURL: store2URL,

destinationType: NSSQLiteStoreType,

destinationOptions: nil, 

error: &error)
NSEntityMigrationPolicy

subclass
func createDestinationInstancesForSourceInstance(

_ sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping,

manager: NSMigrationManager, error: NSErrorPointer) -> Bool {
let dInstance = NSEntityDescription

.insertNewObjectForEntityForName(mapping.destinationEntityName,

inManagedObjectContext: manager.destinationContext)
for (key, value) in dInstance.entity.propertiesByName {
let oldValue = sInstance.valueForKey(key as! String)
let convertedValue = convert(oldValue)
dInstance.setValue(convertedValue, forKey: key as! String)
}
manager.associateSourceInstance(sInstance,

withDestinationInstance: dInstance, forEntityMapping: mapping)
return true
}
Launch Arguments
-com.apple.CoreData.MigrationDebug 1

Описывает процесс миграции + ошибки
-com.apple.CoreData.SQLDebug 1 (3)

Логгирует настоящие SQL запросы на каждый
store
Материалы
1. Custom CoreData migration
2. Apple Guide: Core Data migration
Runtime Models
Runtime models
Advanced caching — write once, use anytime —
грамотно написанное runtime построение модели
позволяет полностью настраивать store с бекэнда
let entity = NSEntityDescription()
entity.name = “Person”
//если есть
entity.managedObjectClassName = “Person”
var properties = [NSAttributeDescription]()
let attribute = NSAttributeDescription()
attribute.name = “_id”
attribute.optional = false
attribute.indexed = true
attribute.type = .StringAttributeType
properties.append(attribute)
//...
entity.properties = properties
let model = NSManagedObjectModel()
model.entities = [entity]
let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: model)
Материалы
1. Simple Core Data model in Runtime
2. Generating Registered Runtime Classes
3. Mike Ash Blog: Runtime Ambassador
Key Points
• Экспериментировать со сборками CoreData
стеков, чтобы найти удачный вариант
• Сложная миграция — это просто
• Вся CoreData без труда строится в runtime,
позволяя делать “гибкие” модели с бекенда
Курс НИУ ВШЭ — Swift
Идёт прямо сейчас. Все материалы на русском языке.
https://itunes.apple.com/ru/course/razrabotka-ios-prilozenij/id941293188?l=en
iTunes U
Спасибо!

@spronin
sergey@pronin.me

More Related Content

Similar to Things you might have missed from CoreData

MSI In-Store Pickup Функционал & сложности
MSI In-Store Pickup Функционал & сложностиMSI In-Store Pickup Функционал & сложности
MSI In-Store Pickup Функционал & сложности
Magecom UK Limited
 
Aspect Oriented Approach
Aspect Oriented ApproachAspect Oriented Approach
Aspect Oriented Approach
Dmytro Chyzhykov
 
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Ontico
 
Метапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScriptМетапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScript
Timur Shemsedinov
 
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
Sergey Teplyakov
 
Вебинар "Оптимизация производительности мобильных веб-приложений"
Вебинар "Оптимизация производительности мобильных веб-приложений"Вебинар "Оптимизация производительности мобильных веб-приложений"
Вебинар "Оптимизация производительности мобильных веб-приложений"MobiDev
 
Где кончается react native? / Павел Кондратенко (Rambler&Co)
Где кончается react native? / Павел Кондратенко (Rambler&Co)Где кончается react native? / Павел Кондратенко (Rambler&Co)
Где кончается react native? / Павел Кондратенко (Rambler&Co)
Ontico
 
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
Омские ИТ-субботники
 
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоStanfy
 
Что нового в Visual Studio 2010 и .Net 4.0
Что нового в Visual Studio 2010 и .Net 4.0Что нового в Visual Studio 2010 и .Net 4.0
Что нового в Visual Studio 2010 и .Net 4.0
akrakovetsky
 
Lift, play, akka, rails part1
Lift, play, akka, rails part1Lift, play, akka, rails part1
Lift, play, akka, rails part1Eduard Antsupov
 
Толстая модель. История разработки ORM
Толстая модель. История разработки ORMТолстая модель. История разработки ORM
Толстая модель. История разработки ORM
Mikhail Shamin
 
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)ZFConf Conference
 
[jeeconf-2011] Java Platform Performance BoF
[jeeconf-2011] Java Platform Performance BoF[jeeconf-2011] Java Platform Performance BoF
[jeeconf-2011] Java Platform Performance BoFAleksey Shipilev
 
Alexander manuhin selenium_php_v2.0
Alexander manuhin selenium_php_v2.0Alexander manuhin selenium_php_v2.0
Alexander manuhin selenium_php_v2.0
matroskin1980
 
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
Ontico
 
Нагруженный поиск на Sphinx
Нагруженный поиск на SphinxНагруженный поиск на Sphinx
Нагруженный поиск на SphinxRoman Pavlushko
 

Similar to Things you might have missed from CoreData (20)

MSI In-Store Pickup Функционал & сложности
MSI In-Store Pickup Функционал & сложностиMSI In-Store Pickup Функционал & сложности
MSI In-Store Pickup Функционал & сложности
 
Aspect Oriented Approach
Aspect Oriented ApproachAspect Oriented Approach
Aspect Oriented Approach
 
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
 
Метапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScriptМетапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScript
 
Async
AsyncAsync
Async
 
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
 
Вебинар "Оптимизация производительности мобильных веб-приложений"
Вебинар "Оптимизация производительности мобильных веб-приложений"Вебинар "Оптимизация производительности мобильных веб-приложений"
Вебинар "Оптимизация производительности мобильных веб-приложений"
 
Где кончается react native? / Павел Кондратенко (Rambler&Co)
Где кончается react native? / Павел Кондратенко (Rambler&Co)Где кончается react native? / Павел Кондратенко (Rambler&Co)
Где кончается react native? / Павел Кондратенко (Rambler&Co)
 
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
2014-08-02 03 Дмитрий Шматко. Первые впечатления от Node.js
 
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел Тайкало
 
Что нового в Visual Studio 2010 и .Net 4.0
Что нового в Visual Studio 2010 и .Net 4.0Что нового в Visual Studio 2010 и .Net 4.0
Что нового в Visual Studio 2010 и .Net 4.0
 
Lift, play, akka, rails part1
Lift, play, akka, rails part1Lift, play, akka, rails part1
Lift, play, akka, rails part1
 
Sphinx
SphinxSphinx
Sphinx
 
Толстая модель. История разработки ORM
Толстая модель. История разработки ORMТолстая модель. История разработки ORM
Толстая модель. История разработки ORM
 
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)
ZFConf 2011: Толстая модель: История разработки собственного ORM (Михаил Шамин)
 
[jeeconf-2011] Java Platform Performance BoF
[jeeconf-2011] Java Platform Performance BoF[jeeconf-2011] Java Platform Performance BoF
[jeeconf-2011] Java Platform Performance BoF
 
Alexander manuhin selenium_php_v2.0
Alexander manuhin selenium_php_v2.0Alexander manuhin selenium_php_v2.0
Alexander manuhin selenium_php_v2.0
 
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)
 
Нагруженный поиск на Sphinx
Нагруженный поиск на SphinxНагруженный поиск на Sphinx
Нагруженный поиск на Sphinx
 

More from Sergey Pronin

App in the Air Internship 2018
App in the Air Internship 2018App in the Air Internship 2018
App in the Air Internship 2018
Sergey Pronin
 
PTA Ancillaries
PTA AncillariesPTA Ancillaries
PTA Ancillaries
Sergey Pronin
 
Департамент Программной Инженерии
Департамент Программной ИнженерииДепартамент Программной Инженерии
Департамент Программной Инженерии
Sergey Pronin
 
Swift School #4
Swift School #4Swift School #4
Swift School #4
Sergey Pronin
 
Swift School #3
Swift School #3Swift School #3
Swift School #3
Sergey Pronin
 
Swift School #2
Swift School #2Swift School #2
Swift School #2
Sergey Pronin
 
Swift School #1
Swift School #1Swift School #1
Swift School #1
Sergey Pronin
 
Empatika Design Hours
Empatika Design HoursEmpatika Design Hours
Empatika Design HoursSergey Pronin
 
Greenfield Feedback Squeek
Greenfield Feedback SqueekGreenfield Feedback Squeek
Greenfield Feedback SqueekSergey Pronin
 

More from Sergey Pronin (15)

App in the Air Internship 2018
App in the Air Internship 2018App in the Air Internship 2018
App in the Air Internship 2018
 
PTA Ancillaries
PTA AncillariesPTA Ancillaries
PTA Ancillaries
 
Департамент Программной Инженерии
Департамент Программной ИнженерииДепартамент Программной Инженерии
Департамент Программной Инженерии
 
Swift School #4
Swift School #4Swift School #4
Swift School #4
 
Swift School #3
Swift School #3Swift School #3
Swift School #3
 
Swift School #2
Swift School #2Swift School #2
Swift School #2
 
Swift School #1
Swift School #1Swift School #1
Swift School #1
 
Empatika Design Hours
Empatika Design HoursEmpatika Design Hours
Empatika Design Hours
 
Greenfield Feedback Squeek
Greenfield Feedback SqueekGreenfield Feedback Squeek
Greenfield Feedback Squeek
 
Squeek School #8
Squeek School #8Squeek School #8
Squeek School #8
 
Squeek School #7
Squeek School #7Squeek School #7
Squeek School #7
 
Squeek school #6
Squeek school #6Squeek school #6
Squeek school #6
 
Squeek School #5
Squeek School #5Squeek School #5
Squeek School #5
 
Squeek school 4
Squeek school 4Squeek school 4
Squeek school 4
 
Squeek School #3
Squeek School #3Squeek School #3
Squeek School #3
 

Things you might have missed from CoreData

  • 2. Сергей Пронин Full-stack developer CTO, Senior Dev, Co-Founder
 App in the Air Senior Developer
 Empatika Преподаватель
 НИУ-ВШЭ
 Департамент Программной Инженерии
  • 5.
  • 6. Advanced CoreData • CoreData stack set-ups • High-performance Concurrency • Migration • Runtime models
  • 8. Default • Предлагается Xcode при создании проекта • Самый простой в понимании • Вся работа проходит через Main (UI) thread • Подходит для маленьких проектов с примитивным хранением Store Coordinator Context Main
  • 9. Nested Simple • I/O операции делаются асинхронно через PrivateConcurrency context • Mid-level проекты, где данные нужны в момент доступа • Стандартный MagicalRecord работает таким образом • Main thread страдает при больших объемах данных Context Private Store Coordinator Context Main
  • 10. Nested Workers • I/O операции делаются асинхронно через PrivateConcurrency context • Одна из самых популярных сборок вложенных контекстов асинхронности • Ведет себя лучше, чем Nested Simple на больших объемах • Изменения из workers все равно проходят через Main workers Context Private Store Coordinator Context Main Context Private Context Private
  • 11. High-Performance Concurrent Classic • I/O операции делаются асинхронно через PrivateConcurrency context • Ведет себя почти идеально на больших объемах • Изменения из worker не касаются Main • Нужно “затягивать” изменения • Записи из Main синхронны — нужно сохранять большие объемы отдельно worker Store Coordinator Context Private Context Main
  • 12. High-Performance Concurrent Modified • I/O операции делаются асинхронно через PrivateConcurrency context • Ведет себя почти идеально на больших объемах • Изменения из worker не касаются Main • Нужно “затягивать” изменения через 
 NSManagedObjectContextDidSaveNotification • Записи Main асинхронны — удобство использования worker Context Private Store Coordinator Context Private Context Main
  • 13. Performance All Off-main Main Default 4.2s 0.0s 4.2s Nested
 simple 18.647s 15.637s 3.010s Nested
 workers 18.927s 15.85s 3.077s HP Classic 21.05s 20.9s 0.158s
  • 14. Материалы 1. The concurrent Core Data Stack 2. Backstage with Nested Managed Object Contexts 3. Concurrent Core Data Stacks – Performance Shootout 4. Не нужно бояться CoreData
  • 16. Light-weight migration NSMigratePersistentStoresAutomaticallyOption: true
 NSInferMappingModelAutomaticallyOption: true • Удаление entity, отношения или атрибута • Переименование с renamingIdentifier • Добавление атрибута • Изменение иерархии
  • 17. Heavy-weight migration • Все, что не подходит под light-weight миграцию • Изменение типа атрибута
 NSMigrationManager — отвечает за миграцию, принимает две модели (“из” и “в”) NSMappingModel — хранит набор NSEntityMapping NSEntityMapping — соотношение двух NSEntityDescription между которыми будет маппинг NSEntityMigrationPolicy — “политика” миграции, обрабатывает преобразование каждого объекта
  • 18. NSEntityMapping let eMapping = NSEntityMapping() eMapping.mappingType = .TransformEntityMappingType eMapping.entityMigrationPolicyClassName = “MyEntityMigrationPolicy" eMapping.sourceEntityName = fromEntity.name eMapping.sourceEntityVersionHash = fromEntity eMapping.sourceExpression = NSExpression(format: MANTRA) eMapping.destinationEntityVersionHash = toEntity.versionHash eMapping.destinationEntityName = toEntity.name let idMapping = NSPropertyMapping() idMapping.name = "_id" idMapping.valueExpression = NSExpression(format: 
 “FUNCTION($source, "valueForKey:", ”_id")") eMapping.attributeMappings = [idMapping] mappings.append(entityMapping) MANTRA ”FETCH(
 FUNCTION($manager, "fetchRequestForSourceEntityNamed:predicateString:",
 "(fromEntity.name)", "TRUEPREDICATE"),
 FUNCTION($manager, "sourceContext"), 
 NO)"
  • 19. NSMappingModel let mappingModel = NSMappingModel() //... mappingModel.entityMapping = mappings
  • 20. NSMigrationManager let migrationManager = NSMigrationManager(
 sourceModel: sourceModel,
 destinationModel: destinationModel) //... migrationManager.migrateStoreFromURL(store1URL,
 type: NSSQLiteStoreType, options: nil,
 withMappingModel: mappingModel,
 toDestinationURL: store2URL,
 destinationType: NSSQLiteStoreType,
 destinationOptions: nil, 
 error: &error)
  • 21. NSEntityMigrationPolicy
 subclass func createDestinationInstancesForSourceInstance(
 _ sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping,
 manager: NSMigrationManager, error: NSErrorPointer) -> Bool { let dInstance = NSEntityDescription
 .insertNewObjectForEntityForName(mapping.destinationEntityName,
 inManagedObjectContext: manager.destinationContext) for (key, value) in dInstance.entity.propertiesByName { let oldValue = sInstance.valueForKey(key as! String) let convertedValue = convert(oldValue) dInstance.setValue(convertedValue, forKey: key as! String) } manager.associateSourceInstance(sInstance,
 withDestinationInstance: dInstance, forEntityMapping: mapping) return true }
  • 22. Launch Arguments -com.apple.CoreData.MigrationDebug 1
 Описывает процесс миграции + ошибки -com.apple.CoreData.SQLDebug 1 (3)
 Логгирует настоящие SQL запросы на каждый store
  • 23. Материалы 1. Custom CoreData migration 2. Apple Guide: Core Data migration
  • 25. Runtime models Advanced caching — write once, use anytime — грамотно написанное runtime построение модели позволяет полностью настраивать store с бекэнда
  • 26. let entity = NSEntityDescription() entity.name = “Person” //если есть entity.managedObjectClassName = “Person” var properties = [NSAttributeDescription]() let attribute = NSAttributeDescription() attribute.name = “_id” attribute.optional = false attribute.indexed = true attribute.type = .StringAttributeType properties.append(attribute) //... entity.properties = properties let model = NSManagedObjectModel() model.entities = [entity] let coordinator = NSPersistentStoreCoordinator( managedObjectModel: model)
  • 27. Материалы 1. Simple Core Data model in Runtime 2. Generating Registered Runtime Classes 3. Mike Ash Blog: Runtime Ambassador
  • 28. Key Points • Экспериментировать со сборками CoreData стеков, чтобы найти удачный вариант • Сложная миграция — это просто • Вся CoreData без труда строится в runtime, позволяя делать “гибкие” модели с бекенда
  • 29. Курс НИУ ВШЭ — Swift Идёт прямо сейчас. Все материалы на русском языке. https://itunes.apple.com/ru/course/razrabotka-ios-prilozenij/id941293188?l=en iTunes U