#c #ios #objective-c #swift #objective-c
#c #iOS #objective-c #swift #objective-c
Вопрос:
Я хотел бы уведомлять swift code всякий раз, когда что-то меняется или событие запускается в моих функциях .cpp.
В моем приложении я использую следующую структуру.
(.cpp) [.hpp] -> (.mm) [.h] -> .swift
Я могу обработать changeThisString из кода swift через :
let swiftString = CPPWrapper().MyMethodWrapper()
это нормально для нажатия кнопки / viewDidLoad, но я хотел бы обновлять это значение всякий раз, когда я устанавливаю его с c .
Если C передает новую строку в swift, она не должна ждать нажатия кнопки, она должна работать как слушатель.
Я буду очень рад любой помощи, спасибо.
Пример:
my.cpp:
std::string changeThisString = "";
...
virtual void myCallBack(MyCallBackParam amp;paramter){
changeThisString = "I WANT TO SEE THIS MESSAGE ON MY APP!"
}
std::string MyClass::MyMethod() {
return changeThisString;
}
my.hpp:
#include <string>
class MyClass{
public:
std::string MyMethod();
};
wrapper.mm
#import "wrapper.h"
#import "my.hpp"
@implementation CPPWrapper
MyClass myClass;
- (NSString*) MyMethodWrapper {
NSString* result = [NSString stringWithUTF8String:myClass.MyMethod().c_str()];
return resu<
}
@end
Wrapper.h
#import <Foundation/Foundation.h>
@interface CPPWrapper : NSObject
-(NSString*) MyMethodWrapper;
@end
.swift
let swiftString = CPPWrapper().MyMethodWrapper()
Ответ №1:
Это пример обратного вызова C, который запускает уведомление об объединении.
(Перенесено на GitHub прямо здесь: https://github.com/moosefactory/C-callback-to-Swift )
Этот пример изменяет значение строки C в библиотеке C и отображает его в поле в представлении SwiftUI.
Вам не нужно проходить Objective-C.
- Первая часть — это библиотека C (несколько изменений, чтобы заставить ее работать с C )
- Вторая часть — это класс C<->Swift
Я думаю, что неплохо иметь объект, который создает мост между вашим кодом C и вашим приложением swift, чтобы удалить эзотерический синтаксис из кода приложения. В этом примере файл MyCLibraryInterface
выполняет эту работу.
Этот класс является наблюдаемым объектом, который будет публиковать изменение значения с помощью combine, так что это немного выходит за рамки вопроса — вы можете остановиться на этом и делать то, что хотите, как только окажетесь в блоке обратного вызова. Обратите внимание, что мы не можем перехватить контекст swift в вызовах c (нет вызовов self или переменных, объявленных в куче)
- Третья часть — это простое приложение SwiftUI, которое получает изменения и обновляет интерфейс
Библиотека C
- MyCLibrary.h
#ifndef MyCLibrary_h
#define MyCLibrary_h
#include <stdio.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>
/// The callback to call when the string is changed
typedef void callback_t(const char* string);
void setCallBack(callback_t _callback);
/// A function that will change the string
void setString(const char* string);
void startTimer(void);
void cancelTimer(void);
#endif /* MyCLibrary_h */
- MyCLibrary.c
#include "MyCLibrary.h"
const char* myString;
dispatch_queue_t queue;
dispatch_source_t timer;
bool running;
callback_t* callback;
void setCallBack(callback_t _callback) {
callback = _callback;
}
void setString(const char* string) {
myString = string;
callback(myString);
}
/// A function that will start a timer that changes string
int ticks = 0;
void idle(dispatch_source_t timer)
{
ticks ;
char ticksStr[32];
sprintf(ticksStr, "Time : %ds", ticks);
setString(ticksStr);
}
void startTimer() {
if (running) { cancelTimer(); sleep(1); }
queue = dispatch_queue_create("timerQueue", 0);
// Create dispatch timer source
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_event_handler(timer, ^{idle(timer);});
dispatch_source_set_cancel_handler(timer, ^{
dispatch_release(timer);
dispatch_release(queue);
});
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 0);
// Set timer
dispatch_source_set_timer(timer, start, NSEC_PER_SEC, 0);
ticks = 0;
running = true;
dispatch_resume(timer);
}
void cancelTimer() {
running = false;
dispatch_source_cancel(timer);
char ticksStr[32];
sprintf(ticksStr, "Canceled after %ds", ticks);
setString(ticksStr);
}
Часть C<>Swift
- MyApp-Bridging-Header.h
#import "MyCLibrary.h"
- MyCLibraryInterface.swift
import Foundation
class MyCLibraryInterface: ObservableObject {
@Published var string: String = "This is a string"
static let shared = MyCLibraryInterface()
init() {
setCallBack { stringPtr in
let newString = CFStringCreateWithCString(kCFAllocatorDefault, stringPtr, kCFStringEncodingASCII) ?? "" as CFString
DispatchQueue.main.async {
MyCLibraryInterface.shared.string = newString as String
}
}
}
func setLibString(string: String) {
string.withCString { stringPointer in
setString(stringPointer)
}
}
func startLibTimer() {
startTimer()
}
func cancelLibTimer() {
cancelTimer()
}
}
Пример SwiftUI
В этом примере приложения представлены начальная строка и кнопка. При щелчке или касании setString
функция вызывается в CLibrary, вызывается быстрый обратный вызов, и представление обновляется после ObservableObject
изменения
import SwiftUI
struct ContentView: View {
@ObservedObject var myCLibInterface: MyCLibraryInterface = MyCLibraryInterface.shared
var body: some View {
VStack {
Text(myCLibInterface.string).frame(width:150).padding()
Button("Reset") {
myCLibInterface.setLibString(string: "C Timer Example")
}.padding()
Button("Start Timer") {
myCLibInterface.startLibTimer()
}.padding()
Button("Cancel") {
myCLibInterface.cancelLibTimer()
}.padding()
}.padding(20)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Комментарии:
1. Спасибо, но я уже могу использовать этот способ с тем, чем я поделился, я хочу что-то вроде observer вместо ожидания нажатия кнопки, это должно измениться напрямую. setString — это не то, что я хочу, и использовать поток обратного вызова в c через swift непросто. В любом случае спасибо.
2. Кнопка приведена здесь только в качестве примера для запуска функции C. Извините, я должен был дать более конкретный ответ. Если вы используете таймер, который увеличивает счетчик в библиотеке c, ваш обратный вызов будет вызван в swift. Вам всегда придется передавать обратный вызов в любом случае. Что-то вроде
myCLibInterface.startTimer() { // Your swift callback }
. А также вам не нужна часть obj-C. Я заменю пример ‘setString’ на ‘startTimer’, когда найду время. Приветствия3. Я изменил, чтобы добавить таймер, который обновляет строку без действий пользователя. Если вы действительно хотите наблюдать строковое значение C без вызова
setString
, вам следует создать таймер, который отслеживает строковое значение. Но я не уверен, что это хороший вариант.4. Большое вам спасибо, я действительно ценю ваши усилия. Это действительно ясно и понятно. Мне нужна некоторая модификация, чтобы иметь возможность адаптировать ее для c , тогда в соответствии с результатом я одобрю ваш ответ. Еще раз большое вам спасибо @Moose!
5. Добро пожаловать, Эмре :). Ошибка странная. Вы пробовали использовать проект, который я загрузил на github? Это похоже на проблему с соединительным заголовком.