Вступление
Столкнувшись с задачей создать фреймворк, первым делом я, как и полагается, порядочно погуглил. Однако, во всех туториалах, которые я встречал, создавали фреймворк без подов, и конечно было непонятно как работать в данном случае с cocoapods. Как переносить поды, нужно ли их вообще переносить и как в целом правильно сбилдить фреймворк, а затем его внедрить в другой проект.
Тестировалось на xcode 12.4, swift 5
Как создать статическую библиотеку Static Library с Cocoapods я писал здесь
1. Билдим фреймворк
Что ж, перейдем сразу к делу, открываем xcode, создаем новый проект, выбираем Framework, жмем далее, назовем проект FrameworkExample, тесты включать/не включать - на ваше усмотрение.
![](https://habrastorage.org/getpro/habr/upload_files/a3f/0ff/39b/a3f0ff39bc0ba0b70a883fcac0f0f59b.png)
Я включил юнит-тесты, в итоге получили пустой проект-фреймворк:
![](https://habrastorage.org/getpro/habr/upload_files/6c8/721/1e8/6c87211e87d0fa7cd2a7f0a7c562999c.png)
Теперь мы можем создавать .swift-файлы и добавлять их в наш пока еще пустой фреймворк.
Не забываем указывать модификаторы доступа public и open для классов свойств и функций!
Шрифты, локализацию Localizable.strings, картинки Assets не вносим в библиотеку, их добавим в клиентский проект отдельно!
Если же у нас имеются файлы(например мы хотим из другого проекта перенести в фреймворк), которые нужно включить в фреймворк, то просто переносим(drag and drop) их в FrameworkExample:
![](https://habrastorage.org/getpro/habr/upload_files/b56/312/5f2/b563125f228328a600b72aaa71f3a024.png)
Щелкнув на таргет FrameworkExamples -> General не забудьте настроить с какой версии доступен фреймворк и для каких устройств:
![](https://habrastorage.org/getpro/habr/upload_files/ac4/6a5/b88/ac46a5b88166256317a205b099650b6a.png)
После этого билдим наш фреймворк Ctrl+B (два раза - для устройства и симулятора), и, если у вас нет cocoapods, то все должно сбилдится успешно Build Succeeded - переходим к окончанию пункта 1.
Если же есть подсы, то получим ошибку что они не найдены:
![](https://habrastorage.org/getpro/habr/upload_files/0c8/75e/6de/0c875e6de5475d031891e474acc5059d.png)
Итак, создаем Podfile в папке проекта, вставляем нужные подсы, не забываем указывать use_frameworks! :
platform :ios, '13.0'
target 'FrameworkExample' do
use_frameworks!
pod 'Moya'
pod 'Alamofire'
pod 'Kingfisher'
pod 'EasyPeasy'
pod 'KeychainAccess'
pod 'SwiftPhoneNumberFormatter'
end
Устанавливаем поды, открываем workspace, билдим Ctrl+B - для симулятора и устройства - должны получить Build Succeeded:
Находим папку Products, щелкаем правой кнопкой по framework-файлу, затем Show In Finder:
![](https://habrastorage.org/getpro/habr/upload_files/f29/6a6/3a8/f296a63a8b47c7dfd5abea27a1fcf87b.png)
Попадаем либо в папку Debug-iphoneos, либо Debug-iphonesimulator в зависимости на какой product нажали, видим папку нашего фреймворка FrameworkExample.framework, а также подгруженные подсы.
![](https://habrastorage.org/getpro/habr/upload_files/2f7/66b/90d/2f766b90d7e4543cabaffcb3f7e4be14.png)
Если перейти на уровень выше в папку Products, то увидим такую же папку и для устройства:
![](https://habrastorage.org/getpro/habr/upload_files/597/3f6/a05/5973f6a054ff65e913124494c8aa5573.png)
Таким образом, на этом этапе мы сбилдили фреймворк, в котором будут все файлы, помещенные в проект, сам фреймворк состоит из следующих файлов:
![](https://habrastorage.org/getpro/habr/upload_files/424/289/731/424289731a8f81d8780aa8aa17be18b4.png)
2. Компилируем Universal Framework
Под Universal Framework имеется в виду фреймворк, который подходит и под устройство и под симулятор. То есть, мы объединим фреймворки из папок Debug-iphoneos и Debug-iphonesimulator.
Для начала добавим новый target -> Aggregator:
![](https://habrastorage.org/getpro/habr/upload_files/3e5/bf0/5d3/3e5bf05d3734641e2e413e64fa0a5848.png)
![](https://habrastorage.org/getpro/habr/upload_files/fa9/d29/8bc/fa9d298bcf962b422d737775ac89d40b.png)
Назовем его UniversalFramework, должно получиться так:
![](https://habrastorage.org/getpro/habr/upload_files/70d/569/380/70d5693809c6787cce73c525441bff2d.png)
Далее добавим Run Script Phase - код, который будет выполняться при билде UniversalFramework'a и создавать нам объединенный фреймворк:
![](https://habrastorage.org/getpro/habr/upload_files/60e/4a5/ae4/60e4a5ae4aad4629607706d09244e029.png)
Вставляем следующий скрипт:
echo "project"
# 1) Открываем "${PROJECT_DIR}"
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-iosuniversal
# 2) Переходим (либо создаем) папку с выходными файлами
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# 3) Билдим для устройства и симулятора
echo "iphone"
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" build
echo "iphonesim"
xcodebuild -target "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" build
# 4) Копируем из папки Debug-iphoneos в папку universal
echo "universal"
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# 5) Копируем swift modules
echo "iphone simulator path"
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# 6) С помощью lipo создаем universal framework
echo "lipo create"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
echo "end"
fi
Билдим UniversalFramework Ctrl+B,
![](https://habrastorage.org/getpro/habr/upload_files/b2f/70b/097/b2f70b097298e5b21341d6d2c5182bee.png)
Теперь выбрав правой кнопкой FrameworkExample.framework и нажав Show In Finder, переходим в папку с фреймворком, и, поднявшись на уровень выше в папку Products, видим созданную папку с нашим универсальным фреймворком:
![](https://habrastorage.org/getpro/habr/upload_files/9ae/926/c4c/9ae926c4c57c5dabc18d5e752de9b395.png)
В этой папке находятся все файлы нашего UniversalFramework. Переходим к следующему этапу!
3. Внедряем фреймворк в ClientApp
Под "ClientApp" имеется в виду любой проект
Cоздаем новый проект xcode -> new -> file -> project -> App
![](https://habrastorage.org/getpro/habr/upload_files/109/438/777/109438777830be505dda9aa38b36035c.png)
Назовем его ClientAppWithFramework:
![](https://habrastorage.org/getpro/habr/upload_files/57f/109/ab5/57f109ab5443ce661e1e00d9da636861.png)
Перенесем папку Debug-iosuniversal, т.е. наш универсальный фреймворк в папку с нашим проектом, затем подключим его в Build Phases -> Link Binary With Libraries:
![](https://habrastorage.org/getpro/habr/upload_files/2a5/4fa/cbe/2a54facbe12f55ce3d59b4880a36a698.png)
![](https://habrastorage.org/getpro/habr/upload_files/443/f0c/08f/443f0c08f17ffe23d6084b31f56db121.png)
![](https://habrastorage.org/getpro/habr/upload_files/ebe/4d2/f6c/ebe4d2f6caf06b1f6965f4b61b4c50e3.png)
Далее скачиваем pods в наш клиентский проект - Podfile можно взять из FrameworkExample:
platform :ios, '13.0'
target 'ClientAppWithFramework' do
use_frameworks!
pod 'Moya'
pod 'Alamofire'
pod 'Kingfisher'
pod 'EasyPeasy'
pod 'KeychainAccess'
pod 'SwiftPhoneNumberFormatter'
end
Установив pods, открываем workspace. Импортируем FrameworkExample во ViewController.swift:
![](https://habrastorage.org/getpro/habr/upload_files/d04/f2a/b6c/d04f2ab6c0f7db66e06250639cc3c124.png)
Отлично, переходим к тестировке и к возможным ошибкам!
4. Тестируем и правим ошибки
Среди моих файлов фреймворка был файл для тестирования, который содержит публичную функцию, которая печатает нам The Framework works!:
public class TestViewController: UIViewController {
public override func viewDidLoad() {
super.viewDidLoad()
}
public func printLog() {
print("The Framework works!")
}
}
Попробуем вызвать эту функцию из ClientApp, добавляем две строчки:
import FrameworkExample
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let vc = TestViewController()
vc.printLog()
}
}
Выше я писал, что шрифты и картинки мы подгрузим отдельно, так вот, если у вас имеются шрифты, картинки, локализация, то самое время перенести их в ClientApp
Запускаем приложение, если у вас падает с ошибкой:
![](https://habrastorage.org/getpro/habr/upload_files/fe5/d53/17b/fe5d5317b0cfff9af2514b846cf037ae.png)
То необходимо на вкладке General сменить Do Not Embed на Embed & Sign:
![](https://habrastorage.org/getpro/habr/upload_files/128/d6b/461/128d6b46189d33994482ca70194a8e6d.png)
Пытаемся запустить вновь, и снова возможна ошибка:
![](https://habrastorage.org/getpro/habr/upload_files/29c/b67/eb2/29cb67eb2ef182d8e60730bcda092706.png)
Здесь решение на вкладке Build Phases -> Validate Workspace сменить с No на Yes, сбилдить проект, ошибка должна стать Warning'oм (Магия!), затем измените Validate Workspace снова на No - ошибка и ворнинг должны исчезнуть:
![](https://habrastorage.org/getpro/habr/upload_files/97b/d01/32e/97bd0132e8731e81b6b2f146d2233699.png)
Запускаем приложение, убеждаемся, что все работает корректно:
![](https://habrastorage.org/getpro/habr/upload_files/8c3/463/d1d/8c3463d1df621549fb570dbca9621cb9.png)
Отлично, фреймворк внедрен в другой проект и им можно пользоваться!