Лекция 3. MVVM. CS193P Spring 2023.

Ниже представлен начальный фрагмент Лекции 3 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 3 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны на платной основе.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

В этой Лекции вы увидите, наверное, самое большое количество слайдов на протяжении всего семестра, но нам нужно изучить базовую архитектуру прежде, чем погрузиться в следующую часть демонстрационного приложения, которая будет освещаться во второй половине сегодняшней Лекции.
Есть вопросы, прежде чем я начну? Хорошо, нет.
Итак, Лекция номер 3.
У нас есть две очень большие темы для разговора: одна из них — MVVM, которая представляет собой архитектуру, парадигму дизайна, которую вы будете использовать для создания iOS приложений, вторая —  система ТИПов Swift:

Сегодня

MVVM

  • Парадигма конструирования

Система ТИПов Swift

  • structструктура
  • class — класс
  • protocol — протокол
  • “Don’t Care” ТИП (Generic) — “Не важно какой” ТИП (Дженерик)
  • enum — перечисления
  • functions — функции

Возвращаемся к Демо!

  • Применение MVVM к Memorize

В Swift есть разные ТИПы:  структура struct, протокол protocol и многое другое.
Итак, это две основные темы Лекции 3, а затем я вернусь к демонстрационному приложению, в которой мы начнем создавать логику игру Memorize, то есть что происходит, когда вы кликаете на карты.

Архитектура MVVM

Итак, что это за штука MVVM?
Надо сказать, что в Swift очень важно отделить логику и данные вашего приложения от пользовательского интерфейса (UI).
Это действительно очень важно.В некотором смысле, SwiftUI построен на той идее, что у вас будут ДАHHЫЕ и ЛОГИКА, которые связаны с тем, что делает ваше приложение, и еще у вас будет UI, который покажет это пользователю и будет взаимодействовать с пользователем как совершенно отдельная вещь. Так что это реально очень важно.
Та часть приложения, которая связана с ДАННЫМИ и ЛОГИКОЙ, например, в  нашем приложении Memorize это “что происходит, когда вы кликаете на карте?” или “карты лежат лицом вверх или лицом вниз?”, все это, мы называем Model (Моделью) нашего приложения. Вы услышите, что я использую это слово Model (Модель) сотни раз. Вся логика и данные “живут” в Model (Модели).

Всё, что мы делали до сих пор на этом курсе, был только UI и иногда мы будем называть его View, потому что это наш «взгляд», наш портал на Model (Модель).

Model и UI

Отделение «Логика и данных» от UI

  • SwiftUI очень серьезно относится к отделению логики и данных приложения от UI
  • Мы называем логику и данные нашей Model (Моделью)
  • Это может быть структура struct или SQL база данных или некоторый код машинного    обучения или многие другие вещи
  • Или любая комбинация таких вещей
  • UI — это “параметризованная” оболочка, которую “питает” и вызывает к “жизни” Model
  • Думайте о UI как о “визуальном” проявлении Model (Модели)
  • Model (Модель)  — это где “живут” такие вещи как isFaceUp и cardCount (a не @State в UI)
  • SwiftUI заботится о том, чтобы UI перестраивался всякий раз, когда Model (Модель) изменяется

Model (Модель) может быть одной структурой struct. Это может быть целая SQL база данных, полная всяких сущностей. Возможно, что это может быть какой-то код машинного обучения.
Это может быть  похоже на REST API для Интернета. Это может быть почти что угодно.
Наша Model (Модель) — концептуальная вещь, это не просто одна структура struct, хотя в нашем приложении Memorize это будет действительно одна структура struct, так как у нас очень простое маленькое стартовое приложение. Но я не хочу, чтобы вы думали, что ваша Model (Модель) не может быть более мощной и сложной.

Что касается UI части нашего приложения, то это на самом деле просто “параметризованная” оболочка, которую Model “кормит”. Одна из лучших фраз, которые я слышал о UI — это визуальное проявление Model, потому что Model — это то, чем реально является ваше приложение, это игра на запоминание Memorize, вот что это такое, так что вся эта логика игры находится в Model.
UI — это просто то, как вы показываете Model пользователю, то есть её визуальное проявление. Вам действительно хочется думать об этом именно так.
То, что мы разместили наши переменные isFaceUp и cardCount в @State, принадлежат Model. Всё это касается игры, в которую мы играем. Поэтому мы не хотим, чтобы они были в UI, и я постараюсь избавиться от них в UI, поместив их в нашу Model.

