Format integer to minutes with new Foundation formatters? - swiftui

I'm trying to format an integer to display it as minutes, but I can't get the syntax right and the docs are still light on this. This is what I'm trying:
let value: Int = 20
Text(value.formatted(.components(style: .abbreviated, fields: [.minute])))
// Compile error: Instance method 'formatted' requires the types 'Int' and 'Date.ComponentsFormatStyle.FormatInput' (aka 'Range<Date>') be equivalent
I'm trying to display: 20 min. How can this be done using the new Foundation formatters improvements from WWDC 2021?
I can do this the old way like this:
static let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .short
formatter.allowedUnits = [.minute]
return formatter
}()
Self.formatter.string(for: DateComponents(minute: value))

to format an integer you need a NumberFormatter. Or you could just do:
Text("\(value) min")

Related

Swift 2 to 3 conversion error with Value of type '[Any]' has no member

I'm having a problem with a Swift 2 to 3 conversion piece of work and some of the remains syntax giving: Value of type '[Any]' has no member errors.
I was hoping someone could point me at a good solution.
Swift 2 code
Swift 2 code
func search() {
epsonPrinters = [Printer]()
starPrinters = [Printer]()
epson_startSearching()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { [unowned self] in
let devices = SMPort.searchPrinter()
self.starPrinters = devices.map { portInfo -> Printer in
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
if let name = portInfo.modelName as? String {
p.emulation = name.containsString("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}
}
}
Swift 3 Code (I've added comments above areas with errors)
func search() {
epsonPrinters = [Printer]()
starPrinters = [Printer]()
epson_startSearching()
DispatchQueue.global(qos: .background).async { [unowned self] in
let devices = SMPort.searchPrinter()
self.starPrinters = [devices.map { portInfo -> Printer in
// id, model and portName in below fails with messages like:
// Value of type '[Any]' has no member 'modelName'
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
// error on portInfo.modelName
// Value of type '[Any]' has no member 'modelName'
if let name = portInfo.modelName as? String {
p.emulation = name.containsString("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}!]
}
}
I know that I can replace the 'id:...' part with the likes of:
id: ((portInfo[0] as AnyObject).modelName) ?? "",
But this isn't correct because PortInfo can have none, 1 or multiples depending on the number of printers we find.
I'd appreciate any suggestions for refactoring this in an elegant way that is good Swift 3 syntax and likely to survive into Swift 4.
I'm working in Xcode 8.3.2
When you get some errors about types, you'd better check what type each variable has. When you select devices in the line let devices = ..., Quick Help of Xcode will show you something like this:
Declaration let devices: [Any]?
First, it's an Optional and you need to unwrap it, before using the actual content.
Second, the type of the elements in devices is Any, to which you cannot apply any methods (including property accessors). You need to cast it to an appropriate type at an appropriate place.
To solve the two things above, you can write something like this:
guard let devices = SMPort.searchPrinter() as? [PortInfo] else {
fatalError("This may never happen")
}
With guard statement above, Quick Help will show you:
Declaration let devices: [PortInfo]
Non-Optional, simple Array of PortInfo, so you can use any methods of PortInfo for the elements of this devices.
I would translate your Swift 2 code into Swift 3 like this:
func search() {
epsonPrinters = []
starPrinters = []
epson_startSearching()
DispatchQueue.global(qos: .default).async {
guard let devices = SMPort.searchPrinter() as? [PortInfo] else {
fatalError("This may never happen")
}
self.starPrinters = devices.map { portInfo -> Printer in
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
if let name = portInfo.modelName {
p.emulation = name.contains("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}
}
}
You may need some fixes (not many, I believe) to use this code, as you are not showing all relevant things such as the definition of Printer.

Swift thinks my string is an MDL material

I'm trying to save data in an image metadata in iOS/Swift3. It does not appear that CG will let you save out custom tags (is that true?) so I JSON encoded my dictionary and put the result as a string into the TIFF tag's ImageDescription. When I load the image and get the metadata back...
if let data = NSData(contentsOfFile:oneURL.path), let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) {
let allmeta = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as? [String : AnyObject]
The allMeta contains (among other things):
▿ 0 : 2 elements
- key : ImageDescription
- value : {
"CameraOrientationW" : 0.1061191,
"CameraOrientationZ" : -0.01305595,
"CameraOrientationX" : 0.01319851,
"CameraOrientationY" : 0.9941801
}
Which has the JSON data, yay! So now I simply have to get the TIFF metadata, get the ImageDescription from that, and de-JSON it...
let tiffmeta = allmeta?["{TIFF}"]
if let tiffMeta = tiffmeta {
let descmeta = tiffMeta["ImageDescription"]
var descdata = descmeta?.data(usingEncoding: NSUTF8StringEncoding)!
let descdict = try? JSONSerialization.jsonObject(with: descdata, options: [])
But this will not compile. Xcode puts an error on the let descdata line:
Value of type 'MDLMaterialProperty??' has no member 'data'
I tried casting it to String on the line above, at which point it complains I didn't unwrap the optional MDLMaterialProperty.
Am I missing something obvious here?
So just to close this one, this appears to be a problem in the compiler. I made a number of minor changes to the syntax, nothing that had any actual effect on the code, and suddenly it decided the object was indeed a string.

cast from float to nsdecimalnumber always fails swift

I am reading my data through a web service that returns a percentage as a string and I need to format it properly in my app. Currently I receive an error message that reads "Argument labels '(_:)' do not match any available overloads".
Suggestions on how to resolve this issue?
if let dUnInsured = result[0]["UnInsured"] as? String, let doubleNum = Double(dUnInsured) {
let sUnInsured = dollarFormatter.string(from: (NSDecimalNumber(Decimal(doubleNum))))!
self.inUninsured.text = sUnInsured
}
Try like this way.
if let dUnInsured = result[0]["UnInsured"] as? String, let doubleNum = Double(dUnInsured) {
let sUnInsured = dollarFormatter.string(from: (NSNumber(value: doubleNum)))!
self.inUninsured.text = sUnInsured
}

Scala.js js.Dynamic usage leads to a recursively infinite data structure

I'm trying to use Google Visualizations from Scala.js. I generated the type definitions using TS importer and the relevant portion it generated is:
#js.native
trait ColumnChartOptions extends js.Object {
var aggregationTarget: String = js.native
var animation: TransitionAnimation = js.native
var annotations: ChartAnnotations = js.native
// ... more
}
#js.native
trait TransitionAnimation extends js.Object {
var duration: Double = js.native
var easing: String = js.native
var startup: Boolean = js.native
}
Now, I'm trying to figure out how to actually use this and came up with:
val options = js.Dynamic.literal.asInstanceOf[ColumnChartOptions]
options.animation = js.Dynamic.literal.asInstanceOf[TransitionAnimation] // comment this and the next line and chart will appear
options.animation.duration = 2000
options.title = "Test Chart"
options.width = 400
options.height = 300
This works if I don't set the animation settings, but fails with the chart showing "Maximum call stack size exceeded" if I do.
I debugged, and found the following:
So animation contains a reference to itself, but I don't feel like this should happen based on the code above.
Ideas how to fix it?
Any other suggestions on how to best use the generated types to provide a type-safe way of creating the JavaScript objects which Google Visualizations expects? I tried new ColumnChartOptions {} which looks cleaner than js.Dynamic but that failed with "A Scala.js-defined JS class cannot directly extend a native JS trait."
P.S. I'd like to note that
options.animation = js.Dynamic.literal(
easing = "inAndOut",
startup = true,
duration = 2000
).asInstanceOf[TransitionAnimation]
actually works, but isn't type-safe (a mis-spelling of duration to durration won't be caught).
Your code lacks () when calling literal(), so the fix would be:
val options = js.Dynamic.literal().asInstanceOf[ColumnChartOptions]
options.animation = js.Dynamic.literal().asInstanceOf[TransitionAnimation] // comment this and the next line and chart will appear
In Scala (and therefore in Scala.js), the presence or absence of () is sometimes meaningful. literal is the singleton object literal, whereas literal() calls the method apply() of said object.

iOS-NSArray filter having condition

I'm new here!
I have an NSArray like this ("file01_header","file01_body", "file01_xxx", ... ,"file02_header", ...).
I filter it and get 2 distinct NSArrays:
Array_header ("file01_header","file02_header",...)
Array_body
("file01_body", "file02_body", ...)
now I have to subtract from Array_header all the items that have a corresponding one in Array_body, because not all items have a fileNN_body for the fileNN_header in the original array, and I have to give the Array_header as output with only items that has no corresponding body.
How can I do this?
Thanks!!
If you're using swift you can filter pretty easily using a closure and some minor string-fu.
Here's an example:
let funkyArray = ["file01_header","file01_body", "file01_xxx", "file02_header","file03_xxx"]
let headerFilterClosure : (String) -> Bool = {fileName in
if fileName.containsString("header") {
let fileBase = fileName.componentsSeparatedByString("_")[0]
let fileBodyName = "\(fileBase)_body"
if funkyArray.contains(fileBodyName) {
return true
}
}
return false
}
funkyArray.filter(headerFilterClosure)
I think a better long term solution would be to not have such a funky array and use a data structure or at least a tuple with options to manage your file info.
Best,
Josh