Adding Multiple Key-Value Pairs to QR Code - ios

I'm creating QR Codes in my app and I'm wanting to know if it's possible to add a second key-value pair. Right now I have a 12 digit number for the "inputMessage" key and I'm wanting to have some other data for another key. For example, using another string for a new key called "gym".
Here is my function for creating the QR Code:
func generateCode() {
let gymData = gymName.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)
let codeNumberData = generateRandomNumber(12).dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(codeNumberData, forKey: "inputMessage")
// filter?.setValue(gymData, forKey: "gym")
filter?.setValue("Q", forKey: "inputCorrectionLevel")
let qrCodeImage = filter?.outputImage
let context = CIContext(options: nil)
let cgImage = context.createCGImage(qrCodeImage!, fromRect: (qrCodeImage?.extent)!)
let image = UIImage(CGImage: cgImage, scale: 1.0, orientation: .Up)
let resized = resizeImage(image, withQuality: CGInterpolationQuality.None, rate: 5.0)
codeImageView.image = resized
}

The CIFilter is only expecting to generate the QR code from inputMessage, so you need to create a single aggregate inputMessage and pass that to the filter. One fairly straight forward way of doing this is to create a Dictionary from your inputs, serialize it into a NSData blob using the NSKeyedArchiver, and then set the result as your inputMessage.
func generateCode() {
var aggregateData = [String: NSData]()
if let gymData = gymName.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false) {
aggregateData.updateValue(gymData, forKey: "gymData")
}
if let codeNumberData = generateRandomNumber(12).dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false) {
aggregateData.updateValue(codeNumberData, forKey: "codeNumberData")
}
let archived = NSKeyedArchiver.archivedDataWithRootObject(aggregateData)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(archived, forKey: "inputMessage")
filter?.setValue("Q", forKey: "inputCorrectionLevel")
let qrCodeImage = filter?.outputImage
let context = CIContext(options: nil)
let cgImage = context.createCGImage(qrCodeImage!, fromRect: (qrCodeImage?.extent)!)
let image = UIImage(CGImage: cgImage, scale: 1.0, orientation: .Up)
let resized = resizeImage(image, withQuality: CGInterpolationQuality.None, rate: 5.0)
codeImageView.image = resized
}
Of course this means that on the receiving end, you'll need to expect the payload to be a dictionary, and access the individual components by their keys. Should look something like this.
guard let inputData = scannedQrString.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false),
dictionary = NSKeyedUnarchiver.unarchiveObjectWithData(inputData) as? [String: NSData] else {
return
}
let gymData = dictionary["gymData"]
let codeNumberData = dictionary["codeNumberData"]

Related

Swift - Image Data From CIImage QR Code / How to render CIFilter Output