Одна из очень важных вещей, которые делает Swift, супер важная его обязанность, заключается в том, чтобы обеспечить отражение в UI любых изменений в Model. Model отображается в UI. И для этого у Swift есть огромная инфраструктура для этого. 
В свою очередь ВАМ НЕ нужно нести ответственность за то, что все, что есть в вашей Model, отображалось бы в UI. Swift всё это сделает за вас. Вам же просто нужно дать Swift несколько подсказок о том, что в Model влияет на ваш UI. Как только вы сделаете это единожды, дальше волноваться об этом не придется. 
Беспокоиться следует о том, чтобы разделить эти вещи: Model и UI.
Но если мы разделим Model и UI, как они будут между собой взаимодействовать? Потому что очевидно, что им нужно поговорить друг с другом.

Я свел это к трем различным способам коммуникации Model и UI. Возможно, существуют и другие способы, но большинство вещей уместилось бы в одной из этих трех категорий. 

Model и UI

Подключение Model к UI

Существует несколько вариантов подключения Model к UI …

  1. Редко, но Model может быть просто @State в View (это минимум разделения, его практически нет)
  2. Model может быть доступна только через “привратника” (gatekeeper) — класс classView Model” (полное разделение)
  3. Существует “View Model” класс class, но Model все еще доступна View напрямую (частичное разделение)

Почти всегда этот выбор зависит от сложности Model. . .

  • Model представляет собой SQL + struct(s) + что-то еще — скорее всего опция #2
  • Model представляет собой простейший кусок данных и не имеющая практически никакой логики — скорее всего опция #1
  • Что-то между опцией 1 и опцией 2 — опция #3
  • Сегодня мы будем говорить об опции #2 (полное отделение).
  • Мы называем такую архитектуру, которая подключает Model к UI таким образом, MVVM.
  • Model — View — ViewModel
  • Это основная архитектура для любого разумного по сложности SwiftUI приложения
  • Мы быстро взглянем на то, как использовать  #3 (частичное разделение) путем минимальных изменений MVVM.
  1. Редко, но один из способов, каким можно подключить Model к UI, заключается в том, что Model (Моделью) является @State в View. Я имею в виду, что если вы всю Model, всю логику в вашем приложении разместите в @State в вашем View, то, очевидно, View может получить к ней доступ. Это крайне минимальное разделение. Я бы, наверное, не стал этого делать.
  2. Давайте поговорим о том, что мы будем делать в ближайшее время. Номер 2 заключается в том, что Model доступна для UI только через “привратника” (Gatekeeper). У нас будет привратник (Gatekeeper), class ViewModel, и работа этого “парня” — поддерживать безопасную коммуникацию UI и Model. Это основной способ, который мы будем использовать в наших приложениях, и этот способ называется MVVM. Номер 2 мы будем использовать в 99% случаев при создании приложений
  3. Номер 3 является своего рода гибридом первых двух, то есть у нас есть этот привратник (gatekeeper), который мы называем ViewModel, но иногда у нас есть прямой доступ к Model. У нас будет public переменная var в Model, которая доступна UI и через неё UI на самом деле разговаривает с Model напрямую, прямо с игрой Memorize. По сути, это гибрид первых двух.

Итак, как узнать, какой из этих вариантов использовать? 

Читать далее

Лекция 2. Ещё больше SwiftUI. CS193P Spring 2023.

Ниже представлен начальный фрагмент Лекции 2 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Целиком конспект Лекции 2 можно прочитать на Google Doc здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

Иногда мне нравится начинать свои Лекции с возврата на предыдущую лекцию и доделывать всё, что забыл там сделать. Вы видели, как проходят Лекции —  мне нужно слишком многое вам рассказать. и иногда я слишком быстро что-то проскакиваю.

Режим Выбора (Selection)  в Preview


Итак, я хотел бы начать с возвращения к предыдущей Лекции и немного уточнить одну вещь, связанную с инспектором.
 

