How to generate dataMatrix using CIFilter? - swift

I am getting error:
Value for key inputBarcodeDescriptor of type CIDataMatrixCodeDescriptor is not yet supported
let string = "tempValue&123"
let data = string.data(using: String.Encoding.ascii, allowLossyConversion: false)
guard let data = data else {
return nil
}
let descriptor = CIDataMatrixCodeDescriptor(payload: data, rowCount: 1, columnCount: 1, eccVersion: CIDataMatrixCodeDescriptor.ECCVersion(rawValue: 0))
let inputParameter = ["inputBarcodeDescriptor": descriptor]
let datafilter = CIFilter(name: "CIBarcodeGenerator", parameters: inputParameter)
let image = datafilter?.outputImage
print(image)

Well, I think you should just believe this runtime warning. You can't create a barcode using a CIDataMatrixCodeDescriptor; the class is documented but it isn't actually working. Use a different CIBarcodeDescriptor subclass instead (such as CIAztecCodeDescriptor).

wow, I've just ran into this issue... in 2022 :D
message is still
[api] Value for key inputBarcodeDescriptor of type CIDataMatrixCodeDescriptor is not yet supported
so I filed a feedback, let's hope for the best, as there's no lightweight alternative to generate DataMatrix codes that I could find

Related

Issue with UserDefaults (converting data to array and back)

What I want to do:
I want to get an array from UserDefaults that I saved beforehand and append a custom object to it. Afterwards I want to encode it as a Data-type again and set this as the UserDefaults Key again.
My problem:
The encoding part is what is not working as intended for me.
It says: -[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60000011a540
But I do not know how to fix this.
Below is my code for more context:
do {
let decoded = defaults.object(forKey: "ExArray") as! Data
var exo = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded) as! [Exerc]
exo.append(datas[indexPath.row])
let enco = try NSKeyedArchiver.archivedData(withRootObject: exo, requiringSecureCoding: false) <- Here is the error
defaults.set(enco, forKey: "ExArray")
} catch {
print("Error encoding custom object NOSEARCHO")
}
This is how Exerc looks:
struct Exerc: Codable {
var title: String
var exID: String
}
Seems like you are not using the archiver features, so why don't you just use the codable?
do {
let key = "ExArray"
let decoded = defaults.data(forKey: key)!
var exo = try JSONDecoder().decode([Exerc].self, from: decoded)
exo.append(datas[indexPath.row])
let enco = try JSONEncoder().encode(exo)
defaults.set(enco, forKey: key)
} catch {
print("Error encoding/decoding custom object NOSEARCHO", error)
}
It just a simple refactored MVP of the original code, but you can even work a bit on this and make it human readable right in the plist file!

iOS Swift - reading a persisted data model after having altered properties in that model