I've been having this problem for a while now and looked at dozens of answers here and can't seem to find anything that helps.
Scenario
I am generating a QR Code on the iOS side of my app and want this QR code to be sent to the WatchKit Extension that I am currently developing.
How I am generating the QR Code
func createQR(with string: String) {
if let filter = CIFilter(name: "CIQRCodeGenerator") {
//set the data to the contact data
filter.setValue(string, forKey: "inputMessage")
filter.setValue("L", forKey: "inputCorrectionLevel")
if let codeImage = filter.outputImage {
return UIImage(ciImage: codeImage);
}
}
}
What I want next
I want to get the data from the QR image so that I can send it to the Apple Watch app, like so:
let data = UIImagePNGRepresentation(QRCodeImage);
But, This always returns nil because there is no image data backing the output from the filter.
Note: I know that there is no data associated with the CI Image because it hasn't been rendered and doesn't even have data associated with it because it's just the output from the filter. I don't know how to get around this because I'm pretty new to image processing and such. :/
What I've Tried
Creating a cgImage from the filter.outputImage
func createQR(with string: String) {
if let filter = CIFilter(name: "CIQRCodeGenerator") {
//set the data to the contact data
filter.setValue(contactData, forKey: "inputMessage")
filter.setValue("L", forKey: "inputCorrectionLevel")
if let codeImage = filter.outputImage {
let context = CIContext(options: nil)
if let cgImage = context.createCGImage(codeImage, from: codeImage.extent) {
self.QRCode = UIImage(cgImage: cgImage)
}
}
}
}
But this doesn't work, it doesn't seem, because the image on the view is blank.
Creating a blank CIImage as Input Image
func update(with string: String) {
let blankCiImage = CIImage(color: .white) //This probably isn't right...
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(contactData, forKey: "inputMessage")
filter.setValue("L", forKey: "inputCorrectionLevel")
filter.setValue(blankCiImage, forKey: kCIInputImageKey)
if let codeImage = filter.outputImage {
let context = CIContext(options: nil)
if let cgImage = context.createCGImage(codeImage, from: codeImage.extent) {
self.contactCode = UIImage(cgImage: cgImage)
print(self.contactCode!)
print(UIImagePNGRepresentation(self.contactCode!))
}
}
}
}
This doesn't work either - my thought was to add a blank image to it and then do the filter on top of it, but I am probably not doing this right.
My Goal
Literally, just to get the data from the generated QR Code. Most threads suggest UIImage(ciImage: output) , but this doesn't have any backing data.
If anyone could help me out with this, that'd be great. And any explanation on how it works would be wonderful too.
Edit: I don't believe this is the same as the marked duplicate - The marked duplicate is about editing an existing image using CI filters and getting that data and this is about an image that is solely created through CI filter with no input image - QR Codes. the other answer did not fully relate.
You have a couple of issues in your code. You need to convert your string to data using String Encoding isoLatin1 before passing it to the filter. Another issue is that to convert your CIImage to data you need to redraw/render your CIImage and to prevent blurring the image when scaled you need to apply a transform to the image to increase its size:
extension StringProtocol {
var qrCode: UIImage? {
guard
let data = data(using: .isoLatin1),
let outputImage = CIFilter(name: "CIQRCodeGenerator",
parameters: ["inputMessage": data, "inputCorrectionLevel": "M"])?.outputImage
else { return nil }
let size = outputImage.extent.integral
let output = CGSize(width: 250, height: 250)
let format = UIGraphicsImageRendererFormat()
format.scale = UIScreen.main.scale
return UIGraphicsImageRenderer(size: output, format: format).image { _ in outputImage
.transformed(by: .init(scaleX: output.width/size.width, y: output.height/size.height))
.image
.draw(in: .init(origin: .zero, size: output))
}
}
}
extension CIImage {
var image: UIImage { .init(ciImage: self) }
}
Playground testing:
let link = "https://stackoverflow.com/questions/51178573/swift-image-data-from-ciimage-qr-code-how-to-render-cifilter-output?noredirect=1"
let image = link.qrCode!
let data = image.jpegData(compressionQuality: 1) // 154785 bytes

How to get a quality barcode image from string in ios, swift

I'm generating an image with barcode using its string like below.
class BarCode {
class func fromString(string : String) -> UIImage? {
let data = string.dataUsingEncoding(NSASCIIStringEncoding)
let filter = CIFilter(name: "CICode128BarcodeGenerator")
filter!.setValue(data, forKey: "inputMessage")
return UIImage(CIImage: filter!.outputImage!)
}
}
so this generates a accurate image. but the quality is low. how can I increase the quality of the image.(I cant increase the size of the image, if I do so it looks liked blured)
When the CIImage is converted to a UIImage it does so with a fixed size that is determined by the CIImage, if you subsequently try to scale this image up, say by assigning it to a UIImageView, then you will get the typical pixellation associated with scaling up a bitmap.
Transform the image before assigning it to the UIImage
if let barImage = filter.outputImage {
let transform = CGAffineTransformMakeScale(5.0, 5.0)
let scaled = barImage.imageByApplyingTransform(transform)
return(UIImage(CIImage: scaled))
}
Try transform to make it bigger
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.applying(transform) {
return UIImage(ciImage: output)
}
my two cents for OSX :)
func barCodeFromString(string : String) -> NSImage? {
let data = string.data(using: .ascii)
guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else{
return nil
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage : CIImage = filter.outputImage else{
return nil
}
let transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
let scaled = ciImage.transformed(by: transform)
let rep = NSCIImageRep(ciImage: scaled)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}

Can not get info out of generated QRCode

I am trying to generate QRCode with multiple strings. It is working, however the generated image QRCode is too small inside the imageView so it is impossible to read it in(At least I think that is why I can't get info out of it).
This is how it looks:
And like this I generate it:
func generateQRWithInfo(){
var aggregateData = [String: NSData]()
if let firstName = firstName?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(firstName as NSData, forKey: "firstName")
}
if let lastName = lastName?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(lastName as NSData, forKey: "lastName")
}
if let job = job?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(job as NSData, forKey: "job")
}
if let organization = organization?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(organization as NSData, forKey: "organization")
}
if let mobilePhone = mobilePhone?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(mobilePhone as NSData, forKey: "mobilePhone")
}
if let workPhone = workPhone?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(workPhone as NSData, forKey: "workPhone")
}
if let email = email?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false) {
aggregateData.updateValue(email as NSData, forKey: "email")
}
let archived = NSKeyedArchiver.archivedData(withRootObject: aggregateData)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(archived, forKey: "inputMessage")
filter?.setValue("Q", forKey: "inputCorrectionLevel")
let qrCodeImage = filter?.outputImage
let context = CIContext(options: nil)
//let cgImage = context.createCGImage(qrCodeImage!, from: (qrCodeImage?.extent)!)
let transform = CGAffineTransform(scaleX: 50,y: 50)
let output = filter?.outputImage?.applying(transform)
let newImage = UIImage(ciImage: output!)
qrImageView.image = newImage
}
I do not know if it is how it should be but I can't get info out of it. What I am doing wrong?
QR Codes holds lots of data based on these parameters.
Data type
Size a.k.a pixels
Error correction level
Data type can be Numeric, Alphanumeric and Binary.
Error correction level can be categorised as Type L,M,Q and H based on loss recovery possible.
so as per your case you want to generate 30*30 alphanumeric so obviously you cant store more then allowed values. So make it bigger or reduce the data. To make a note all the QR code readers are not same.
For more info check this table

