¿Cómo llamar a C desde Swift?

136

¿Hay alguna manera de llamar a las rutinas C desde Swift?

Muchas bibliotecas de iOS / Apple son solo C y todavía me gustaría poder llamarlas.

Por ejemplo, me gustaría poder llamar a las bibliotecas de tiempo de ejecución objc desde swift.

En particular, ¿cómo se unen los encabezados de iOS C?

Resplandor
fuente

Respuestas:

106

Sí, por supuesto, puede interactuar con las bibliotecas de Apples C. Aquí se explica cómo.
Básicamente, los tipos C, los punteros C, etc. se traducen en objetos Swift, por ejemplo, una C inten Swift es a CInt.

Construí un pequeño ejemplo, para otra pregunta, que puede usarse como una pequeña explicación, sobre cómo tender un puente entre C y 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);

Aquí está la respuesta original.

Leandros
fuente
1
Soy nuevo en ios / swift. Me gustaría utilizar la función c de registro de #include <asl.h> en archivos rápidos. ¿Nadie?
Dmitry Konovalov
¿Es compatible con C ++, archivos .cpp?
Carlos.V
Para usar archivos C ++ directamente, debe crear un contenedor de objetivos C. Querrá que la implementación (que debe residir sola en un archivo .mm) contenga el código Objective-C ++ (que es solo Objective-C y C ++ en un archivo) y la interfaz (que debe estar en su propio .h archivo de encabezado) debe contener código puro de Objective-C, por lo que tendrá que convertir los tipos de C ++ a tipos de Objective-C en la implementación, para exponerlos a Swift. Luego puede importar ese encabezado con un encabezado de puente de Objective-C.
William T Froggard
2
"Por supuesto, puedes interactuar con las bibliotecas de Apples C" Incorrecto. Puede interactuar con cualquier biblioteca C, no limitada en absoluto, solo a Apple.
Slipp D. Thompson
Enlace de documentación actualizado developer.apple.com/documentation/swift/…
MechEthan
9

El compilador convierte C API a Swift al igual que lo hace para Objective-C.

import Cocoa

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

import Darwin

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

Consulte Interactuar con las API de Objective-C en los documentos.

rickster
fuente
1
Gracias por el ejemplo, y tienes razón, puedo ver cómo podría funcionar. Sin embargo, en realidad no responde a mi pregunta, que era cómo llamar a las bibliotecas de Apple C. Pero definitivamente es más cercano.
Blaze
Busque en otra parte de ese documento. Por ejemplo, los "objetos" ( CFTypeRef) de estilo CoreFoundation se convierten en objetos Swift. Sin embargo, la mayoría de las funciones ObjCRuntime.h no son significativas para Swift.
rickster
"Sin embargo, la mayoría de las funciones de ObjCRuntime.h no son significativas para Swift" ¿Por qué dices eso? Creo que necesito encontrar alguna forma de importar un encabezado y cerrarlo. Eso parece incómodo, pero supongo que es el camino a seguir.
Blaze
5

En caso de que seas tan nuevo en XCode como yo y quieras probar los fragmentos publicados en la respuesta de Leandro :

  1. Archivo-> Nuevo-> Proyecto
  2. elija la herramienta de línea de comandos como un preset de proyecto y nombre el proyecto "cliinput"
  3. haga clic derecho en el navegador de proyectos (el panel azul a la izquierda) y elija "Nuevo archivo ..."
  4. En el cuadro de diálogo desplegable, nombre el archivo "UserInput". Desmarca la casilla "Crear también un archivo de encabezado". Una vez que haga clic en "Siguiente", se le preguntará si XCode debe crear el archivo Bridging-Header.h por usted. Elige "Sí".
  5. Copie y pegue el código de la respuesta de Leandro anterior. Una vez que haga clic en el botón de reproducción, debe compilarse y ejecutarse en el terminal, que en xcode está integrado en el panel inferior. Si ingresa un número en la terminal, se le devolverá un número.
lukas83
fuente
4

Esta publicación también tiene una buena explicación sobre cómo hacer esto usando el soporte del módulo clang .

Está enmarcado en términos de cómo hacer esto para el proyecto CommonCrypto, pero en general debería funcionar para cualquier otra biblioteca de C que desee usar desde Swift.

Experimenté brevemente haciendo esto para zlib. Creé un nuevo proyecto de marco de iOS y creé un directorio zlib, que contiene un archivo module.modulemap con lo siguiente:

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

Luego, en Objetivos -> Enlace binario con bibliotecas, seleccioné agregar elementos y agregué libz.tbd.

Es posible que desee construir en este punto.

Luego pude escribir el siguiente código:

import zlib

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

No tiene que poner el nombre de la biblioteca zlib al frente, excepto en el caso anterior que nombré la función de clase Swift de la misma manera que la función C, y sin la calificación la función Swift termina siendo llamada repetidamente hasta que la aplicación se detiene.

Julian
fuente
3

En el caso de c ++, aparece este error:

  "_getInput", referenced from: 

También necesita un archivo de encabezado c ++. Agregue c-linkage a su función, luego incluya el archivo de encabezado en bridge-header:

Swift 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"

Aquí está el video original que explica esto

John James
fuente
si no funciona, intente agregar un __OBJCcheque a su Encabezado de puente, por ejemplo#ifdef __OBJC @import UIKit; #endif
Chris Yim
1

Parece ser una bola de cera bastante diferente cuando se trata de punteros. Esto es lo que tengo hasta ahora para llamar a la llamada al readsistema 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
fuente