How to store a generic Measurement in Swift? - swift

I'd like to have a variable which can represent a single Measurement but may have different unit types. For example, it could store a length or a mass. It seems so simple, but I can't figure it out.
Here's what I tried:
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return weight
} else {
return length
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)
I also tried using <Dimension> as suggested in another answer, but that had a similar error.
This results in a compiler error:
error: cannot convert return expression of type 'Measurement<UnitMass>' to return type 'Measurement<Unit>'
Am I'm missing something obvious or is this just not possible?

You could just create new generic return values. This seems to compile OK for me.
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return Measurement<Unit>(value: weight.value, unit: weight.unit)
} else {
return Measurement<Unit>(value: length.value, unit: length.unit)
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)

First, don't make your own struct named Data, because Foundation already has a type named Data that's pretty widely used.
Second, since (in a comment) you said β€œIt's for weight loss, the user can choose a target as either a weight or a waist size”, it seems like you should probably model this using an enum rather than a struct:
enum WeightLossTarget {
case weight(Measurement<UnitMass>)
case waistSize(Measurement<UnitLength>)
}
Third, if you really need to use a struct, you can fall back to the Objective-C type NSMeasurement for non-generic type:
struct WeightLossTarget {
enum TargetType {
case weight
case waistSize
}
var weight: Measurement<UnitMass>
var waistSize: Measurement<UnitLength>
var target: TargetType
var valueOfTarget: NSMeasurement {
switch target {
case .weight: return weight as NSMeasurement
case .waistSize: return waistSize as NSMeasurement
}
}
}

Related

Cannot convert value of type '<Int>' to expected element type <Any>

I'm trying to learn swift, but I have a problem where using <Object> in Java would fix my problem I think, and the Apple doc says I should use <Any> but I keep getting errors.
I'm trying to build a memorize card game, I have the following models:
Theme.swift <- In charge of modeling different kind of themes for the cards, the idea is that the cards could have numbers, images etc, thats why it has a generic type after the name
import Foundation
import UIKit
struct Theme<Type> {
internal init(name: String, emojis: [Type], numberOfPairs: Int, cardsColor: UIColor) {
self.name = name
self.emojis = emojis
if(numberOfPairs > emojis.count || numberOfPairs < emojis.count) {
fatalError("Index out of bounds")
}
self.numberOfPairs = numberOfPairs
self.cardsColor = cardsColor
}
var name: String
var emojis: [Type]
var numberOfPairs: Int
var cardsColor: UIColor
}
I also have a Game model in charge of the game logic and cards model, I still have to implement a lot of stuff, but here's the code
import Foundation
struct Game {
var themes: [Theme<Any>]
var cards: [Card<Any>]
var score = 0
var isGameOver = false
var choosenTheme: Theme<Any>
init(themes: [Theme<Any>]) {
self.themes = themes
self.choosenTheme = self.themes.randomElement()!
cards = []
for index in 0..\<choosenTheme.numberOfPairs {
cards.append(Card(id: index*2, content: choosenTheme.emojis[index]))
cards.append(Card(id: index*2+1, content: choosenTheme.emojis[index]))
}
}
mutating func endGame() {
isGameOver = true
}
mutating func penalizePoints() {
score -= 1
}
mutating func awardPoints () {
score += 2
}
struct Card<T>: Identifiable {
var id: Int
var isFaceUP: Bool = false
var content: T
var isMatchedUP: Bool = false
var isPreviouslySeen = false
}
}
As you can notice I've used the Any type for creating an array of Cards and themes, cuz they could have strings, numbers or images
In my ViewModel I have the following code, where I'm trying to fill the Array of themes with two themes, one of string type of content, and the other one of Int:
import Foundation
import SwiftUI
class GameViewModel {
static let halloweenTheme = Theme<Int>(name: "WeirdNumbers", emojis: [1, 2, 4, 9, 20, 30], numberOfPairs: 6, cardsColor: .darkGray)
static let emojisTheme = Theme<String>(name: "Faces", emojis: ["πŸ₯°", "πŸ˜„", "😜", "πŸ₯³", "πŸ€“", "😎", "πŸ˜‹", "🀩"], numberOfPairs: 5, cardsColor: .blue)
var gameController: Game = Game(themes: [halloweenTheme, emojisTheme])
}
But I keep getting this or a similar error:
Cannot convert value of type 'Theme<Int>' to expected element type
'Array<Theme<Any>>.ArrayLiteralElement' (aka 'Theme<Any>')
Cannot convert value of type 'Theme<String>' to expected element type
'Array<Theme<Any>>.ArrayLiteralElement' (aka 'Theme<Any>')
And my mind is going crazy, I thought that by using [Theme<Any>] I would be able to have an array like this: [Theme<String>, Theme<Int>, Theme<Image>, ...] but it looks like not
Does anybody have a clue of what is going on here?
You can use a wrapper struct, basic example below. Note: if you need conformance to Codable, you need to implement encode/decode on your own.
struct Values<A> {
let value: A
}
struct Wrapper {
let wrappedValue: Values<Any>
}
class MyClass {
var wrappedValues: [Wrapper] = [Wrapper(wrappedValue: Values(value: "hello")), Wrapper(wrappedValue: Values(value: 1))]
}

