標籤:
雖然Objective-C還活的很好,但是蘋果已經把重心轉移到Swift上。未來Mac和iOS的開發必然是以Swift為主。因為Swift還比較新,很多SDK還沒有提供Swift版本。這裡分享下如何使用Swift來調用C。
參考原文:How to Bridge C Code to Create Swift Barcode Reader on Mac
Xiao Ling
翻譯:yushulx
軟體下載
混合使用Swift和C
蘋果在iBooks裡提供了電子書Using Swift with Cocoa and Objective-C。在網頁上可以閱讀Interacting with C APIs。
Swift和C類型映射關係參考:
使用Swift和C在Mac上實現1D/2D條碼應用
在Xcode中使用快速鍵Command+Shift+N建立新工程。
標頭檔和依賴的庫直接拖入工程就行了。Xcode會自動關聯。
Command+N建立一個C檔案,用於調用底層的C動態連結程式庫(Dynamsoft Barcode dylib)。
完成之後,Xcode會彈出提示:
確認之後,Xcode會自動產生一個橋接標頭檔。把C用到的標頭檔添加進去:
#import "native_lib.h"
參考Dynamsoft Barcode SDK的線上程式碼範例做一些修改:
#include "native_lib.h" int dbr_release_memory(pBarcodeResultArray paryResult){ DBR_FreeBarcodeResults(&paryResult); printf("Game Over\n"); return 0;} pBarcodeResultArray dbr_decodeBarcodeFile(char* pszImageFile){ // Parse command __int64 llFormat = (OneD |QR_CODE); int iMaxCount = 0x7FFFFFFF; int iIndex = 0; ReaderOptions ro = {0}; pBarcodeResultArray paryResult = NULL; int iRet = -1; char * pszTemp = NULL; char * pszTemp1 = NULL; struct timeval begin, end; if (NULL == pszImageFile) { printf("The syntax of the command is incorrect.\n"); return NULL; } // Set license DBR_InitLicense("A825E753D10C6CAC7C661140EC5ABEC3"); // Read barcode gettimeofday(&begin, NULL); ro.llBarcodeFormat = llFormat; ro.iMaxBarcodesNumPerPage = iMaxCount; iRet = DBR_DecodeFile(pszImageFile, &ro, &paryResult); gettimeofday(&end, NULL); // Output barcode result pszTemp = (char*)malloc(4096); if (iRet != DBR_OK) { sprintf(pszTemp, "Failed to read barcode: %s\r\n", DBR_GetErrorString(iRet)); printf("%s", pszTemp); free(pszTemp); return NULL; } if (paryResult->iBarcodeCount == 0) { sprintf(pszTemp, "No barcode found. Total time spent: %.3f seconds.\r\n", ((float)((end.tv_sec * 1000 * 1000 + end.tv_usec) - (begin.tv_sec * 1000 * 1000 + begin.tv_usec))/(1000 * 1000))); printf("%s", pszTemp); DBR_FreeBarcodeResults(&paryResult); return 0; } sprintf(pszTemp, "Total barcode(s) found: %d. Total time spent: %.3f seconds\r\n\r\n", paryResult->iBarcodeCount, ((float)((end.tv_sec * 1000 * 1000 + end.tv_usec) - (begin.tv_sec * 1000 * 1000 + begin.tv_usec))/(1000 * 1000))); printf("%s", pszTemp); return paryResult;}
修改下產生的標頭檔:
#ifndef __DBRConsole__native_lib__#define __DBRConsole__native_lib__ #include <stdio.h> #include <stdlib.h>#include <string.h>#include <sys/time.h> #include "If_DBR.h" pBarcodeResultArray dbr_decodeBarcodeFile(char* fileName);int dbr_release_memory(pBarcodeResultArray paryResult);const char * GetFormatStr(__int64 format); #endif /* defined(__DBRConsole__native_lib__) */
用Swift編寫命令列工具
把Swift中的String轉換成char *:
var file: String = "/Applications/Dynamsoft/Barcode Reader 3.0 Trial/Images/AllSupportedBarcodeTypes.tif" // barcode file//let namePtr = strdup(filePath.bridgeToObjectiveC().UTF8String) var filePtr = strdup(file.cStringUsingEncoding(NSUTF8StringEncoding)!)var fileName: UnsafeMutablePointer<CChar> = UnsafeMutablePointer(filePtr)
注釋掉的介面bridgeToObjectiveC 在早期的Swift版本中是可以用的。Xcode 6.4中已經去掉了。
命令列擷取Barcode結果:
var result : pBarcodeResultArray = dbr_decodeBarcodeFile(fileName) free(filePtr) println("Total barcode: \(String(result.move().iBarcodeCount))\n.......") var count = result.move().iBarcodeCountvar pBarcodeResult: pBarcodeResult = nilvar barcodeIndex = 1 // print barcode recognition resultsfor i in 0..<Int(count) { pBarcodeResult = result.move().ppBarcodes.advancedBy(i).move() println("Barcode: \(barcodeIndex++)") println("Page: \(String(pBarcodeResult.move().iPageNum))") var lFormat: __int64 = pBarcodeResult.move().llFormat var format = String.fromCString(GetFormatStr(lFormat)) println("Type: \(format!)") println("Value: \(String.fromCString(pBarcodeResult.move().pBarcodeData)!)") println(".......") } // free C memorydbr_release_memory(result)
用Swift建立Cocoa應用
在AppDelegate.swift中建立按鈕和文本控制項:
@IBOutlet weak var window: NSWindow!@IBOutlet weak var btLoad: NSButton!@IBOutlet weak var btRead: NSButton!@IBOutlet weak var text: NSTextField!@IBOutlet weak var filePath: NSTextField!
建立按鈕響應函數:
@IBAction func onClick(sender: NSButton) { var title = sender.title switch(title) { case "Load Barcode File": dispatch_async(dispatch_get_main_queue()) { self.openPanel() } break case "Read Barcode": if self.filePath.stringValue == "" { text.stringValue = "Please add image path!" return } println("default:" + self.filePath.stringValue) var dbr = DBR() text.stringValue = dbr.decodeFile(self.filePath.stringValue)! break default: break } }
在Interface Builder中把代碼和UI元素關聯起來:
使用NSOpenPanel 負載檔案:
func openPanel() { var openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = false openPanel.canCreateDirectories = false openPanel.canChooseFiles = true openPanel.beginWithCompletionHandler { (result) -> Void in if result == NSFileHandlingPanelOKButton { if let path = openPanel.URL?.path { self.filePath.stringValue = path } } } }
建立DBR.swift用於讀取條碼:
import Foundation class DBR { func decodeFile(file: String)->String? { var filePtr = strdup(file.cStringUsingEncoding(NSUTF8StringEncoding)!) var fileName: UnsafeMutablePointer<CChar> = UnsafeMutablePointer(filePtr) var result : pBarcodeResultArray = dbr_decodeBarcodeFile(fileName) free(filePtr) println("Total barcode: \(String(result.move().iBarcodeCount))\n.......") var count = result.move().iBarcodeCount var barcodeIndex = 1 var results: String = "" // print barcode recognition results for i in 0..<Int(count) { var pBarcodeResult = result.move().ppBarcodes.advancedBy(i).move() results += "Barcode: \(barcodeIndex++)\n" results += "Page: \(String(pBarcodeResult.move().iPageNum))\n" var lFormat: __int64 = pBarcodeResult.move().llFormat var format = String.fromCString(GetFormatStr(lFormat)) results += "Type: \(format!)\n" results += "Value: \(String.fromCString(pBarcodeResult.move().pBarcodeData)!)\n" results += ".......\n" } // free C memory dbr_release_memory(result) return results }}
運行1D/2D條碼應用:
源碼
https://github.com/yushulx/swift-barcode-reader
如何在Mac上使用Swift調用C介面開發條碼應用