虽然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().iBarcodeCount var pBarcodeResult: pBarcodeResult = nil var barcodeIndex = 1 // print barcode recognition results for 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 memory dbr_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