"Expression type 'Bool' is ambiguous without more context" in ternary operation - swift

I am trying to pass an enum to a function that does an operation on that enum's arguments. I receive this error:
Expression type 'Bool' is ambiguous without more context
The same error happens in an equivalent if clause, so it's not the ternary operator itself that causes the problem.
enum auto {
case pkw (SerialNumber: String, Axles: Int, Weight: Float)
case lkw (SerialNumber: String, Axles: Int, Weight: Float)
}
func checkIntegrity(car: auto) -> Bool {
switch car {
case .pkw:
if (checkSerialNumber(serialNumber: .pkw.SerialNumber.rawValue)
&& checkWeight(weight: .pkw.Weight.rawValue)) { // Error here, "&&" is underlined
return true
} else {
return false
}
break;
case .lkw:
return (checkSerialNumber(serialNumber: .lkw.SerialNumber.rawValue)
&& checkWeight(weight: .lkw.Weight.rawValue)) ? true : false; // same error here, "&&" is underlined
break;
default:
return false
}
The other functions that are called just return a Bool:
func checkSerialNumber(serialNumber: String) -> Bool {
return serialNumber.contains("ABABWA") // changed after a hint in the comments
}
func checkWeight(weight: Float) -> Bool {
return (weight < 2)
}
I am suspecting something wrong with the enum and how I use them, but I haven't found the solution yet.

The error message is misleading. You want to check the associated values
of an enumeration value, therefore you must bind those in the case pattern:
func checkIntegrity(car: auto) -> Bool {
switch car {
case let .pkw(serialNumber, _, weight):
if checkSerialNumber(serialNumber: serialNumber)
&& checkWeight(weight: weight) {
return true
} else {
return false
}
break;
case let .lkw(serialNumber, _, weight):
return (checkSerialNumber(serialNumber: serialNumber)
&& checkWeight(weight: weight)) ? true : false;
break;
default:
return false
}
}
This can be simplified to
func checkIntegrity(car: auto) -> Bool {
switch car {
case let .pkw(serialNumber, _, weight),
let .lkw(serialNumber, _, weight):
return checkSerialNumber(serialNumber: serialNumber) && checkWeight(weight: weight)
}
}

Related

Enum pattern matching as a parameter to a function call

I've setup a playground with an example:
enum CarType : Equatable {
case wheeled(wheels: Int)
case flying
public static func ==(lhs: CarType, rhs: CarType) -> Bool {
return lhs.enumName == rhs.enumName
}
var enumName: String {
let stuff = "\(self)".split(separator: "(").first!
return String(describing: stuff)
}
}
var typesPresentAtMyParty = [CarType.wheeled(wheels:4), .wheeled(wheels:4), .flying]
let aKnownType = CarType.flying
if case aKnownType = typesPresentAtMyParty[2] {
print("Was the type")
}
func isPresent(type: CarType, inArray: [CarType]) -> Bool {
return inArray.filter {
if case type = $0 {
return true
}
return false
}.first != nil
}
func isWheeled(inArray: [CarType]) -> Bool {
return inArray.filter {
if case .wheeled = $0 {
return true
}
return false
}.first != nil
}
isPresent(type: .flying, inArray: typesPresentAtMyParty)
isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
The last line here does not compile. While i can do if case .wheeled = $0 ignoring associated type as a check, i cannot find a way of doing the same in a function call isPresent(type: CarType, inArray: [CarType]), when sending isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
Is there a way of writing a function that takes only the valid pattern matching part of the enum as a parameter?
It is not possible to pass partially constructed enums to a function. Partially constructed enums are not valid values, and they only work in pattern matching because the compiler has a concrete value to work with - the one from the right side of the pattern.
These being said, you could easily rewrite your functions to better, more swiftier versions.
Firstly, you don't need isPresent, you can simply use contains:
typesPresentAtMyParty.contains { $0 == .flying }
typesPresentAtMyParty.contains { if case . wheeled = $0 { return true } else { return false } }
Similarly, isWheeled can be shortened (and renamed, for better semantics):
func isWheeled(_ carType: CarType) -> Bool {
if case . wheeled = carType { return true } else { return false }
}
which can pe passed to contains:
let hasWeeled = typesPresentAtMyParty.contains(where: isWheeled)

swift affect value to inout generic variable

I want to simplify this piece of code with a T variable but could not succeed in compiling it. Hope could you give me the way.
here is the "duplicate" code I want to rewrite :
func getIntegerValue (listValues: [Any], numValueToRead: Int, readValue: inout Int) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! Int
return true
} else {
return false
}
}
func getStringValue (listValues: [Any], numValueToRead: Int, readValue: inout String) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! String
return true
} else {
return false
}
}
Here is the code I wrote but do not compile :
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
switch value {
case let integerValue as Int:
readValue = integerValue
case let stringValue as String:
readValue = stringValue
default:
return false
}
return true
} else {
return false
}
}
for those affectations I got those compilation errors :
readValue = integerValue -> 'Int' is not convertible to 'T'
readValue = stringValue -> 'String' is not convertible to 'T'
Is there a way to synthetise my two functions with a unique one using generics ?
You theoretically could make it compile by adding forced casts, since you already know that value has the type T:
case let integerValue as Int:
readValue = integerValue as! T
case let stringValue as String:
readValue = stringValue as! T
But the far better solution is to use a conditional cast (as? T) and
conditional binding (if let):
func getValue<T>(listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues[numValueToRead]
if let tvalue = value as? T {
readValue = tvalue
return true
} else {
return false
}
}
which then works for arbitrary types, not only Int and String.
A “swiftier” way would be return an optional value (with nil
indicating "no value"). The code can then be simplified to
func getValue<T>(listValues: [Any], numValueToRead: Int) -> T? {
guard listValues.indices.contains(numValueToRead) else {
return nil
}
return listValues[numValueToRead] as? T
}
This should work:
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
if let genericValue = value as? T {
readValue = genericValue
return true
}
return false
} else {
return false
}
}
At first sight, the function is wrongly named. You cannot call function getValue when it returns bool... I would call it transform or modify or something other than get value, because you are NOT getting value.
I think this method suits better your needs, not tested tought it should work.
func transformValue<T>(from listValues: [Any], numValueToRead: Int, readValue: inout T?) throws -> Bool {
// Guard suits better this case...
guard numValueToRead > 0 || numValueToRead < listValues.count else { return false }
let value = listValues[numValueToRead]
if type (of: value) == type(of: readValue) {
guard let value = value as? T else {
throw NSError(
domain: "Smth",
code: 1,
userInfo: ["Description": "Failed to cast to generic type T"]
)
}
readValue = value as? T
return true
}
return false // No need to call else...
}
Explenation: Returning optional generic type T is much safer. You try to cast it, you fail and you throw error that something went wrong. In my opinion saving force casts with throwing errors is much more safer approach, you know what went wrong and so.
As #MartinR pointed out, returning a nil value instead of an inout+Bool combination gives the same results, but with less, and more readable code. This is the path Swift also took when importing most of the NSError ** methods from Objective-C (i.e. dropped the last parameter, imported them as throwable functions).
These being said, another approach would be to add an extension over Array for extracting the value:
extension Array {
subscript<T>(_ index: Int, as type: T.Type) -> T? {
guard 0..<count ~= index else { return nil }
return self[index] as? T
}
}
let arr: [Any] = [1, "two", 3, "four"]
arr[1, as: String.self] // two
arr[2, as: String.self] // nil