Units and Measurements in Swift

I am trying to make a measurement entry keyboard class that has a text field for the value, and a picker for the unit.
I am having trouble using the Measurement type because of its generic behaviour.
I would like my class to return a new Measurement instance based on the initial value, but allowing the user to change the unit without necessarily converting it. I get the error "Cannot convert value of type 'Unit' to expected argument type 'T'" on the line where I am initialising meas with a value, and a unit of a different type.
Also, is there an way to iterate through the built-in sub units of a Dimension Type? i.e get all the sub units of UnitPressure for example.
If someone could just point me to an answer about similar generic behaviour, that would be much appreciated.
class MeasurementPicker<T : Dimension> {
init(initialUnit: Measurement<T>) {
self.initUnit = initialUnit
}
var initUnit: Measurement<T>
func getUnitList() -> [Unit]? {
switch initUnit.unit.self {
case is UnitPressure:
let retUnits: [Unit]? = [
UnitPressure.bars,
UnitPressure.gigapascals,
UnitPressure.hectopascals,
UnitPressure.inchesOfMercury,
UnitPressure.kilopascals,
UnitPressure.megapascals,
UnitPressure.millibars,
UnitPressure.millimetersOfMercury,
UnitPressure.newtonsPerMetersSquared,
UnitPressure.poundsForcePerSquareInch
]
return retUnits
default:
return nil
}
}
func getNewType(index: Int) -> Measurement<T> {
let myNewUnit : Unit = getUnitList()![index]
var meas = Measurement<T>.init(value: 6, unit: myNewUnit)
}
}
let x = Measurement(value: 5.5, unit: UnitPressure.kilopascals)
let y = MeasurementPicker<UnitPressure>(initialUnit: x)
let z = y.getNewType(index: 0)
print(z.unit.symbol)
There is no need to create a generic class. Just create a unit or dimension property. And in your method getNewType return Measurement<Unit> or just the unit or the dimension. You can also just use a subscript to get the dimension from your list:
class MeasurementPicker {
init(dimension: Dimension) { self.dimension = dimension }
var dimension: Dimension
var list: [Dimension] {
switch dimension.self {
case is UnitPressure:
return [UnitPressure.bars,
UnitPressure.gigapascals,
UnitPressure.hectopascals,
UnitPressure.inchesOfMercury,
UnitPressure.kilopascals,
UnitPressure.megapascals,
UnitPressure.millibars,
UnitPressure.millimetersOfMercury,
UnitPressure.newtonsPerMetersSquared,
UnitPressure.poundsForcePerSquareInch]
default: return []
}
}
}
Usage:
let y = MeasurementPicker(dimension: UnitPressure.kilopascals)
let z = y.list[0]
print(z.symbol) // "bar\n"

Type of expression is ambiguous without more context in Xcode 11

