Ответ
Да, но с определенными условиями. Swift Package Manager изначально ориентирован на Swift, но поддерживает Objective-C через модули (modulemaps).
Основной способ — использование файла module.modulemap:
- Создайте структуру папок
include/в директории с исходным кодом (Sources/ИмяЦели). - Поместите заголовочные файлы (
.h) вinclude/. - Создайте файл
include/module.modulemap, который описывает модуль.
Пример структуры и кода:
MyObjCPackage/
├── Package.swift
└── Sources/
└── MyObjCLib/
├── MyObjCLib.c
└── include/
├── MyObjCLib.h
└── module.modulemap
Содержимое module.modulemap:
module MyObjCLib {
header "MyObjCLib.h"
export *
}
Настройка в Package.swift:
let package = Package(
name: "MyObjCPackage",
targets: [
.target(
name: "MyObjCLib",
publicHeadersPath: "include" // Указывает путь к заголовкам
)
]
)
Ограничения и рекомендации:
- SPM не поддерживает смешанные цели (файлы
.mи.swiftв одной цели). Цель должна быть либо чисто Swift, либо чисто C/Objective-C. - Для удобного использования Objective-C кода в Swift-проектах часто создают чистую Objective-C цель (как библиотеку) и затем зависимую от неё Swift-цель.
- Альтернатива для сложных библиотек — дистрибуция в виде
.xcframework, который SPM может использовать.
Ответ 18+ 🔞
Ну, слушай, вот сидишь ты такой, пишешь на Swift, всё красиво, пакетный менеджер под боком. И тут тебе вдруг понадобилось впихнуть в проект какой-нибудь старый, добрый, вонючий Objective-C код. Ну, библиотеку какую-нибудь, которую ещё твой тимлид на коленке писал, когда Swift в проекте Apple только в виде грязной мысли был.
И думаешь: "Блядь, SPM же для Swift, он с Objective-C не дружит!" А вот и нихуя! Дружит, сука! Но не просто так, а через эти ваши модули (modulemaps). Это как взять этого упрямого осла Objective-C, надеть на него намордник в виде module.modulemap и только тогда он в Swift-табун впишется.
Как это, блядь, работает? Через этот самый module.modulemap:
- Ты в папке с исходниками (
Sources/ТвояЦель) создаёшь структуру, как у параноика. Папкаinclude/— святая святых. - Туда кидаешь все свои
.hзаголовки, как драгоценности в сейф. - А потом создаёшь в той же
include/файликmodule.modulemap. Это, сука, инструкция для SPM, как этот обшарпанный Objective-C код превратить в приличный модуль.
Вот смотри, как это выглядит, ёпта:
MyObjCPackage/
├── Package.swift
└── Sources/
└── MyObjCLib/ <-- Цель чисто под Objective-C
├── MyObjCLib.m <-- Сам код, который пахнет стариной
└── include/ <-- Волшебная папка
├── MyObjCLib.h
└── module.modulemap <-- Вот он, ключик!
А в этом самом module.modulemap пишешь простыню:
module MyObjCLib {
header "MyObjCLib.h" // Вот этот хедер — лицо твоего модуля
export * // И всё, что в нём есть, тащи на выход
}
Ну и в Package.swift не забудь ткнуть пальцем, где эти хедеры искать:
let package = Package(
name: "MyObjCPackage",
targets: [
.target(
name: "MyObjCLib",
publicHeadersPath: "include" // Смотри сюда, блядь, заголовки тут!
)
]
)
Но есть, конечно, подводные ебеня, куда ж без них:
- SPM — максималист, блядь. Он не терпит смешанного дерьма в одной цели. Либо всё Swift, либо всё C/Objective-C. Файлы
.mи.swiftвместе — это пиздец и ошибка компиляции. - Поэтому мудрый путь — сделать отдельную, чистую Objective-C цель (библиотеку), а потом уже твой Swift-код будет на неё с придыханием ссылаться как на зависимость.
- А если твоя библиотека — это такой монстр, с кучей настроек и файлов, то проще, наверное, запаковать её в
.xcframeworkи скормить уже SPM'у в таком, готовом виде. Меньше мороки, хотя и не так изящно.
Вот так вот, не так страшен чёрт, как его малюют. Главное — правильно надеть на него намордник в виде modulemap.