Помните, я говорил вам, что вы можете войти в режим Выбора (Selection) внизу вашего PreView.
В режиме Выбора (Selection) вы сможете выбирать элементы вашего UI или даже несколько элементов одновременно.

Пару замечаний об этом. Я говорил о том, почему это важно, ведь если вы умеете программировать, зачем вам это нужно? И я приводил примеры,  кому это нужно: локализаторам или дизайнерам вашего UI, которые могут и не быть программистами. 
Однако не значит, что это не имеет абсолютно никакой важности лично для вас. Это может быть хорошо для настройки определенных элементов UI.

И еще одна вещь, которую следует отметить по этому поводу. Я не думаю, что я упомянул о том, что этот режим Выбора (Selection) работает в обе стороны. Если я кликну на  строка кода, то будет выбраны (голубой рамкой) элементы UI, представляющие этот код:

И наоборот, если вы выберите элемент UI, то будет подсвечен код, представляющий этот элемент UI. Другими словами, дело не только в том, что ВЫ выбираете, а что выбирает XCode. Если вы выберете любой из двух вариантов, то будет выбран другой, поэтому я хочу, чтобы вы это поняли. 
И тогда как программисту, почему бы вам не повозиться с цвет или с некоторыми отступами или с чем-то еще в этом роде без необходимости постоянно печатать и редактировать исходный код. Это зависит от того, что вам нравится. Лично я люблю редактировать исходный код. Это как бы вытекает из моего мироощущения, поэтому мне нравится все делать в коде. Но некоторым людям нравится кликать на кнопки. И поэтому я просто не хотел принижать возможности Инспектора и режима Выбора (Selection). Это может быть интересным и значимым.
Итак, это было единственное, к чему из прошлой Лекции я хотел вернуться .

Настройки проекта  Project Settings 

Еще одна вещь, которую я хотел бы упомянуть.  Когда мы вышли на Навигатор в раздел Файлы, я сказал: “Посмотрите, есть три файла: MemorizeApp, ContentView и Assets. Но на самом деле есть еще одна вещь,  вы можете кликнуть на самой верхней строке иерархии, это что-то вроде файла, и увидеть настройки Project Settings вашего проекта. 

Читать далее

WWDC 2023. Новый фреймворк SwiftData для управления данными. Эксперименты

SwiftData дебютировал на WWDC 2023 в качестве замены фреймворка Core Data и обеспечивает постоянное хранение данных на Apple устройствах и беспрепятственную синхронизацию с облаком iCloud. Весь API SwiftData построен вокруг современного Swift.

Примечание. SwiftData является частью iOS 17, и на момент написания этой статьи мы имеем версии Xcode 15.0 и iOS 17.0 .

В SwiftData, в отличие от своего предшественника, базы данных Core Data, очень просто создать Схему (или Модель Данных) для постоянного хранения информации в вашем приложении. Для этого прямо в коде создаются обычные Swift классы class со свойствами, имеющими обычные базовые Swift ТИПы, ТИПы или другие Swift классы Схемы. Вы также можете использовать как Optional, так и НЕ-Optional ТИПы.

Чтобы превратить эти обычные Swift классы в постоянно хранимые объекты, Apple дала нам «волшебную палочку» в виде макросов, самым главным из которых является макрос @Model.

Если вы пометите макросом @Model обычные Swift классы, то получите не только постоянно хранимые объекты, но и сделаете их Observable, Hashable и Identifiable, и вам не нужно предпринимать никаких дополнительных усилий при использовали их в SwiftUI, ибо новый в iOS 17 протокол Observable обеспечит вам «живое» отображение на UI всех изменений ваших хранимых объектов, а Identifiable и Hashable позволят беспрепятственное использовать их в списках ForEach.

В SwiftData, в отличие от Core Data, нет никаких внешних файлов для Модели Данных и никакой «закулисной» генерации старых Objective-C классов, которые еще нужно адаптировать для использования в Swift. В SwiftData всё исключительно просто.

Кроме того, в SwiftData существенно, по сравнению с Core Data, упрощена выборка данных и отображение её результатов на UI. Для этого предназначена «обертка свойства» @Query, для которой вы можете указать предикат Predicate (то есть условия выборки данных) и сортировку результата SoreDescriptor. Новый мощный предикат Predicate выгодно отличается от старого предиката NSPredicate Core Data тем, что теперь вы можете задавать условия выборки данных, используя операции самого языка программирования Swift, а не какую-то замысловатую форматированную строку.