How to look for enum in enums list?

I want to determine if an enum is present in an enums list.
Intuitively I would do this:
if myEnum == (.one || .two) { return true }
else { return false }
Of course it doesn't work.
I know that I could do:
if myEnum == .one || myEnum == .two { return true }
else { return false }
Or
if [EnumType.one, EnumType.two].contains(myEnum) { return true }
else { return false }
But I just want to be fancy today :)
I am thinking about using filter but it seems overkill.
Do you have an idea?
Thanks a lot.
Thierry
You can do
//: Playground - noun: a place where people can play
import Cocoa
enum MyEnum {
case one
case two
case three
case four
}
let list: [MyEnum] = [.one, .two, .three, .four]
if list.contains(.one) {
// Contains
} else {
// Doesn't Contain
}
If you have associated data you have to make your enum be Equatable for this to work though. For example:
//: Playground - noun: a place where people can play
import Cocoa
enum MyEnum: Equatable {
case one
case two
case three
case four
case other(String)
static func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
switch (lhs, rhs) {
case (.one, .one),
(.two, .two),
(.three, .three),
(.four, .four):
return true
case (.other(let lhsString), .other(let rhsString)) where lhsString == rhsString:
return true
default:
return false
}
}
}
let list: [MyEnum] = [.one, .two, .three, .four, .other("test")]
if list.contains(.one) {
} else {
}
I would do a switch on each one and then have a default for if you can't find either of those types.
switch myEnum {
case .one:
print("One is here baby")
case .two:
print("Two is here baby")
default:
print("Can't find the case??????")
}
That's what OptionSet are for. It's technically a struct, but in usage, look very close to enum:
struct MyOptions : OptionSet {
var rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let one = MyOptions(rawValue: 1)
static let two = MyOptions(rawValue: 2)
static let three = MyOptions(rawValue: 4)
}
let option: MyOptions = [.one, .two]
if option.contains([.one, .two]) {
print("Has one and two")
}
if !option.intersection([.one, .three]).isEmpty {
print("Has one or three")
}
I would use a switch as well and group the enum cases which are handled with common logic as follows:
enum MyEnum {
case one
case two
case three
case four
}
switch myEnum {
case .one, .two:
//deal with enum cases .one and .two
default:
//deal with all other cases
}
}
If you are trying to match arbitrary strings to various cases in your enum then you can do something like this (using Swift 3).
enum CompassPoint:String {
case north
case south
case east
case west
static func has(key: String) -> Bool {
for f in iterateEnum(CompassPoint.self) {
if(f.rawValue == key) {
return true;
}
}
return false;
}
private static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}
}
CompassPoint.has(key: "south") // true
CompassPoint.has(key: "something") // false
Shoutout to #rintaro for his function for iterating over enums (found below).
https://stackoverflow.com/a/28341290/5270038

