Bagaimana cara memanggil C dari Swift?

138

Apakah ada cara untuk memanggil rutinitas C dari Swift?

Banyak pustaka iOS / Apple hanya C dan saya masih ingin dapat memanggilnya.

Misalnya, saya ingin dapat memanggil pustaka runtime objc dari swift.

Secara khusus, bagaimana Anda menjembatani header iOS C?

Api
sumber

Jawaban:

106

Ya, Anda tentu saja dapat berinteraksi dengan perpustakaan Apel C. Berikut dijelaskan caranya.
Pada dasarnya, tipe C, pointer C, dll diterjemahkan ke dalam objek Swift, misalnya C intdi Swift adalah a CInt.

Saya telah membangun sebuah contoh kecil, untuk pertanyaan lain, yang dapat digunakan sebagai sedikit penjelasan, tentang bagaimana menjembatani antara C dan 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);

Inilah jawaban aslinya.

Leandros
sumber
1
Saya baru mengenal ios / swift. Saya ingin menggunakan logging c-function dari #include <asl.h> di file swift. Siapa saja?
Dmitry Konovalov
apakah itu kompatibel dengan C ++, file .cpp ??
Carlos.V
Untuk menggunakan file C ++ secara langsung, Anda harus membuat pembungkus objektif-C. Anda akan menginginkan implementasinya (yang seharusnya berada dengan sendirinya dalam file .mm) untuk memuat kode Objective-C ++ (yang hanya Objective-C dan C ++ dalam satu file), dan antarmuka (yang seharusnya dalam .h sendiri) file header) harus berisi kode Objective-C murni, jadi Anda harus mengonversi tipe C ++ menjadi tipe Objective-C dalam implementasinya, untuk mengeksposnya ke Swift. Kemudian Anda dapat mengimpor tajuk tersebut dengan tajuk penghubung tujuan-c.
William T Froggard
2
“Anda tentu saja dapat berinteraksi dengan perpustakaan Apple C” Salah. Anda dapat berinteraksi dengan pustaka C apa pun , sama sekali tidak terbatas hanya pada milik Apple.
Slipp D. Thompson
Pembaruan
9

Kompilator mengubah C API menjadi Swift seperti yang dilakukannya untuk Objective-C.

import Cocoa

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

import Darwin

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

Lihat Berinteraksi dengan API Objective-C di dokumen.

rickster
sumber
1
Terima kasih atas contohnya, dan Anda benar, saya dapat melihat cara kerjanya. Namun itu tidak benar-benar menjawab pertanyaan saya, yaitu bagaimana memanggil perpustakaan apple C. Tapi itu pasti paling dekat.
Blaze
Cari di tempat lain di dokumen itu. Misalnya, "objek" ( CFTypeRef) bergaya CoreFoundation dikonversi ke objek Swift. Sebagian besar fungsi ObjCRuntime.h tidak berarti bagi Swift.
rickster
"Sebagian besar fungsi ObjCRuntime.h tidak berarti bagi Swift," Mengapa Anda mengatakan itu? Saya rasa saya perlu mencari cara untuk mengimpor header dan menjembataninya. Kelihatannya canggung, tapi saya kira itulah cara untuk pergi.
Blaze
5

Untuk berjaga-jaga jika Anda baru mengenal XCode seperti saya dan ingin mencoba cuplikan yang diposting di jawaban Leandro :

  1. File-> Baru-> Proyek
  2. pilih Command Line Tool sebagai preset proyek dan beri nama proyek "cliinput"
  3. klik kanan di navigator proyek (panel biru di sebelah kiri) dan pilih "File Baru ..."
  4. Pada dialog drop-down beri nama file "UserInput". Hapus centang pada kotak "Buat juga file header". Setelah Anda mengklik "Berikutnya", Anda akan ditanya apakah Xcode harus membuat file Bridging-Header.h untuk Anda. Pilih "Ya".
  5. Salin & tempel kode dari jawaban Leandro di atas. Setelah Anda mengklik tombol putar, itu harus dikompilasi dan dijalankan di terminal, yang di xcode sudah terpasang di panel bawah. Jika Anda memasukkan nomor di terminal, nomor itu akan dikembalikan.
lukas83
sumber
4

Posting ini juga memiliki penjelasan yang bagus tentang bagaimana melakukan ini menggunakan dukungan modul clang .

Ini dibingkai dalam hal bagaimana melakukan ini untuk proyek CommonCrypto, tetapi secara umum itu harus berfungsi untuk pustaka C lain yang ingin Anda gunakan dari dalam Swift.

Saya secara singkat bereksperimen dengan melakukan ini untuk zlib. Saya membuat proyek framework iOS baru dan membuat direktori zlib, yang berisi file module.modulemap dengan berikut ini:

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

Kemudian di bawah Target -> Link Binary With Libraries saya memilih menambahkan item dan menambahkan libz.tbd.

Anda mungkin ingin membangun pada saat ini.

Saya kemudian bisa menulis kode berikut:

import zlib

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

Anda tidak harus meletakkan nama pustaka zlib di depan, kecuali dalam kasus di atas saya menamai kelas Swift func sama dengan fungsi C, dan tanpa kualifikasi fungsi Swift akhirnya dipanggil berulang kali sampai aplikasi berhenti.

Julian
sumber
3

Dalam kasus c ++, ada kesalahan ini yang muncul:

  "_getInput", referenced from: 

Anda juga membutuhkan file header c ++. Tambahkan c-linkage ke fungsi Anda, lalu sertakan file header di bridge-header:

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

Berikut adalah video asli yang menjelaskan hal ini

John James
sumber
jika tidak berhasil, coba tambahkan __OBJCcentang ke Bridging-Header Anda, misalnya#ifdef __OBJC @import UIKit; #endif
Chris Yim
1

Tampaknya menjadi bola lilin yang agak berbeda saat berhadapan dengan pointer. Inilah yang saya miliki sejauh ini untuk memanggil panggilan sistem C POSIX read:

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
sumber