SwiftData дополнен такими современными возможностями как Swift многопоточность и макросы. В результате в Swift 5.9 мы получили, по определению самого Apple, “бесшовное” взаимодействие с постоянным хранилищем данных в нашем приложении. SwiftData совершенно естественным образом интегрируется в SwiftUI и прекрасно работает с CloudKit и Widgets.

Если вы начнете работать со SwiftData, то вообще не почувствуете даже «духа» Core Data, всё очень Swifty. Apple настаивает на том, что SwiftData — это совершенно отдельный от Core Data фреймворк, нам точно неизвестно, является ли SwiftData «оболочкой» Core Data, но даже если это так, то она настолько элегантно, интуитивно и мастерски реализована, что у вас будет ощущение работы исключительно в «родной» cреде языка программирования Swift.

В этом посте я покажу вам, как:

  • определить Схему данных в SwiftData, 
  • выполнить CRUD операции (Create — Создать, Read — прочитать, Update — модифицировать, Delete — удалить),;
  • сформировать различные запросы Query к данным с помощью предиката Predicate
  • использовать «живой» запрос @Query в SwiftUI и как его динамически настраивать,
  • эффективно «закачать» JSON данные в SwiftData хранилище без блокировки пользовательского интерфейса (UI).
Читать далее

Задание 6 Stanford CS 193P Fall 2017. Галерея изображений Image Gallery с постоянным хранением (persistent). Решение.

Содержание

Текст Домашнего задания на английском языке доступен на  iTunes в пункте “Programming: Project 6: Persistent Image Gallery″. На русском языке вы можете скачать Задание 6 здесь: «Задание VI: Галерея изображений Image Gallery с постоянным хранением (persistent)»

Для решения Задания 6 необходимо ознакомиться с Лекцией 7, Лекцией 11Лекцией 12 , Лекции 13 и Лекцией 14.

Цель этого Задания 6 — понять  работу FileManager, URL, Codable, UIDocument и UIDocumentBrowserViewController и изучить, как пользоваться iOS API полностью самостоятельно по документации.

Это Задание использует код, который вы создали в Задании 5, но, возможно, вы захотите начать новый Xcode проект “с нуля” (так что вы сможете использовать Document Base App шаблон).

Мое решение обязательных и допонительных пунктов Задания 6 находится на Github  для iOS 11 и на Github для iOS 12:

ImageGallery_6_Requied_OLD — сохранение Модели в файловой системе без UIDocumentBrowserViewController

ImageGallery_6_Requed_Browser— подключение UIDocumentBrowserViewController, но пока присутствует кнопка «Sav

ImageGallery_6_Requed_Browser_No_Save_Button — работает UIDocumentBrowserViewController, но НЕТ кнопки «Save», используется URLCache,

ImageGallery_6_Requed_Browser_No_Save_Button_LocalImage — присутствует UIDocumentBrowserViewController, НЕТ кнопки «Save«, используется URLCache, сохранение изображений, для которых нет URL, производится в локальной файловой системе, документ имеет свой  UTI .imagegallery.

Пункты 1, 2 обязательные

  1. Документы Image Gallery в вашем приложении теперь должны сохраняться постоянно.
  2. Вы можете полностью убрать поддержку таблицы UITableView, добавленную на прошлой неделе.

Приложение Image Gallery — это как раз такое приложение, которое естественно хочется превратить в приложение, основанное на документах, то есть Document based app. Потому что с помощью Image Gallery вы можете создавать тематические живописные Галереи Изображений, и определенно вы захотите их сохранить.

Читать далее

Задание 5 Stanford CS 193P Fall 2017. Галерея изображений Image Gallery. Решение обязательных пунктов. Часть 2.

Содержание

В Задании 5 вы должны освоить работу с Table View, Collection View, Scroll View и Text Fields, понять, как работает многопоточность (multithreading) и механизма Drag & Drop

Разработка Задания 5 начинается “с нуля”. Оно не имеет отношения к первым 4-м Заданиям этого семестра.

Текст Домашнего задания на английском языке доступен на  iTunes в пункте “Programming: Project 5: Image Gallery″. На русском языке вы можете скачать Задание 5 здесь: Задание 5.pdf.