Thanks in advance for your help.
I want to persist data such as a user's stats. Let's say I have a data model, a class 'Stats' with a few properties, and it gets saved to the user's device. Supposing that I've released the app, users are recording their stats but then later on I want to make changes to the class - more or fewer properties, maybe even renaming them (etc.), ahead of a new build release. But after these changes have been made, the type 'Stats' is now different to the one the users have saved on their device, so it won't be able to decode and it seems like all the user's previous data up until that point would be lost/unattainable.
How can I add make these kinds of changes to the class in a way in which the PropertyListDecoder will still be able to decode the stats that are still on the user's device?
This is basically what I have:
class Stat: Codable {
let questionCategory = questionCategory()
var timesAnsweredCorrectly: Int = 0
var timesAnsweredFirstTime: Int = 0
var timesFailed: Int = 0
static func saveToFile(stats: [Stat]) {
let propertyListEncoder = PropertyListEncoder()
let encodedSettings = try? propertyListEncoder.encode(stats)
try? encodedSettings?.write(to: archiveURL, options: .noFileProtection)
}
static func loadFromFile() -> [Stat]? {
let propertyListDecoder = PropertyListDecoder()
if let retrievedSettingsData = try? Data(contentsOf: archiveURL), let decodedSettings = try? propertyListDecoder.decode([Stat].self, from: retrievedSettingsData) {
return decodedSettings
} else {
return nil
}
}
}
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let archiveURL = documentsDirectory.appendingPathComponent("savedVerbStats").appendingPathExtension("plist")
It seems that even just adding a new property to 'Stat' will cause the user's previous persisted data to become un-decodable as type 'Stat', and loadFromFile() will return nil.
Any advice would be great! I'm sure I'm going about this the wrong way. I figured that the array [Stat] would be too big to persist in UserDefaults but even then I think this problem would still exist... Can't find anything about it online; it seems that once you've got your users using a persisted class you can't then alter it. I tried using default values for the new properties but the result is the same.
The only solution I can think of is breaking down the class into literals and saving all these in some kind of a tuple/dictionary form instead. Then I would decode that raw data, and have a function to assemble and create the class out of whatever relevant data can still be taken from the old version of the 'Stat' type. Seems like a big workaround and I'm sure you guys know a much a better way.
Thanks!!
Removing a property is easy enough. Just delete its definition from the Stat class and existing data for that property will be deleted when you read and save stats again.
The key to adding new properties is to make them optional. For example:
var newProperty: Int?
When a previously existing stat is decoded the first time, this property will be nil, but all the other properties will be set correctly. You can set and save the new property as needed.
It may be a minor inconvenience to have all new properties as optional, but it opens the door to other possible migration schemes without losing data.
EDIT: Here is a more complicated migration scheme that avoids optionals for new properties.
class Stat: Codable {
var timesAnsweredCorrectly: Int = 0
var timesAnsweredFirstTime: Int = 0
var timesFailed: Int = 0
//save all stats in the new Stat2 format
static func saveToFile(stats: [Stat2]) {
let propertyListEncoder = PropertyListEncoder()
let encodedSettings = try? propertyListEncoder.encode(stats)
try? encodedSettings?.write(to: archiveURL, options: .noFileProtection)
}
//return all stats in the new Stat2 format
static func loadFromFile() -> [Stat2]? {
let propertyListDecoder = PropertyListDecoder()
//first, try to decode existing stats as Stat2
if let retrievedSettingsData = try? Data(contentsOf: archiveURL), let decodedSettings = try? propertyListDecoder.decode([Stat2].self, from: retrievedSettingsData) {
return decodedSettings
} else if let retrievedSettingsData = try? Data(contentsOf: archiveURL), let decodedSettings = try? propertyListDecoder.decode([Stat].self, from: retrievedSettingsData) {
//since we couldn't decode as Stat2, we decoded as Stat
//convert existing Stat instances to Stat2, giving the newProperty an initial value
var newStats = [Stat2]()
for stat in decodedSettings {
let newStat = Stat2()
newStat.timesAnsweredCorrectly = stat.timesAnsweredCorrectly
newStat.timesAnsweredFirstTime = stat.timesAnsweredFirstTime
newStat.timesFailed = stat.timesFailed
newStat.newProperty = 0
newStats.append(newStat)
}
return newStats
} else {
return nil
}
}
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let archiveURL = documentsDirectory.appendingPathComponent("savedVerbStats").appendingPathExtension("plist")
}
class Stat2: Stat {
var newProperty: Int = 0
}
Following on from Mike's response, I came up with a migration scheme that seems to solve the issue of the optionals and doesn't require any new classes every time the data model is altered. Part of the issue is that a developer may change or add properties to a class that gets persisted and Xcode would never flag this as an issue, which may result in your user's app trying to read the previous data class saved to the device, returning nil and in all likelihood overwriting all the data in question with a reformatted model.
Instead of writing the class (e.g. Stat) to the disk (which is what Apple suggests in its teaching resources), I save a new struct "StatData" which comprises only optional properties of the data that I want to write to the file:
struct StatData: Codable {
let key: String
let timesAnsweredCorrectly: Int?
let timesAnsweredFirstTime: Int?
let timesFailed: Int?
}
That way I can read the properties from the file and any added or deleted properties from the struct would just return nil instead of making the entire struct unreadable. Then I have two functions to convert 'StatData' into 'Stat' (and back), providing default values in case any have been returned nil.
static func convertToData(_ stats: [Stat]) -> [StatData] {
var data = [StatData]()
for stat in stats {
let dataItem = StatData(key: stat.key, timesAnsweredCorrectly: stat.timesAnsweredCorrectly, timesAnsweredFirstTime: stat.timesAnsweredFirstTime, timesFailed: stat.timesFailed)
data.append(dataItem)
}
return data
}
static func convertFromData(_ statsData: [StatData]) -> [Stat] {
// if any of these properties weren't previously saved to the device, they will return the default values but the rest of the data will remain accessible.
var stats = [Stat]()
for item in statsData {
let stat = stat.init(key: item.key, timesAnsweredCorrectly: item.timesAnsweredCorrectly ?? 0, timesAnsweredFirstTime: item.timesAnsweredFirstTime ?? 0, timesFailed: item.timesFailed ?? 0)
stats.append(stat)
}
return stats
}
I then call these functions when reading or saving the data to disk. The advantage of this is that I can choose which properties from the Stat class that I want to save, and because the StatData model is a struct, the memberwise initializer will warn any developer who changes the data model that they will also need to account for the change when reading old data from the file.
This seems to do the job. Any comments or other suggestions would be appreciated