How to get a case of enum in one line?

I have a enum with some case, and a array with instances of this enum, for example:
enum MyEnum {
case foo(Int)
case bar(Int)
case baz(Int)
}
let myArray = [
MyEnum.foo(1),
MyEnum.bar(2),
MyEnum.baz(3)
]
Then, I need to compare if determinate element of this array if foo, baz or baz, for example:
myArray.filter { $0 == .foo } // not work, and should need return [MyEnum.foo(1)]
I can use switch:
myArray.filter {
switch $0 {
case .foo(_):
return true
default:
return false
}
} // work, and return [MyEnum.foo(1)]
But, I want a solution less verbose, and learning more about enum.
The shortest I can think of is:
let result = myArray.filter { if case .foo = $0 { return true } else { return false } }
As mentioned before if case is a good solution since swift 2.0. But if you're going to use this same filter many times, then you will need a more expressive way to do this. For instance, you can make this enum equatable hiding your switchCase away from the viewController:
extension MyEnum: Equatable{
static func ==(lhs: MyEnum, rhs: MyEnum) -> Bool{
switch (lhs, rhs) {
case (.foo(_), .foo(_)):
return true
case (.bar(_), .bar(_)):
return true
case (.baz(_), .baz(_)):
return true
default:
return false
}
}
}
and then:
myArray.filter {
$0 == .foo(1)
}
If you really want to do it in one line, you can use reflection:
myArray.filter { Mirror(reflecting: $0).children.first!.label! == "foo" }

BSON for Swift?