Для решения Задания 5 необходимо ознакомиться с Лекцией 7, Лекцией 11Лекцией 12 и началом Лекции 13.

Центральной частью этого Задания 5 является коллекция изображений Collection View, которая моделирует Галерею Изображений Image Gallery. Необходимо обеспечить ее работоспособность, настроить ее параметры для отображения изображений в удобной и визуально привлекательной форме, подключить определенные жесты для операций над ее элементами, а также наделить ее механизмом Drag & Drop, который позволит добавлять изображения из других приложений, например, поисковой системы Google, и избавляться от ненужных элементов, кидая их в «Мусорный контейнер». В приложении предполагается создание целой серии таких тематических Галерей Изображений, каждой из которых будет дано имя, а список имен всех Галерей размещен в таблице Table View, которую тоже можно будет редактировать.

Логически выполнение обязательных пунктов Задания 5 распадается на две части: первая часть ( ей был посвящен пост «Задание 5 Stanford CS 193P Fall 2017. Галерея изображений Image Gallery. Решение обязательных пунктов. Часть 1.») будет обеспечивать работу одной Галереи Изображений Image Gallery и будет использовать исключительно коллекцию Collection View, а вторая часть будет обеспечивать работу со списком имен Галерей Изображений с помощью таблицы Table View, функционирование которой мы отработаем отдельно, а затем подстыкуем к ней коллекцию Collection View, уже настроенную под Галерею изображений Image Gallery.

Вторая часть будет представлена в этом посте.

Мое решение обязательных пунктов Задания 5 находится на Github для iOS 11 и на Github для iOS 12  в папке ImageGallery_V. Задание очень большое и сложное, поэтому я разместила код для нескольких последовательных этапов его выполнения, что позволит вам проверять ваш код на разных стадиях выполнения Задания 5:

  1. ImageGalleryOnly — работает только коллекция изображений Сollection View
  2. ImageGalleryRequiedTable — работает только таблица имен Table View
  3. ImageGalleryRequiedTwoSeguesSplit View Controller с двумя Segues для разных ПРОТОТИПОВ
  4. ImageGalleryRequiedGenericSegueSplit View Controller с одним Segue и ручным «переездом»
  5. ImageGalleryRequiedNoSegueSplit View Controller без Segue для iPad (наиболее комфортный для пользователя), это ОКОНЧАТЕЛЬНЫЙ ВАРИАНТ выполнения обязательных пунктов Задания 5.

Читать далее

Задание 5 Stanford CS 193P Fall 2017. Галерея изображений Image Gallery. Решение обязательных пунктов. Часть 1.

Содержание

В этом Задании вы должны освоить работу с Table View, Collection View, Scroll View и Text Fields, понять, как работает многопоточность (multithreading). 

Разработка этого Задания начинается “с нуля”. Оно не имеет отношения к первым 4-м Заданиям этого семестра.

Текст Домашнего задания на английском языке доступен на  iTunes в пункте “Programming: Project 5: Image Gallery″. На русском языке вы можете скачать Задание 5 здесь: Задание 5.pdf.

Для решения Задания 5 необходимо ознакомиться с Лекцией 11Лекцией 12 и началом Лекции 13.

Мое решение Задания 5 находится на Github для iOS 11 и на Github для iOS 12 в папке ImageGallery_V. Для этого поста это вариант ImageGalleryOnly.

Центральной частью этого Задания 5 является коллекция изображений Collection View, которая моделирует Галерею Изображений Image Gallery. Необходимо обеспечить ее работоспособность, настроить ее параметры для отображения изображений в удобной и визуально привлекательной форме, подключить определенные жесты для операций над ее элементами, а также наделить ее механизмом Drag & Drop, который позволит добавлять изображения из других приложений, например, поисковой системы Google, и избавляться от ненужных элементов, кидая их в «Мусорный контейнер». В приложении предполагается создание целой серии таких тематических Галерей Изображений, каждой из которых будет дано имя, а список имен всех Галерей размещен в таблице Table View, которую тоже можно будет редактировать.