Swift, Extracting Glyphs from a Font by Name

I am attempting to glyphs from the SF Symbol front using the code sample below provided by an extract https://github.com/davedelong/sfsymbols. I have isolated the code that I can't get to work with values extracted from a run time.
I have condensed the code down to only the statements required to produce the problem.
This issue seems to be the name I am providing to the final statement in CTFontGetGlyphWithName, Every combination of values I have attempted returns 0.
Please note that you have to have the SF Symbols application installed for it to acquire the initial bundle.
Please excuse the code quality, this is just to create the shortest possible example of code to reproduce the problem.
thanks.
I have followed quite a few snipped and tutorials but there doesn't seem to be a great deal of practical examples of these more advanced functions available.
public class StackOverflow {
public func Test() {
let maybeCFURLs = LSCopyApplicationURLsForBundleIdentifier("com.apple.SFSymbols" as CFString, nil)?.takeRetainedValue()
let cfURLs = maybeCFURLs as? Array<URL>
let fontFile = cfURLs!.compactMap { url -> URL? in
guard let appBundle = Bundle(url: url) else { return nil }
return appBundle.url(forResource: "SFSymbolsFallback", withExtension: "ttf")
}
let attributes = [
kCTFontTraitsAttribute: [
kCTFontWeightTrait: NSFont.Weight.regular.rawValue
]
]
let provider = CGDataProvider(url: fontFile.first! as CFURL)
let cgFont = CGFont(provider!)
let ctAttributes = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
let font = CTFontCreateWithGraphicsFont(cgFont!, 32, nil, ctAttributes)
// let name = "uni.plus.square.fill.medium" as CFString - Does not work
//var name = "square.fill.medium" as CFString - Does not work
let glyphName = "plus.square.fill.medium" as CFString
var glyph = CTFontGetGlyphWithName(font, glyphName )
}
}

How to properly use CFStringGetCString in swift?