I'm trying to refer to an [Item] list within an #EnvironmentObject however when accessing it within a SwiftUI List, I get the error. What I don't understand is, this error doesn't pop up when following Apple's Landmark tutorial.
As far as I can tell, the [Item] list is loading correctly as I can print it out and do other functions with it. It just bugs out when using it for a SwiftUI List Is there something I've missed?
ItemHome.swift:
struct ItemHome : View {
#EnvironmentObject var dataBank: DataBank
var body: some View {
List {
ForEach(dataBank.itemList) { item in
Text("\(item.name)") // Type of expression is ambiguous without more context
}
}
}
}
Supporting code below:
Item Struct:
struct Item {
var id: Int
var uid: String
var company: String
var item_class: String
var name: String
var stock: Int
var average_cost: Decimal
var otc_price: Decimal
var dealer_price: Decimal
var ctc_price: Decimal
}
DataBank.swift:
final class DataBank : BindableObject {
let didChange = PassthroughSubject<DataBank, Never>()
var itemList: [Item] = load("itemsResults.json") {
didSet {
didChange.send(self)
}
}
}
func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
itemsResults.json:
[
{
"id": 1,
"uid": "a019bf6c-44a2-11e9-9121-4ccc6afe39a1",
"company": "Bioseed",
"item_class": "Seeds",
"name": "9909",
"stock": 0,
"average_cost": 0.0,
"otc_price": 0.0,
"dealer_price": 0.0,
"ctc_price": 0.0
},
{
"id": 2,
"uid": "a019bf71-44a2-11e9-9121-4ccc6afe39a1",
"company": "Pioneer",
"item_class": "Seeds",
"name": "4124YR",
"stock": 0,
"average_cost": 0.0,
"otc_price": 0.0,
"dealer_price": 0.0,
"ctc_price": 0.0
}
]
Apparently I missed making sure my models (Item in this case) conformed to the Identifiable protocol fixed it. Still, I wish Apple was more clear with their error messages.
As you mentioned in your answer, a ForEach needs a list of Identifiable objects. If you don't want to make your object implement that protocol (or can't for some reason), however, here's a trick:
item.identifiedBy(\.self)
I had the same problem and it wasn't something related to the line itself, it was related to the curly braces/brackets, so that if someone faced the same problem and doesn't know where the problem is, try to trace the curly braces and the brackets
To conform to Identifiable, just give the id or uid variable a unique value.
An easy way to do this is this:
var uid = UUID()
So your full struct would be:
struct Item: Identifiable {
var id: Int
var uid = UUID()
var company: String
var item_class: String
var name: String
var stock: Int
var average_cost: Decimal
var otc_price: Decimal
var dealer_price: Decimal
var ctc_price: Decimal
}
Xcode may show this error in many cases. Usually when using higher order functions (like map) and reason may be "anything".
There are two types:
Error is in higher order function. In this case your only option is to carefully read the block of code. Once again there may be different kinds of problems.
In some cases higher order function does not have any problem by itself, but there may be a compile time error in function that's called from the body of the higher order function.
Unfortunately Xcode does not point to this error sometimes.
To detect such errors and save lot of time, easy workaround is to temporarily comment higher order function and try to build. Now Xcode will show this error function and will show more reasonable error.

Mark closure element mutable Swift

I have 2 structs, first is:
struct LineData {
init (name: String,
colorValue: String,
values: [Int]){
self.name = name
self.colorValue = colorValue
self.values = values
}
private var cachedMaxValue: Int? = nil
let name: String
let colorValue: String
let values: [Int]
// describe max value for Y axis for specific Line
mutating func maxValue() -> Int{
if let cached = cachedMaxValue {
return cached
}
self.cachedMaxValue = values.max()
return cachedMaxValue ?? 0
}
}
Second have array of LineData structs:
struct CharData {
init(xAxis: XAxis,
lines: [LineData]){
self.xAxis = xAxis
self.lines = lines
}
private var cachedMaxValue: Int? = nil
var xAxis: XAxis
var lines: [LineData]
// describe max value for Y axis among lines
func maxValue() -> Int{
var maxValues: [Int] = []
lines.forEach{it in
maxValues.append(it.maxValue())
}
return 0
}
}
Code above not compile, because, of error on method maxValues for struct CharData. It says Cannot use mutating member on immutable value: 'it' is a 'let' constant
What i want is, iterate through an array of lines and among it max values find greater value.
Since lines is an ordinary array, how about simply:
for i in 0..<lines.count {
maxValues.append(lines[i].maxValue())
}
perhaps not quite as Swifty, but nothing gets copied. The optimizer ought to give you pretty much the same performance as forEach.
It's the it parameter/object in the forEach that's immutable. Just like the error says: "it is a let". You could probably do something like this:
lines.forEach { it in
var mutableIt = it
maxValues.append(mutableIt.maxValue())
}
It should be noted that this will create a mutable copy of the "it" struct instance.

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.