Como ligar para C da Swift?

136

Existe uma maneira de chamar rotinas C a partir do Swift?

Muitas bibliotecas iOS / Apple são apenas C e eu ainda gostaria de poder chamá-las.

Por exemplo, eu gostaria de poder chamar as bibliotecas objc runtime da swift.

Em particular, como você faz a ponte dos cabeçalhos do iOS C?

Chama
fonte

Respostas:

106

Sim, é claro que você pode interagir com as bibliotecas da Apples C. Aqui está explicado como.
Basicamente, os tipos C, ponteiros C, etc. são convertidos em objetos Swift, por exemplo, um C intno Swift é a CInt.

Criei um pequeno exemplo, para outra pergunta, que pode ser usada como uma pequena explicação, sobre como fazer a ponte entre C e 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);

Aqui está a resposta original.

Leandros
fonte
1
Eu sou novo no ios / swift. Eu gostaria de usar a função c de log de #include <asl.h> em arquivos rápidos. Qualquer um?
Dmitry Konovalov
é compatível com C ++, arquivos .cpp?
Carlos.V
Para usar arquivos C ++ diretamente, você deve criar um wrapper objetivo-C. Você deseja que a implementação (que deve residir sozinha em um arquivo .mm) contenha o código Objective-C ++ (que é apenas Objective-C e C ++ em um arquivo) e a interface (que deve ser por conta própria .h arquivo de cabeçalho) deve conter código Objective-C puro, portanto, você precisará converter tipos C ++ em tipos Objective-C na implementação, para expô-los ao Swift. Em seguida, você pode importar esse cabeçalho com um cabeçalho de ponte objetivo-c.
William T Froggard 06/12/19
2
“É claro que você pode interagir com as bibliotecas da Apples C” Incorreto. Você pode interagir com qualquer biblioteca C, não limitada a apenas as da Apple.
precisa
Link da documentação atualizada developer.apple.com/documentation/swift/…
MechEthan
9

O compilador converte a API C em Swift, assim como faz para o 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 Interagindo com APIs do Objective-C nos documentos.

rickster
fonte
1
Obrigado pelo exemplo, e você está certo, posso ver como isso pode funcionar. No entanto, ele realmente não responde à minha pergunta, que era como chamar as bibliotecas da Apple C. Mas é definitivamente o mais próximo.
Blaze
Procure em outro lugar nesse documento. Por exemplo, "objetos" ( CFTypeRef) no estilo CoreFoundation são convertidos em objetos Swift. A maioria das funções ObjCRuntime.h não são significativas para o Swift.
Rickster
"A maioria das funções ObjCRuntime.h não são significativas para Swift, embora" Por que você diz isso? Acho que preciso encontrar uma maneira de importar um cabeçalho e fazer a ponte. Parece estranho, mas acho que é o caminho a percorrer.
Chama
5

Caso você seja tão novo no XCode quanto eu e queira experimentar os trechos publicados na resposta de Leandro :

  1. Arquivo-> Novo-> Projeto
  2. escolha Command Line Tool como uma predefinição de projeto e nomeie o projeto como "cliinput"
  3. clique com o botão direito do mouse no navegador do projeto (o painel azul à esquerda) e escolha "Novo arquivo ..."
  4. Na caixa de diálogo suspensa, nomeie o arquivo "UserInput". Desmarque a caixa "Crie também um arquivo de cabeçalho". Depois de clicar em "Avançar", você será perguntado se o XCode deve criar o arquivo Bridging-Header.h para você. Escolha "Sim".
  5. Copie e cole o código da resposta de Leandro acima. Depois de clicar no botão play, ele deve ser compilado e executado no terminal, que no xcode está embutido no painel inferior. Se você digitar um número no terminal, um número será retornado.
lukas83
fonte
4

Este post também tem uma boa explicação sobre como fazer isso usando o suporte ao módulo do clang .

Ele está estruturado em termos de como fazer isso no projeto CommonCrypto, mas, em geral, deve funcionar para qualquer outra biblioteca C que você deseja usar no Swift.

Eu experimentei brevemente fazer isso para zlib. Criei um novo projeto de estrutura do iOS e criei um diretório zlib, contendo um arquivo module.modulemap com o seguinte:

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

Em Targets -> Link Binary With Libraries, selecionei add items e adicionei libz.tbd.

Você pode querer construir neste momento.

Pude escrever o seguinte código:

import zlib

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

Você não precisa colocar o nome da biblioteca zlib na frente, exceto no caso acima, que chamei a classe Swift func da mesma forma que a função C, e sem a qualificação, a função Swift acaba sendo chamada repetidamente até que o aplicativo pare.

Julian
fonte
3

No caso de c ++, existe este erro que aparece:

  "_getInput", referenced from: 

Você também precisa de um arquivo de cabeçalho c ++. Adicione c-linkage à sua função e inclua o arquivo de cabeçalho no cabeçalho da ponte:

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"

Aqui está o vídeo original explicando isso

John James
fonte
se não é trabalho, tentar adicionar __OBJCseleção para o seu Bridging-cabeçalho, por exemplo#ifdef __OBJC @import UIKit; #endif
Chris Yim
1

Parece ser uma cera de bola bastante diferente quando se lida com ponteiros. Aqui está o que tenho até agora para chamar a readchamada de sistema 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
fonte