34

I am a new iOS developer. I was wondering how can I generate a barcode in Swift.

I have the code already, there are multiple resources from where to learn how to read a barcode, but I didn't find any that talks about generating one from a string.

Thanks a lot!

P.S. I know there is a similar question about this, but it's for Objective-C. I don't know Obj-C and I find it difficult coming from .NET.

Tamás Sengel
  • 47,657
  • 24
  • 144
  • 178
Andrei Dobrin
  • 1,035
  • 4
  • 15
  • 31

4 Answers4

85

You could use a CoreImage (import CoreImage) filter to do that!

    class Barcode {
        class func fromString(string : String) -> UIImage? {
             let data = string.data(using: .ascii)
             if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
                  filter.setValue(data, forKey: "inputMessage")
                  if let outputCIImage = filter.outputImage {
                       return UIImage(ciImage: outputCIImage)
                  }
             }
             return nil
        }
    }

    let img = Barcode.fromString("whateva")

A newer version, with guard and failable initialiser:

extension UIImage {

    convenience init?(barcode: String) {
        let data = barcode.data(using: .ascii)
        guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
            return nil
        }
        filter.setValue(data, forKey: "inputMessage")
        guard let ciImage = filter.outputImage else {
            return nil
        }
        self.init(ciImage: ciImage)
    }

}

Usage:

let barcode = UIImage(barcode: "some text") // yields UIImage?

According to the docs :

Generates an output image representing the input data according to the ISO/IEC 15417:2007 standard. The width of each module (vertical line) of the barcode in the output image is one pixel. The height of the barcode is 32 pixels. To create a barcode from a string or URL, convert it to an NSData object using the NSASCIIStringEncoding string encoding.

Matteo Pacini
  • 17,507
  • 3
  • 58
  • 70
  • 2
    Amazing !! But then, I wonder why people create more or less complex libraries to handle barcode generation. Where are differences between this and libs ? – PAD Oct 02 '15 at 16:42
  • 1
    @PAD Well, these Core Image filters are quite new (iOS >= 8.0). Before iOS 8, generating barcodes would have been done through these libraries I guess. – Matteo Pacini Oct 03 '15 at 09:21
  • It should be noted there are three different types of Code128. This implementation uses subtype C. If you are looking to generate Code 128 type A or B, you may need to look into the ZXing library. – AtomicBoolean Jun 29 '16 at 17:58
  • Amazing !! Thanks, If I want to create other barcodes like UPCA, UPCE with HRI values is it possible with this? – Dhaval Jun 07 '17 at 06:27
  • what is the CIFilter name for Barcode Code28, Code39 and CODABAR? – JkAlombro Sep 19 '18 at 02:27
  • This doesn't generate a barcode with the source string below like [this](https://www.computalabel.com/Images/C128.png). – Gerardo Cauich Apr 17 '20 at 10:28
  • Starting with iOS 13 (not 100% sure though) you can `import CoreImage.CIFilterBuiltins` and use something like `let filter = CIFilter.code128BarcodeGenerator()` which provides properties for the relevant keys. The key `inputMessage` becomes `filter.message = …` – D. Mika Apr 26 '21 at 15:00
17

Improved code:

  • Barcode scaling
  • Set barcode image margin
  • Convert the UIImage to NSData (for some reason it wasn't possible with the code above).
  • It won't fail when sharing the barcode image (probably because of the same bug)

Swift 3

func generateBarcode(from string: String) -> UIImage? {

    let data = string.data(using: String.Encoding.ascii)

    if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
        filter.setDefaults()
        //Margin
        filter.setValue(7.00, forKey: "inputQuietSpace")
        filter.setValue(data, forKey: "inputMessage")
        //Scaling
        let transform = CGAffineTransform(scaleX: 3, y: 3)

        if let output = filter.outputImage?.applying(transform) {
            let context:CIContext = CIContext.init(options: nil)
            let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
            let rawImage:UIImage = UIImage.init(cgImage: cgImage)

            //Refinement code to allow conversion to NSData or share UIImage. Code here:
            //http://stackoverflow.com/questions/2240395/uiimage-created-from-cgimageref-fails-with-uiimagepngrepresentation
            let cgimage: CGImage = (rawImage.cgImage)!
            let cropZone = CGRect(x: 0, y: 0, width: Int(rawImage.size.width), height: Int(rawImage.size.height))
            let cWidth: size_t  = size_t(cropZone.size.width)
            let cHeight: size_t  = size_t(cropZone.size.height)
            let bitsPerComponent: size_t = cgimage.bitsPerComponent
            //THE OPERATIONS ORDER COULD BE FLIPPED, ALTHOUGH, IT DOESN'T AFFECT THE RESULT
            let bytesPerRow = (cgimage.bytesPerRow) / (cgimage.width  * cWidth)

            let context2: CGContext = CGContext(data: nil, width: cWidth, height: cHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: cgimage.bitmapInfo.rawValue)!

            context2.draw(cgimage, in: cropZone)

            let result: CGImage  = context2.makeImage()!
            let finalImage = UIImage(cgImage: result)

            return finalImage

        }
    }

    return nil
}
castillejoale
  • 469
  • 4
  • 12