I am looking at the docs for
CFStringGetCString
and AXUIElementCopyAttributeValue.
CFStringGetCString takes the param buffer: UnsafeMutablePointer<Int8>!
AXUIElementCopyAttributeValue takes the param value: UnsafeMutablePointer<CFTypeRef?>
For the latter, I can do a call like this:
var value: CFTypeRef?
let err = AXUIElementCopyAttributeValue(element, attribute as CFString, &value);
This satisfies the doc asking for an UnsafeMutablePointer of type CFTypeRef.
However I can't get the same logic to apply by doing
let buffer: Int8!
CFStringGetCString(attribute as! CFString, &buffer, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
I also tried
let buffer: Int8?
CFStringGetCString(attribute as! CFString, &buffer!, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
Either way, it complains about using buffer before it's initialized, even though it never complained about value in the working method with similar param requirements.
All the working examples I've seen for CFStringGetCString are using objective-c like syntax with *. Not sure what the proper swift way is here.
I also tried this way to get the value I wanted:
let app = AXUIElementCreateSystemWide();
var valueString = "";
var value: CFTypeRef?
// An exception on execution happens here when passing app
// Passing in the element I clicked instead of app
// yields error -25205 (attributeunsupported)
let err = AXUIElementCopyAttributeValue(app, "AXFocusedApplication" as CFString, &value);
if (err == AXError.success) {
valueString = value! as! NSString as String;
} else {
print("ERROR!");
print(err.rawValue);
}
return valueString;
Why are you torturing yourself with CFStringGetCString? If you have a CFString in Swift, you can cast it to a String and get a C string from that:
let cString: [Int8] = (cfString as String).cString(using: .utf8)!
Note also that the value of the kAXFocusedApplicationAttribute is not a CFString. It is an AXUIElement.
Here's my playground test:
import Foundation
import CoreFoundation
let axSystem = AXUIElementCreateSystemWide()
var cfValue: CFTypeRef?
AXUIElementCopyAttributeValue(axSystem, kAXFocusedApplicationAttribute as CFString, &cfValue)
if let cfValue = cfValue, CFGetTypeID(cfValue) == AXUIElementGetTypeID() {
let axFocusedApplication = cfValue
print(axFocusedApplication)
}
The first time I executed this playground, I got a system dialog box telling me that I need to give Xcode permission to control my computer. I went to System Preferences > Security & Privacy > Privacy > Accessibility, found Xcode at the bottom of the list, and turned on its checkbox.
Here's the output of the playground:
<AXUIElement Application 0x7fb2d60001c0> {pid=30253}
I assume you're on macOS since the AXUI API is only available on macOS. If you just want the name of the front application as a string, you can do this:
if let frontAppName = NSWorkspace.shared.frontmostApplication?.localizedName {
print(frontAppName)
}

How to initialize a MLMultiArray in CoreML

I've got an array of 40 arrays with 12 double features, so the type is [[double]]. Currently I'm sending this data to Google Cloud ML API to get a related prediction.
Since Apple recently introduced CoreML and coremltools, I converted my model from keras to .mlmodel to avoid thousand of google cloud api calls and do inference directly on my iPhone:
coreml_model = coremltools.converters.keras.convert(new_Model, input_names=['accelerations'],
output_names=['scores'])
coreml_model.save('PredictionModel.mlmodel')
After adding the model to my Xcode Project, it looks like:
I have no idea, where these others inputs and outputs are comming from.
To get a prediction, I need to convert my Array of Arrays of 12 doubles to an MLMultiArray, but I don't know how to do this. Has anyone faced a similar problem? Here is my current unfinished approach:
_predictionModel = PredictionModel()
guard let mlMultiArray = try? MLMultiArray(dataPointer: <#T##UnsafeMutableRawPointer#>, shape: <#T##[NSNumber]#>, dataType: <#T##MLMultiArrayDataType#>, strides: <#T##[NSNumber]#>, deallocator: <#T##((UnsafeMutableRawPointer) -> Void)?##((UnsafeMutableRawPointer) -> Void)?##(UnsafeMutableRawPointer) -> Void#>) else {
fatalError("Unexpected runtime error.")
}
guard let predictionOutput = try? _predictionModel.prediction(accelerations: mlMultiArray, lstm_1_h_in: nil, lstm_1_c_in: nil, lstm_2_h_in: nil, lstm_2_c_in: nil) else {
fatalError("Unexpected runtime error.")
}
The related documentation can be found here.
I achieved it by reading this blog :)
let data = _currentScaledMotionArrays.reduce([], +) //result is of type [Double] with 480 elements
guard let mlMultiArray = try? MLMultiArray(shape:[40,12], dataType:MLMultiArrayDataType.double) else {
fatalError("Unexpected runtime error. MLMultiArray")
}
for (index, element) in data.enumerated() {
mlMultiArray[index] = NSNumber(floatLiteral: element)
}
let input = PredictionModelInput(accelerations: mlMultiArray)
guard let predictionOutput = try? _predictionModel.prediction(input: input) else {
fatalError("Unexpected runtime error. model.prediction")
}
This is how I did it. Probably not the best way to handle optionals but gets the job done for testing
Create an instance of the MLMultiArray object with shape and data type
let mlArray = try? MLMultiArray(shape: [3], dataType: MLMultiArrayDataType.float32)
mlArray does not have an append function so you literally have to iterate through it and add values
for i in 0..<array.count {
mlArray?[i] = NSNumber(value: input[i])
}
Full function
func convertToMLArray(_ input: [Int]) -> MLMultiArray {
let mlArray = try? MLMultiArray(shape: [3], dataType: MLMultiArrayDataType.float32)
for i in 0..<array.count {
mlArray?[i] = NSNumber(value: input[i])
}
return arr!
}