Search code examples
iosswiftcifilter

Generating QR Code for vEvent gives error "No usable data found"


I am trying to generate a QR code, that when scanned, will prompt the user to add an event to their calendar. I know that this is possible as I've used services like qrmonkey to generate codes that my iOS device will prompt me to do this.

I've created a function that successfully generates a QR code and is scannable. The function is below.

func generateCalendarEvent(summary: String, location: String?, url: String?, description: String?, startDate: Date, endDate: Date) -> UIImage? {
        //Generate Event String
        var eventString: String = "BEGIN:VEVENT"
        eventString.append("\nSUMMARY:\(summary)")
        if let locationString: String = location {
            eventString.append("\nLOCATION:\(locationString)")
        }
        if let urlString: String = url {
            eventString.append("\nURL:\(urlString)")
        }
        if let descriptionString: String = description {
            eventString.append("\nDESCRIPTION:\(descriptionString)")
        }
        eventString.append("\nDTSTART:\(startDate.formatted(format: "yyyymmdd'T'hhmmss"))")
        eventString.append("\nDTEND:\(endDate.formatted(format: "yyyymmdd'T'hhmmss"))")
        eventString.append("\nEND:VEVENT")

        //Setup Filter
        guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
            return nil
        }
        filter.setValue(eventString.data(using: .utf8), forKey: "inputMessage")
        
        //Setup Transform
        let transform = CGAffineTransform(scaleX: 3, y: 3)
        
        guard let output = filter.outputImage?.transformed(by: transform) else {
            return nil
        }
        return UIImage(ciImage: output)
    }

When I scan the generated QR code though, it gives me an error on the iOS device saying "No usable data found".


Solution

  • You didn't show what you're using for .formatted(format: "yyyymmdd'T'hhmmss")...

    But, assuming it produces the string you're expecting, the problem appears to be the format of DTSTART AND DTEND.

    You are using ":" where you should be using ";":

    DTSTART:
    DTEND:
    

    should be:

    DTSTART;
    DTEND;
    

    Try this... if it works, try it with your .formatted(...) extension:

    func generateCalendarEvent(summary: String, location: String?, url: String?, description: String?, startDate: Date, endDate: Date) -> UIImage? {
        //Generate Event String
        var eventString: String = "BEGIN:VEVENT"
        eventString.append("\nSUMMARY:\(summary)")
        if let locationString: String = location {
            eventString.append("\nLOCATION:\(locationString)")
        }
        if let urlString: String = url {
            eventString.append("\nURL:\(urlString)")
        }
        if let descriptionString: String = description {
            eventString.append("\nDESCRIPTION:\(descriptionString)")
        }
        
        let df = DateFormatter()
        df.dateFormat = "yyyymmdd'T'hhmmss"
    
        eventString.append("\nDTSTART;VALUE=DATE:\(df.string(from: startDate))")
        eventString.append("\nDTEND;VALUE=DATE:\(df.string(from: startDate))")
        
        eventString.append("\nEND:VEVENT")
        
        //Setup Filter
        guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
            return nil
        }
        filter.setValue(eventString.data(using: .utf8), forKey: "inputMessage")
        
        //Setup Transform
        let transform = CGAffineTransform(scaleX: 3, y: 3)
        
        guard let output = filter.outputImage?.transformed(by: transform) else {
            return nil
        }
        return UIImage(ciImage: output)
    }