10

If your deployment target is at least iOS 8, you can use Core Image. Here is my BarcodeGenerator class (you need to import CoreImage):

class BarcodeGenerator {
    enum Descriptor: String {
        case code128 = "CICode128BarcodeGenerator"
        case pdf417 = "CIPDF417BarcodeGenerator"
        case aztec = "CIAztecCodeGenerator"
        case qr = "CIQRCodeGenerator"
    }

    class func generate(from string: String, 
                         descriptor: Descriptor, 
                               size: CGSize) -> CIImage? {
        let filterName = descriptor.rawValue

        guard let data = string.data(using: .ascii),
            let filter = CIFilter(name: filterName) else {
                return nil
        }

        filter.setValue(data, forKey: "inputMessage")

        guard let image = filter.outputImage else {
            return nil
        }

        let imageSize = image.extent.size

        let transform = CGAffineTransform(scaleX: size.width / imageSize.width,
                                               y: size.height / imageSize.height)
        let scaledImage = image.transformed(by: transform)

        return scaledImage
    }
}

It can be used like this

BarcodeGenerator.generate(from: "barcode-string", 
                     descriptor: .code128, 
                          size: CGSize(width: 800, height: 300))
MilanPanchal
  • 2,865
  • 1
  • 17
  • 34
Luca Torella
  • 7,094
  • 4
  • 35
  • 47
  • 1
    what is the CIFilter name for Barcode Code28, Code39 and CODABAR? – JkAlombro Sep 19 '18 at 01:32
  • only those 4 descriptors are supported as you can see here https://developer.apple.com/documentation/coreimage/cibarcodedescriptor – Luca Torella Sep 19 '18 at 07:58
  • Unless I'm missing something they seem to have replaced code128 with dataMatrix as of Jan 19 – ChrisH Jan 17 '19 at 17:59
  • if you go to the link i posted above, I still see code128 and I don't see DataMatrix. – Luca Torella Jan 18 '19 at 13:47
  • this is code128 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CICode128BarcodeGenerator – Luca Torella Jan 18 '19 at 13:47
  • @LucaTorella When I detect barcode or qrCode with AVCaptureSession, you know how I get the descriptor? In order to use your class and recreate barcode on my app – Szanownego Jun 03 '19 at 08:52
  • each AVMetadataObject has a ObjectType property. You need to map this enum to the Descriptor. Check https://developer.apple.com/documentation/avfoundation/avmetadataobject/objecttype – Luca Torella Jun 04 '19 at 09:19
0

Use like this,

func createBarcodeFromString(barcode:String)->UIImage?{

    let data = self.data(using: .isoLatin1)
    guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
        return nil
    }
    filter.setValue(data, forKey: "inputMessage")
    filter.setValue(7.0, forKey:"inputQuietSpace")
    guard var ciImage = filter.outputImage else {
        return nil
    }

    let imageSize = ciImage.extent.integral
    let outputSize = CGSize(width:320, height: 60)
    ciImage = ciImage.transformed(by:CGAffineTransform(scaleX: outputSize.width/imageSize.width, y: outputSize.height/imageSize.height))

    let image = convertCIImageToUIImage(ciimage: ciImage)
    return image
}

func convertCIImageToUIImage(ciimage:CIImage)->UIImage{
    let context:CIContext = CIContext.init(options: nil)
    let cgImage:CGImage = context.createCGImage(ciimage, from: ciimage.extent)!
    let image:UIImage = UIImage.init(cgImage: cgImage)
    return image
}