Логически выполнение обязательных пунктов Задания 5 распадается на две части: первая часть ( ей посвящен этот пост) будет обеспечивать работу одной Галереи Изображений Image Gallery и будет использовать исключительно коллекцию Collection View, а вторая часть будет обеспечивать работу со списком имен Галерей Изображений с помощью таблицы Table View, функционирование которой мы отработаем отдельно, а затем подстыкуем к ней коллекцию Collection View, уже настроенную под Галерею изображений Image Gallery.

Вторая часть будет представлена в следующем посте.

Читать далее

Лекция 17 CS193P Fall 2017 — Core Motion & Camera.

Лекция 17 посвящена некоторых специфических API доступа к оборудованию (hardware) нашего устройства. Рассматриваются два API: это Core Motion, отвечающее, главным образом, за определение местоположения устройства в пространстве в процессе перемещения устройства, и Фотокамера (Camera), с помощью которой мы можем сделать фотографию и/или видео и разместить их в своем приложении.

Итак, CoreMotion — это объектно-ориентированное API для доступа к первичным датчикам движения на вашем устройстве. Core Motion поставляет в приложение данные первичных датчиков Apple приборов: акселерометра (accelerometer), гироскопа (gyro) и магнитометра (magnetometer).

Класс, с помощью которого вы можете получить всю информацию как с самих датчиков, так и о наличии датчиков в приборе, называется CMMotionManager. В лекции подробно рассматриваются различные режимы получения информации как с физических датчиков, так и с абстрактного комбинированного датчика CMDeviceMotion.

Датчики движения в iOS — удивительный механизм. И надо отдать должное инженерам Apple — подключение датчиков к приложению является предельно простым, а их дальнейшее использование не ограничивает вас ни в чём. Как и все приложения, дополненные реальностью, они захватывают в первую очередь тем, что дают вам в руки инструмент для преобразования информации об окружающем вас мире в виртуальный мир вашего телефона. Профессор показывает  демонстрационный пример игры в карты Playing Cards, дополняя ее элементами реального Мира, а именно реальным ускорением свободного падения, полученным с акселерометра. Эти показания датчика движения нашего устройства (iPhone или iPad) «подцепляются» к «поведению» гравитации UIGravityBehavior, действующему в динамическом аниматоре Dynamic Animator.  Читать далее

Лекция 16 CS193P Fall 2017 — Еще больше Segues.

Вся Лекция 16 посвящена Segues. Мы уже изучили на прошлых Лекциях Show segue и Show Detail segue, которые работают в Navigation Controllers и Split View Controllers.

На Лекции 16 изучаются все оставшиеся типы Segues: Modal, Unwind, Popover, Emded.

Сначала излагается теоретическая часть по каждому виду Segue, а затем для этого вида Segue следует небольшой демонстрационный пример, показывающий Segue в действии. В результате у профессора формируется блестящий демонстрационный пример, в котором несмотря на различия Segues — Modal и Popover адаптируют свое представление на iPhone, а функционирование Emded вообще не зависит от платформы, все Segues создают новые MVCs,  а Unwind — нет, —  все они работают «бок о бок» в одном приложении и даже разделяют код.

Читать далее

Лекция 15 CS193P Fall 2017 — Alerts и Action Sheet, Notifications & KVO, «жизненный цикл» приложения.

В Лекции 15 освещаются 3 темы:

  • Экстренные сообщения Alerts и списки действий Action Sheets
  • Уведомления Notifications («радиостанции») и KVO
  • “Жизненный цикл” приложения (UIApplication, AppDelegate, Info.plist, Capabilities)

Читать далее

Лекция 14 CS193P Fall 2017 — Еще о Documents, Демонстрационный пример Persistence (постоянное хранение) и Documents.

Лекция 14 состоит из двух неравных частей :

  • первая меньшая часть является продолжением теоретической части, связанной с UIDocument и начатой на Лекции 13. Она посвящена UIDocumentBrowserViewController , прекрасному и очень мощному интерфейсу в вашем собственном приложении для создания файлов, перемещения файлов, удаления файлов, размещения их в папках, перетаскивания файлов с iCloud Drive на локальный жесткий диск.
  • вторая  часть — это огромный демонстрационный пример, охватывающий тему постоянного хранению (persistence) и тему создания приложений, ориентированных на работу с документами, на основе UIDocument и браузера документов UIDocumentBrowserViewController.

Читать далее