If you don't want to use QRCode at all you'll have to create your own pattern to generate/reading the image.
But maybe you can use QRCode.
QRCode has an error correction level. Considering it you can still make your QRCode more aesthetically pleasing as you asked for. Just keep in mind "The higher the error correction level, the less storage capacity" and you can customize your image as long as the algorithm can get the info you need.
When you're generating the QRCode image you can do it like this:
Swift 3.1
private enum InputCorrectionLevel: String {
case low = "L" // 7%
case medium = "M" // 15%
case high = "Q" // 25%
case ultra = "H" // 30%
}
private enum QRCodeGenerationError {
case initializingFilter
case applyingFilter
}
func qrCode(from string: String, withSize frameSize: CGSize) throws -> CIImage {
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
throw QRCodeGenerationError.initializingFilter
}
let data = string.data(using: .isoLatin1, allowLossyConversion: false)
filter.setValue(data, forKey: "inputMessage")
filter.setValue(InputCorrectionLevel.low.rawValue, forKey: "inputCorrectionLevel")
guard let outputImage = filter.outputImage else {
throw QRCodeGenerationError.applyingFilter
}
let scaleX = frameSize.width / outputImage.extent.size.width
let scaleY = frameSize.height / outputImage.extent.size.height
let qrCodeCIImage = outputImage.applying(CGAffineTransform(scaleX: scaleX, y: scaleY))
return qrCodeCIImage
}