Jak dzwonić do C z Swift?

136

Czy istnieje sposób na wywołanie procedur C z poziomu Swift?

Wiele bibliotek iOS / Apple jest dostępnych tylko w języku C i nadal chciałbym móc je wywoływać.

Na przykład chciałbym móc wywołać biblioteki uruchomieniowe objc ze swift.

W szczególności, jak połączyć nagłówki iOS C?

Płomień
źródło

Odpowiedzi:

106

Tak, możesz oczywiście współdziałać z bibliotekami Apples C. Tutaj wyjaśniono, jak to zrobić.
Zasadniczo typy C, wskaźniki C itp. Są tłumaczone na obiekty języka Swift, na przykład C intw języku Swift to CInt.

Zbudowałem mały przykład dla innego pytania, które może być użyte jako małe wyjaśnienie, jak połączyć C i Swift:

main.swift

import Foundation

var output: CInt = 0
getInput(&output)

println(output)

UserInput.c

#include <stdio.h>

void getInput(int *output) {
    scanf("%i", output);
}

cliinput-Bridging-Header.h

void getInput(int *output);

Oto oryginalna odpowiedź.

Leandros
źródło
1
Jestem nowy w iOS / Swift. Chciałbym użyć funkcji logowania c z #include <asl.h> w plikach Swift. Ktoś?
Dmitry Konovalov
czy jest kompatybilny z C ++, plikami .cpp?
Carlos V,
Aby bezpośrednio używać plików C ++, należy utworzyć opakowanie z celem-C. Będziesz chciał, aby implementacja (która powinna znajdować się sama w pliku .mm) zawierała kod Objective-C ++ (który jest po prostu Objective-C i C ++ w jednym pliku), a interfejs (który powinien znajdować się we własnym .h). nagłówek) powinien zawierać czysty kod Objective-C, więc będziesz musiał przekonwertować typy C ++ na typy Objective-C w implementacji, aby udostępnić je Swift. Następnie możesz zaimportować ten nagłówek z nagłówkiem mostkującym z celem-c.
William T Froggard
2
„Możesz oczywiście współpracować z bibliotekami Apple C” . Niepoprawnie. Możesz wchodzić w interakcje z dowolnymi bibliotekami C, które nie są ograniczone tylko do Apple.
Slipp D. Thompson,
Zaktualizowany link do dokumentacji developer.apple.com/documentation/swift/ ...
MechEthan
9

Kompilator konwertuje C API na Swift, tak jak robi to dla Objective-C.

import Cocoa

let frame = CGRect(x: 10, y: 10, width: 100, height: 100)

import Darwin

for _ in 1..10 {
    println(rand() % 100)
}

Zobacz Interakcja z interfejsami API Objective-C w dokumentacji.

rickster
źródło
1
Dzięki za przykład i masz rację, widzę, jak to może działać. Jednak tak naprawdę nie odpowiada na moje pytanie, jak nazwać biblioteki Apple C. Ale zdecydowanie najbliżej.
Blaze
Poszukaj w innym miejscu tego dokumentu. Na przykład „obiekty” ( CFTypeRef) w stylu CoreFoundation są konwertowane na obiekty Swift. Większość funkcji ObjCRuntime.h nie ma jednak znaczenia dla języka Swift.
rickster
„Większość funkcji ObjCRuntime.h nie ma jednak znaczenia dla Swifta” Dlaczego tak mówisz? Myślę, że muszę znaleźć sposób na zaimportowanie nagłówka i połączenie go. Wydaje się to niezręczne, ale przypuszczam, że to właściwy sposób.
Blaze
5