Unable to convert CIImage to UIImage in Swift 3.0

I am making image form QR Code by using following code:
func createQRFromString(str: String) -> CIImage? {
let stringData = str.dataUsingEncoding(NSUTF8StringEncoding)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(stringData, forKey: "inputMessage")
filter?.setValue("H", forKey: "inputCorrectionLevel")
return filter?.outputImage
}
And Then I am adding to UIImageView Like this:
if let img = createQRFromString(strQRData) {
let somImage = UIImage(CIImage: img, scale: 1.0, orientation: UIImageOrientation.Down)
imgviewQRcode.image = somImage
}
Now I need to save this to a JPEG or PNG file. But when I am doing so my app crashes:
#IBAction func btnSave(sender: AnyObject) {
// // Define the specific path, image name
let documentsDirectoryURL = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
// create a name for your image
let fileURL = documentsDirectoryURL.URLByAppendingPathComponent("image.jpg")
if let image = imgviewQRcode.image // imgviewQRcode is UIImageView
{
if let path = fileURL?.path
{
if !NSFileManager.defaultManager().fileExistsAtPath(fileURL!.path!)
{
if UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
{
print("file saved")
}
}//Checking existing file
}//Checking path
}//CHecking image
}
Crash Point
UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
Reason
fatal error: unexpectedly found nil while unwrapping an Optional value
Debug Tests:
func convert(cmage:CIImage) -> UIImage
{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
Use this function to convert CIImage to UIImage . It works .
func convert(image:CIImage) -> UIImage
{
let image:UIImage = UIImage.init(ciImage: image)
return image
}
Perhaps, this was unavailable before, but it is now possible to create UIImages directly from CIImage.
My final code
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
}
return nil
}

How to generate a EAN13 barcode?

I'm trying to create a simple EAN13 image to show a barcode from a String.
I tried with this code but it can only generate a code128. What can I use to generate a EAN13?
class Barcode {
class func fromString(string : String) -> UIImage? {
let data = string.dataUsingEncoding(NSASCIIStringEncoding)
let filter = CIFilter(name: "CICode128BarcodeGenerator")
filter.setValue(data, forKey: "inputMessage")
return UIImage(CIImage: filter.outputImage)
}
}
let img = Barcode.fromString("1234567890123")
you can try this EAN13BarcodeGenerator
Usage is pretty simple:
BarCodeView *barCodeView = [[BarCodeView alloc] initWithFrame:kBarCodeFrame];
[self.view addSubview:barCodeView];
[barCodeView setBarCode:GetNewRandomEAN13BarCode()];
my two cents for osx..
func barCodeFromString(string : String, destSize: NSSize) -> NSImage? {
let data = string.data(using: .ascii)
guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else{
return nil
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage : CIImage = filter.outputImage else{
return nil
}
let c_size = ciImage.extent.size
let w_ratio = destSize.width/c_size.width
let h_ratio = destSize.height/c_size.height
let ratio = w_ratio>h_ratio ? h_ratio : w_ratio
let transform = CGAffineTransform(scaleX: ratio, y: ratio)
let scaled = ciImage.transformed(by: transform)
let rep = NSCIImageRep(ciImage: scaled)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}

Resources