My question's pretty open ended at this point, but I'm curious if anyone has implemented something like SwiftyJSON for BSON in Swift?
I found that the Perfect project has something, but it seems to just be a wrapper around an existing C library (won't do me any good on the iOS side). I toyed with just porting/warping SwiftyJSON, the internals of which are a little above my learning curve so far AND it seems to just be a wrapper around the platform's JSONSerialization.
So is anyone either
A) aware of something done that my googling hasn't discovered yet or
B) or could help nudge me in the right direction with how to generally architect such a thing? (not trying to get others to do work for me)
aside: to forestall the "why don't you just use json" questions... it's because I'm doing quite a bit of MongoDB on the other side AND I work with a lot of Strings and Dates, which have to be ambiguously represented in JSON
In the interest of closure... I ended up writing my own. It is not a complete solution to all of the BSON encodings, just the ones that I'm using. It was fun to figure out how to do it with Swift Enums.
import Foundation
extension GeneratorType {
mutating func next(n: Int) -> [Element] {
var result: [Element] = []
for _ in 1...n {
if let next = self.next() {
result.append(next)
} else {
break
}
}
return result
}
}
extension GeneratorType where Element:Comparable {
mutating func upTo(match:Element) -> [Element]? {
var result: [Element] = []
while let next = self.next() {
if next == match {
return result
}
else {
result.append(next)
}
}
return nil
}
}
extension String {
init?<S : SequenceType, C : UnicodeCodecType where S.Generator.Element == C.CodeUnit>
(codeUnits : S, inout codec : C) {
var str = ""
var generator = codeUnits.generate()
var done = false
while !done {
let r = codec.decode(&generator)
switch (r) {
case .EmptyInput:
done = true
case let .Result(val):
str.append(Character(val))
case .Error:
return nil
}
}
self = str
}
}
enum BSON {
static func toByteArray<T>(value: T) -> [UInt8] {
var io = value
return withUnsafePointer(&io) {
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
}
}
static func fromByteArray<T>(value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
return UnsafePointer<T>($0.baseAddress).memory
}
}
struct Field {
var name:String
var element:BSON
}
case double(Double)
case string(String)
case document([Field])
case array([BSON])
case binary([UInt8])
// case objectid((UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8))
case boolean(Bool)
case datetime(NSDate)
case null
case int32(Int32)
case int64(Int64)
init() {
self = .document([])
}
var bsonType:UInt8 {
switch self {
case .double: return 0x01
case .string: return 0x02
case .document: return 0x03
case .array: return 0x04
case .binary: return 0x05
// case .objectid: return 0x07
case .boolean: return 0x08
case .datetime: return 0x09
case .null: return 0x0A
case .int32: return 0x10
case .int64: return 0x12
}
}
subscript(key:String) -> BSON {
get {
switch self {
case .document (let fields):
for field in fields {
if field.name == key {
return field.element
}
}
return BSON.null
default:
return BSON.null
}
}
set(newValue) {
var newFields:[Field] = []
switch self {
case .document (let fields):
newFields = fields
var append = true
for (index, field) in newFields.enumerate() {
if field.name == key {
newFields[index].element = newValue
append = false
break
}
}
if append {
newFields.append(Field(name: key, element: newValue))
}
default:
newFields = [Field(name: key, element: newValue)]
}
self = .document(newFields)
}
}
subscript(index:Int) -> BSON {
get {
switch self {
case .array (let elements):
return index < elements.count ? elements[index] : BSON.null
default:
return BSON.null
}
}
set(newValue) {
switch self {
case .array (let elements):
if index < elements.count {
var newElements = elements
newElements[index] = newValue
self = .array(newElements)
}
default:
break
}
}
}
func encoded() -> [UInt8] {
switch self {
case double (let value):
return BSON.toByteArray(value)
case string (let value):
let encoded = value.utf8
return BSON.toByteArray(Int32(encoded.count + 1)) + encoded + [0]
case document (let fields):
var body:[UInt8] = []
for field in fields ?? [] {
body += [field.element.bsonType]
body += field.name.utf8
body += [0]
body += field.element.encoded()
}
body += [0]
return BSON.toByteArray(Int32(body.count + 4)) + body
case array (let elements):
var body:[UInt8] = []
for (index, element) in elements.enumerate() {
body += [element.bsonType]
body += String(index).utf8
body += [0]
body += element.encoded()
}
body += [0]
return BSON.toByteArray(Int32(body.count + 4)) + body
case binary (let bytes):
return BSON.toByteArray(Int32(bytes.count)) + [0x00] + bytes
// case objectid:
// return []
case boolean (let value):
return value ? [0x01] : [0x00]
case datetime (let value):
let since = Int64(value.timeIntervalSince1970 * 1000.0)
return BSON.toByteArray(since)
case null:
return []
case int32 (let value):
return BSON.toByteArray(value)
case int64 (let value):
return BSON.toByteArray(value)
}
}
static func decode(inout stream stream:IndexingGenerator<[UInt8]>, bsonType:UInt8 = 0x03) -> BSON {
switch bsonType {
case 0x01:
let bytes = stream.next(sizeof(Double))
return self.double(fromByteArray(bytes, Double.self))
case 0x02:
let _ = stream.next(sizeof(Int32)) // skip the count
if let buffer = stream.upTo(0) {
var codec = UTF8()
if let decoded = String(codeUnits: buffer, codec: &codec) {
return self.string(decoded)
}
}
fatalError("utf8 parse error!")
case 0x03:
var fields:[Field] = []
stream.next(sizeof(Int32)) // throw out size
while let bsonType = stream.next() {
if bsonType == 0 {
return self.document(fields)
}
if let buffer = stream.upTo(0) {
var codec = UTF8()
if let fieldName = String(codeUnits: buffer, codec: &codec) {
let element = BSON.decode(stream:&stream, bsonType: bsonType)
fields.append(Field(name: fieldName, element: element))
}
}
}
case 0x04:
var elements:[BSON] = []
stream.next(sizeof(Int32)) // throw out size
while let bsonType = stream.next() {
if bsonType == 0 {
return self.array(elements)
}
stream.upTo(0) // skip name
elements.append(BSON.decode(stream:&stream, bsonType: bsonType))
}
case 0x05:
let count = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
assert(stream.next() == 0x00)
return self.binary(stream.next(Int(count)))
case 0x07:
break
case 0x08:
let value = stream.next()
return self.boolean(value == 0x01)
case 0x09:
let milliseconds = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
let interval = NSTimeInterval(milliseconds) / 1000.0
return self.datetime(NSDate(timeIntervalSince1970: interval))
case 0x0A:
return self.null
case 0x10:
let value = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
return self.int32(value)
case 0x12:
let value = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
return self.int64(value)
default:
break
}
fatalError()
}
var document:BSON? {
switch self {
case .document:
return self
default:
return nil
}
}
var double:Double? {
switch self {
case .double (let value):
return value
default:
return nil
}
}
var int32:Int32? {
get {
switch self {
case .int32 (let value):
return value
default:
return nil
}
}
set {
if let newValue = newValue {
self = .int32(newValue)
}
else {
self = .null
}
}
}
var int64:Int64? {
switch self {
case .int64 (let value):
return value
default:
return nil
}
}
var boolean:Bool? {
switch self {
case .boolean (let value):
return value
default:
return nil
}
}
var binary:[UInt8]? {
switch self {
case .binary (let value):
return value
default:
return nil
}
}
var string:String? {
switch self {
case .string (let value):
return value
default:
return nil
}
}
var datetime:NSDate? {
switch self {
case .datetime (let value):
return value
default:
return nil
}
}
var array:[BSON]? {
switch self {
case .array (let elements):
return elements
default:
return nil
}
}
var isNull:Bool {
switch self {
case .null:
return true
default:
return false
}
}
var keyValues:[(String, BSON)] {
switch self {
case document (let fields):
return fields.map() { ($0.name, $0.element) }
default:
return []
}
}
}
extension BSON: Equatable {}
extension BSON.Field: Equatable {}
func == (a:BSON.Field, b:BSON.Field) -> Bool {
return a.name == b.name && a.element == b.element
}
func == (a:BSON, b:BSON) -> Bool {
switch (a, b) {
case (.double(let a), .double(let b)) where a == b: return true
case (.string(let a), .string(let b)) where a == b: return true
case (.document(let a), .document(let b)) where a == b: return true
case (.array(let a), .array(let b)) where a == b: return true
case (.binary(let a), .binary(let b)) where a == b: return true
case (.boolean(let a), .boolean(let b)) where a == b: return true
case (.datetime(let a), .datetime(let b)) where a.isEqualToDate(b): return true
case (.null, .null): return true
case (.int32(let a), .int32(let b)) where a == b: return true
case (.int64(let a), .int64(let b)) where a == b: return true
default: return false
}
}
protocol BSONConvertable {
var bson:BSON { get }
}
extension Int32: BSONConvertable {
var bson:BSON {
return BSON.int32(self)
}
}
extension Int64: BSONConvertable {
var bson:BSON {
return BSON.int64(self)
}
}
extension Int: BSONConvertable {
var bson:BSON {
let wide = self.toIntMax()
if Int32.min.toIntMax() <= wide && wide <= Int32.max.toIntMax() {
return BSON.int32(Int32(wide))
}
else {
return BSON.int64(Int64(wide))
}
}
}
extension Float:BSONConvertable {
var bson:BSON {
return Double(self).bson
}
}
extension Double:BSONConvertable {
var bson:BSON {
return BSON.double(self)
}
}
extension Bool:BSONConvertable {
var bson:BSON {
return BSON.boolean(self)
}
}
extension BSON:BSONConvertable {
var bson:BSON {
return self
}
}
extension NSDate:BSONConvertable {
var bson:BSON {
return BSON.datetime(self)
}
}
extension String:BSONConvertable {
var bson:BSON {
return BSON.string(self)
}
}
extension Array where Element:BSONConvertable {
var bson:BSON {
return BSON.array(self.map({$0.bson}))
}
}
Late to the party but the OpenKitten implementation on github seems to be a good implementation and it is referenced from the BSON spec site.
https://github.com/OpenKitten/BSON