Na wypadek, gdybyś był równie nowy w XCode jak ja i chciałbyś wypróbować fragmenty zamieszczone w odpowiedzi Leandro :

  1. Plik-> Nowy-> Projekt
  2. wybierz narzędzie wiersza poleceń jako ustawienie wstępne projektu i nazwij projekt „cliinput”
  3. kliknij prawym przyciskiem myszy w nawigatorze projektu (niebieski panel po lewej) i wybierz „Nowy plik ...”
  4. W rozwijanym oknie dialogowym nazwij plik „UserInput”. Odznacz pole „Utwórz także plik nagłówkowy”. Po kliknięciu „Dalej” zostaniesz zapytany, czy XCode powinien utworzyć dla Ciebie plik Bridging-Header.h. Wybierz „Tak”.
  5. Skopiuj i wklej kod z odpowiedzi Leandro powyżej. Po kliknięciu przycisku odtwarzania powinien skompilować się i uruchomić w terminalu, który w xcode jest wbudowany w dolny panel. Jeśli wprowadzisz numer w terminalu, zostanie zwrócony numer.
lukas83
źródło
4

Ten post zawiera również dobre wyjaśnienie, jak to zrobić, korzystając z obsługi modułów clang .

Jest sformułowany pod kątem tego, jak to zrobić dla projektu CommonCrypto, ale ogólnie powinien działać dla każdej innej biblioteki C, z której chcesz korzystać w Swift.

Krótko eksperymentowałem z robieniem tego dla zlib. Stworzyłem nowy projekt platformy iOS i utworzyłem katalog zlib, zawierający plik module.modulemap zawierający:

module zlib [system] [extern_c] {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/zlib.h"
    export *
}

Następnie w obszarze Cele -> Połącz pliki binarne z bibliotekami wybrałem dodaj elementy i dodałem libz.tbd.

W tym momencie możesz chcieć zbudować.

Byłem wtedy w stanie napisać następujący kod:

import zlib

public class Zlib {
    public class func zlibCompileFlags() -> UInt {
        return zlib.zlibCompileFlags()
    }
}

Nie musisz umieszczać nazwy biblioteki zlib na początku, z wyjątkiem powyższego przypadku, gdy nazwałem funkcję klasy Swift tak samo jak funkcję C, a bez kwalifikacji funkcja Swift jest wywoływana wielokrotnie, aż aplikacja się zatrzyma.

juliański
źródło
3

W przypadku C ++ pojawia się następujący błąd:

  "_getInput", referenced from: 

Potrzebujesz też pliku nagłówkowego C ++. Dodaj link-c do swojej funkcji, a następnie dołącz plik nagłówkowy do nagłówka mostu:

Szybki 3

UserInput.h

#ifndef USERINPUT_H
#define USERINPUT_H    

#ifdef __cplusplus
extern "C"{
#endif

getInput(int *output);

#ifdef __cplusplus
}
#endif

UserInput.c

#include <stdio.h>

void getInput(int *output) {
    scanf("%i", output);
}    

main.swift

import Foundation
var output: CInt = 0
getInput(&output)
print(output)

cliinput-Bridging-Header.h

#include "UserInput.h"

Oto oryginalny film wyjaśniający to

John James
źródło
jeśli to nie działa, spróbuj dodać __OBJCczek do swojego nagłówka Bridging-Header, np.#ifdef __OBJC @import UIKit; #endif
Chris Yim
1

Wydaje się, że to trochę inna kula wosku, gdy mamy do czynienia ze wskazówkami. Oto, co mam do tej pory do wywoływania wywołania readsystemowego C POSIX :

enum FileReadableStreamError : Error {
case failedOnRead
}

// Some help from: http://stackoverflow.com/questions/38983277/how-to-get-bytes-out-of-an-unsafemutablerawpointer
// and https://gist.github.com/kirsteins/6d6e96380db677169831
override func readBytes(size:UInt32) throws -> [UInt8]? {
    guard let unsafeMutableRawPointer = malloc(Int(size)) else {
        return nil
    }

    let numberBytesRead = read(fd, unsafeMutableRawPointer, Int(size))

    if numberBytesRead < 0 {
        free(unsafeMutableRawPointer)
        throw FileReadableStreamError.failedOnRead
    }

    if numberBytesRead == 0 {
        free(unsafeMutableRawPointer)
        return nil
    }

    let unsafeBufferPointer = UnsafeBufferPointer(start: unsafeMutableRawPointer.assumingMemoryBound(to: UInt8.self), count: numberBytesRead)

    let results = Array<UInt8>(unsafeBufferPointer)
    free(unsafeMutableRawPointer)

    return results
}
Chris Prince
źródło