#ios #objective-c #swift #xcode #import
#iOS #objective-c #swift #xcode #импорт
Вопрос:
Пытаюсь заставить проект приложения ObjC вызывать функцию Swift в статической библиотеке ObjC……….
Моя сборка проекта приложения ObjC получает ошибку сборки для ссылки на функцию Swift, которая находится в статической библиотеке ObjC (.a), которая импортируется в проект приложения.
Файл Hub_lib-Bridging-Header.у h нет кода.
ПРОЕКТ ПРИЛОЖЕНИЯ OBJ-C……………………………………….
ViewController.mm в рамках проекта приложения ObjC…
#import "ViewController.h"
#import "Hub_lib.h"
#import "Hub_lib-Swift.h"
#import "hublib.hpp"
@interface ViewController ()
@end
@implementation ViewController
. . .
- (IBAction)run_simple_central:(id)sender {
[self BLE.start_central];
}
BLE.h в проекте приложения ObjC………..
#import <CoreBluetooth/CoreBluetooth.h>
#import "Hub_lib-Swift.h"
@interface BLE: NSObject
//< CBPeripheralManagerDelegate >
@property(strong, nonatomic) CBPeripheralManager* peripheralManager;
@property(strong, nonatomic) CBMutableCharacteristic* transferCharacteristic;
@property(strong, nonatomic) NSData* dataToSend;
@property(nonatomic, readwrite) NSInteger sendDataIndex;
-(void)start_central;
@end /* BLE_h */
BLE.m внутри приложения; оболочка для вызова swift……………………..
#import "BLE.h"
#import "Hub_lib-Swift.h"
@interface BLE ()
@end
@implementation BLE
-(void)start_central
{
Hub_lib* BLE_central = [Hub_lib new];
[BLE_central centralManager.run_central];
}
Комментарии:
1. чем больше вы кодируете в objc, тем больше вам понравится, когда вы будете следовать синтаксису camelCase и именовать классы с завершающей прописной буквой и значениями / свойствами со строчной буквой. кстати: вы не можете импортировать
-Swift.h
в свое приложение objc, потому что это относится к внутренней части статической библиотеки. смотрите мой ответ, особенно правила #import .
Ответ №1:
создал тестовый проект, чтобы иметь возможность копировать ваши ошибки. Вы близки, но вам нужно позаботиться о том, чтобы в заголовке вашей статической библиотеки были представлены ее внутренние методы и классы, чтобы вы могли использовать их в другом месте.
давайте начнем с проекта статической библиотеки Objective-C. Hub_lib.h
#import <Foundation/Foundation.h>
// we want the lib header as clean as possible
// it will then be imported in your project with '#import <Hub_lib/Hub_lib.h>'
// auto-generated swift -> objc bridging header imported here will
// mess it up when imported somewhere else.
// so when using swift->objc bridging place next line in .m file instead
//#import "Hub_lib-Swift.h"
//@class BLE_Central; // pre-declaration of a later fully declared Class.
// as we moved the property into .m file we dont need it here.
@interface Hub_lib : NSObject
// to make this work you would need pre-declaration of BLE_Central, see above interface
//@property BLE_Central *ble; // placed in .m interface extension instead.
-(void)run_central;
@end
аналог статической библиотеки / реализация Hub_lib.m
#import "Hub_lib.h"
#import "Hub_lib-Swift.h"
@interface Hub_lib ()
@property BLE_Central *ble_central;
@end;
@implementation Hub_lib
-(instancetype)init {
if (!(self=[super init])) return nil;
_ble_central = [[BLE_Central alloc] init];
return self;
}
-(void)run_central {
[_ble_central run_central];
}
@end
обратите BLE_Central
внимание, что свойство помещается в расширение интерфейса класса, и если вы хотите использовать модуль swift, который возвращается обратно в objc, вам нужно где-то объявить автоматически сгенерированный мост (лучше всего сделать в .m
файле #import "Hub_lib-Swift.h"
)
ваша BLE_central.swift
реализация метода протокола
import Foundation
import UIKit
import CoreBluetooth
import os
var centralManager: CBCentralManager = CBCentralManager()
class BLE_Central: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
os_log("centralManagerDidUpdateState")
}
var discoveredPeripheral: CBPeripheral?
var transferCharacteristic: CBCharacteristic?
var writeIterationsComplete = 0
var connectionIterationsComplete = 0
let defaultIterations = 5 // change this value based on test usecase
var data = Data()
// as you figured out before we need to expose to @objc
@objc public func run_central()
{
os_log("run_central")
// out-commented for testing purposes
//mobile_sys_hub_lib.centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
os_log("Scanning started")
}
}
Пока дополнительный код из objc не используется в swift внутри статической библиотеки,
Hub_lib-Bridging-Header.h
она пуста
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
Далее давайте посмотрим, как импортировать вашу статическую библиотеку в ваше приложение проекта Objective-C «sim backend UI».
Перейдите в настройки целевого приложения «sim backend UI»> Общие> Фреймворк, библиотеки и .. > нажмите
кнопку и найдите свой скомпилированный libHub_lib.a
файл. > выберите его> нажмите ok.К настоящему времени в вашем списке фреймворков должно быть очень много.
Да, этого недостаточно! Вы должны где-то объявить его заголовок в своем проекте приложения. Где именно, зависит от вас. Вместо того, чтобы реализовывать его несколько раз, мы делаем следующее в BLE.h
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
//#import "Hub_lib-Swift.h" // no no no no
#import <Hub_lib/Hub_lib.h> // much better. test-compile once if it is crying
NS_ASSUME_NONNULL_BEGIN
@interface BLE: NSObject
// <CBPeripheralManagerDelegate> // "//<Protocol>" will confuse doxygen
@property (strong, nonatomic) CBPeripheralManager* peripheralManager;
@property (strong, nonatomic) CBMutableCharacteristic* transferCharacteristic;
@property (strong, nonatomic) NSData* dataToSend;
@property (nonatomic, readwrite) NSInteger sendDataIndex;
-(void)start_central;
@end /* BLE_h */
NS_ASSUME_NONNULL_END
аналог BLE.m
#import "BLE.h"
@implementation BLE
-(void)start_central
{
NSLog(@"invoked BLE start_central");
Hub_lib *Hub_central = [Hub_lib new];
[Hub_central run_central];
}
@end
Ваш ViewController.m
or .mm
использует BLE.h
с уже импортированным статическим заголовком lib
#import "ViewController.h"
#import "BLE.h"
@interface ViewController ()
@property (nonatomic) BLE *ble;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.frame = CGRectMake(100, 100, 200, 50);
[btn setTitle:@"run simple central" forState:(UIControlStateNormal)];
[btn setTitleColor:UIColor.greenColor forState:(UIControlStateNormal)];
btn.layer.backgroundColor = UIColor.orangeColor.CGColor;
btn.layer.cornerRadius = 5.0f;
[self.view addSubview:btn];
[btn addTarget:self action:@selector(run_simple_central:) forControlEvents:(UIControlEventTouchUpInside)];
}
- (IBAction)run_simple_central:(id)sender {
// BLE needs to be allocated to take effect.
if (!_ble) _ble = [[BLE alloc] init];
// testing.. should log "run_central" "Scanning started"
[self.ble start_central];
}
@end
Создал testButton, чтобы вы могли видеть, вызывает ли реализация то, что вы ожидаете. Скомпилируйте!
И? Ого! Не работает. Что произошло?
Если ваше приложение представляет собой простой проект Objective-C, оно еще не знает о swift и будет жаловаться странным образом, возможно, с помощью чего-то вроде
Ссылка: не удалось найти или использовать автоматически связанную библиотеку ‘swiftCoreImage’
Неопределенный символ: таблица-свидетель значений для встроенной.Неизвестный объект*
Решение: самый простой способ — создать файл swift в вашем проекте приложения и разрешить Xcode создавать для вас связующий заголовок. (В качестве альтернативы измените настройки проекта, найдите «мост» и перейдите шаг за шагом через свойства) Файл swift не должен иметь специального содержимого.
//
// MakeProjectSwiftCompatible.swift
//
import Foundation
Скомпилируйте снова. теперь это должно сработать, потому что частично реализованный модуль swift из вашей статической библиотеки может работать должным образом, когда ваш Objc-App-Project способен работать со swift.
Редактировать поскольку вы просили работать с Objective-C / C в вашей статической библиотеке, все немного меняется .. так что вот дополнительный пример кода для доказательства этого. В проекте Hub_lib (нацеленном на вашу «фреймворк») добавьте несколько файлов, которые будут содержать некоторый случайный тестовый код c
//HubCPP.h
#import <Foundation/Foundation.h>
#include <vector>
NS_ASSUME_NONNULL_BEGIN
class SomeCPPClass
{
public:
SomeCPPClass(id<NSObject> obj, size_t size);
~SomeCPPClass();
id<NSObject> getBuffer() { return buffers[bufferIdx]; }
unsigned int getCurrentIdx();
private:
std::vector <id <NSObject>> buffers;
unsigned int bufferIdx;
bool isReady;
};
NS_ASSUME_NONNULL_END
To make Xcode know that you work with C you need to have implementation file ending with .mm
and also need to change HubCpp.h
«Identity and Type (right Xcode panel while file selected)» to C Header
// HubCpp.mm
#import "HubCPP.h"
SomeCPPClass::SomeCPPClass (id<NSObject> obj, size_t size) :
bufferIdx (0),
isReady(false)
{
uint8_t ringSize = 255;
assert (ringSize > 0);
for (uint8_t i = 0; i < ringSize; i )
{
//buffers.push_back ();
bufferIdx = (unsigned int)size;
}
}
SomeCPPClass::~SomeCPPClass() {
// cleanup allocated stuff here.
}
unsigned int SomeCPPClass::getCurrentIdx() {
return bufferIdx;
}
Переименуйте Hub_lib.m
.mm
и измените его правила импорта в соответствии со следующим ..
#import "Hub_lib.h"
#import <CoreBluetooth/CoreBluetooth.h> //needed because the -Swift.h bridge will cry in the next line
#import "Hub_lib-Swift.h"
#import "HubCPP.h"
давайте изменим метод проверки Hub_lib.mm
, чтобы он действительно использовал C
-(void)run_central {
[_ble_central run_central];
SomeCPPClass *cpp = new SomeCPPClass(@"justSomeNSStringObject",2);
unsigned int idx = cpp->getCurrentIdx();
NSLog(@"objectiveCplusplus testIdx = %u", idx);
}
Скомпилируйте Hub_lib (схема). К настоящему времени он должен работать, а также принимать использование #import <vector>
.
Если это работает, продолжайте и измените свой Objc-Project-App.
Переключите схему компиляции на таргетинг на ваше Objc-приложение.
Измените имя файла BLE.m
на BLE.mm
(делает его источником Objective C )
Измените имя файла BLE.h
на BLE.hh
(делает его заголовком C )
Изменение в BLE.mm #import "BLE.h
чтобы #import "BLE.hh
в ViewController.m
удалите строку #import "BLE.h"
и замените ее на ViewController.h
вместо этого как #import "BLE.hh"
(В общем, гораздо проще информировать ваш компилятор о том, какой язык следует ожидать в реализации, когда вы помещаете заголовки импорта в заголовочные файлы.)
Скомпилируйте. Вот и все! Ваша статическая библиотека Objective-C должна правильно работать на этом этапе.
Редактировать Вы можете найти готовое рабочее пространство для Xcode здесь…
github: объединить cpp swift и objective-c в статической библиотеке
Комментарии:
1. Ol Sen: статическая библиотека ObjC на самом деле Hub-lib.mm и не .m, потому что он импортирует библиотеку C .
2. Ol Sen: строка BLE_Central.swift от Hub_lib «class BLE_Central …» выдает следующую ошибку: «Тип ‘BLE_Central’ не соответствует протоколу ‘CBCentralManagerDelegate’. Я разыщу это место.
3. Приветствую, старый сенатор. Если у вас это получилось, не могли бы вы, пожалуйста, отправить почтовый индекс?
4. Вау, объединение C и Swift в одной статической библиотеке слишком сложно. Вместо этого я создам две статические библиотеки: одну для переноса C и одну для переноса Swift BLE
5. Хм. @property BLE_Central в файле .m выдает ошибку сборки «Неизвестное имя типа ‘BLE_Central'».
Ответ №2:
Решение от пользователя StackO: Asperi
- Пустая верхняя папка
- Создано рабочее пространство в верхней папке 1.1 Скопировано чистое приложение и библиотека в верхнюю папку
- Добавьте ObjC lib .xcodeproj в рабочую область с помощью Xcode> File> Add files …
- Добавьте ObjC app .xcodeproj в workspace
- Добавлена зависимость sim_backend_UI от библиотеки lib через рабочую область a. app proj> вкладка Общие> Фреймворки, библиотеки .. > b. Выберите библиотеку .a. Добавить.
- Добавьте немного.swift (любой файл swift, который вы хотите) в sim_backend_UI, просто для того, чтобы Xcode добавил необходимые системные динамические библиотеки swift (которые также понадобятся для swift-части в статической библиотеке)… и подтвердите создание моста в появившемся диалоговом окне a. новый файл> Swift> Some.swift b. Создайте заголовок моста c. Добавлен в Some.swift …
импортировать основу
struct Some{}
- Установите активную схему Xcode для приложения и целевого устройства
- Сборка «выполнена успешно»