web-dev-qa-db-fra.com

Comment appeler C de Swift?

Existe-t-il un moyen d'appeler les routines C depuis Swift?

Un grand nombre de bibliothèques iOS/Apple ne sont qu'en C et j'aimerais quand même pouvoir les appeler.

Par exemple, j'aimerais pouvoir appeler les bibliothèques d'exécution objc à partir de Swift.

En particulier, comment relie-t-on les en-têtes iOS C?

131
Blaze

Oui, vous pouvez bien sûr interagir avec les bibliothèques Apples C. Ici est expliqué comment.
Fondamentalement, les types C, les pointeurs C, etc. sont traduits en objets Swift, par exemple un C int dans Swift est un CInt.

J'ai construit un petit exemple, pour une autre question, qui peut servir de petite explication, sur la manière de relier C et 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);

Ici est la réponse originale.

101
Leandros

Le compilateur convertit l'API C en Swift comme pour Objective-C.

import Cocoa

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

import Darwin

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

Voir Interaction avec les API Objective-C dans la documentation.

9
rickster

Juste au cas où vous êtes aussi novice en XCode que moi et que vous voulez essayer les extraits publiés dans réponse de Leandro :

  1. Fichier-> Nouveau-> Projet
  2. choisissez l'outil de ligne de commande comme préréglage de projet et nommez-le "cliinput"
  3. faites un clic droit dans le navigateur de projet (le panneau bleu à gauche) et choisissez "Nouveau fichier ..."
  4. Dans la liste déroulante, nommez le fichier "UserInput". Décochez la case "Créer également un fichier d'en-tête". Une fois que vous avez cliqué sur "Suivant", il vous sera demandé si XCode doit créer le fichier Bridging-Header.h. Choisissez "Oui".
  5. Copiez et collez le code de la réponse de Leandro ci-dessus. Une fois que vous avez cliqué sur le bouton de lecture, il devrait être compilé et exécuté dans le terminal intégré dans xcode dans le panneau inférieur. Si vous entrez un numéro dans le terminal, un numéro sera renvoyé.
5
lukas83

Ce post a aussi une bonne explication sur la façon de le faire en utilisant Clang's support du module .

Cela est expliqué en termes de procédure pour le projet CommonCrypto, mais en général, cela devrait fonctionner pour toute autre bibliothèque C que vous souhaitez utiliser dans Swift.

J'ai brièvement essayé de faire cela pour zlib. J'ai créé un nouveau projet d'infrastructure iOS et un répertoire zlib contenant un fichier module.modulemap contenant les éléments suivants:

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

Ensuite, sous Cibles -> Lien binaire avec bibliothèques, j'ai sélectionné ajouter des éléments et ajouter libz.tbd.

Vous voudrez peut-être construire à ce stade.

J'ai alors pu écrire le code suivant:

import zlib

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

Vous n'avez pas pour mettre le nom de la bibliothèque zlib devant, sauf que dans le cas ci-dessus, j'ai appelé la classe Swift identique à la fonction C, et sans qualification, la fonction Swift finit par être appelée à plusieurs reprises jusqu'à ce que l'application s'arrête.

3
Julian

Dans le cas de c ++, il y a cette erreur qui apparaît:

  "_getInput", referenced from: 

Vous avez également besoin d’un fichier d’en-tête c ++. Ajoutez c-linkage à votre fonction, puis incluez le fichier d'en-tête dans le pont-en-tête:

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"

Voici l'original vidéo expliquant cela

2
John James

Il semble que ce soit un peu différent quand il s'agit de pointeurs. Voici ce que j'ai jusqu'à présent pour appeler l'appel système 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
}
1
Chris Prince