Extend Optional to check whether it is nil or false - swift

I want to extend the Optional class to return a Bool indicating whether self is nil or false. How do I do this?
I already have an extension for optional to check if it is empty or nil like this:
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
return self?.isEmpty ?? true
}
}
So it needs to be something along those lines but I can't figure it out.

When in doubt, unwrap using guard:
extension Optional where Wrapped == Bool {
var isNilOrFalse: Bool {
guard let wrapped = self else { return true }
return !wrapped
}
}
Which can be shortened to:
extension Optional where Wrapped == Bool {
var isNilOrFalse: Bool {
return !(self ?? false)
}
}
but since short is not always the most readable, I would personally use explicit:
extension Optional where Wrapped == Bool {
var isNilOrFalse: Bool {
return self == nil || self == false
}
}
However, I would warn against using such extensions. They don't make your code more readable.

import Foundation
extension Optional where Wrapped == String {
var isNotBlank: Bool {
if let a = self, a.isNotEmpty {
return true
} else {
return false
}
}
var isBlank: Bool {
return !isNotBlank
}
}
extension Optional {
var isNil: Bool {
return self == nil
}
var isNotNil: Bool {
return self != nil
}
func ifLet(_ action: (Wrapped)-> Void) {
if self != nil {
action(self.unsafelyUnwrapped)
}
else { return }
}
func ifNil (_ action: ()-> Void) {
if self == nil { action() }
else { return }
}
func ifElse(_ notNil: ()-> Void, _ isNil: ()-> Void) {
if self != nil { notNil() }
else { isNil() }
}
func or<T>(_ opt: T) -> T {
if self == nil { return opt }
else { return self as! T }
}
mutating func orChange<T>(_ opt: T) {
if self == nil { self = opt as? Wrapped }
}
}

You don't need to do anything new. You can use != true on an optional Bool:
var aBool: Bool? = nil
if aBool != true {
print("aBool is nil or false")
}
That is legal and works. It works because nil is not equal to true.

Related

Making the Core Data class as final to satisfy the protocol 'Self' requirement

I've written a protocol, allowing to link objects in a simple chain with predecessors and successors.
I'm trying to use this protocol with my Core Data entity "Event", but I'm getting this error:
Error:
Protocol 'Chainable' requirement 'chain' cannot be satisfied by a non-final class ('Event') because it uses 'Self' in a non-parameter, non-result type position.
How can I make this class final (I wouldn't like to set the Codegen to manual), or rewrite the var chain: [Self] definition?
Thank you in advance.
extension Event: Chainable {}
protocol Chainable where Self: NSManagedObject {
var chain: [Self] { get }
var predecessor: Self? { get set }
var successor: Self? { get set }
var selfIndexInChain: Int { get }
mutating func moveInChain(to index: Int)
mutating func removeSelfFromChain()
mutating func addSelfAsSuccessor(of object: Self)
mutating func addSelfAsPredecessor(of object: Self)
}
extension Chainable {
var chain: [Self] {
var _chain: [Self] = [self]
// go right
var current: Self = self
while let successor = current.successor {
_chain.append(successor)
current = successor
}
// go left
current = self
while let predecessor = current.predecessor {
_chain.insert(predecessor, at: 0)
current = predecessor
}
// TODO: - Compare speed with the alternative realization: Find the first element, then populate the tasks array.
return _chain
}
// Self Index in the chain
var selfIndexInChain: Int { return self.chain.index(of: self)! }
// Change place in the cahain
mutating func moveInChain(to index: Int) {
guard index != selfIndexInChain else { return } // Check the index isn't same
guard 0...(chain.count-1) ~= index else { return }
let taskAtDestination = chain[index]
if index > selfIndexInChain {
removeSelfFromChain()
addSelfAsSuccessor(of: taskAtDestination)
} else {
removeSelfFromChain()
addSelfAsPredecessor(of: taskAtDestination)
}
}
mutating func removeSelfFromChain() {
let successor = self.successor
let predecessor = self.predecessor
self.predecessor = nil
self.successor = nil
self.predecessor?.successor = successor
self.successor?.predecessor = predecessor
}
mutating func insertSelfBetween(lhs: Self, rhs: Self) {
// self shouldn't be linked
guard self.predecessor == nil && self.successor == nil else { return }
guard (lhs.successor == rhs && rhs.predecessor == lhs) ||
(lhs.successor == nil && rhs.predecessor == nil ) else { return } // If both are nil, they will be connected thru self
self.predecessor = lhs
self.successor = rhs
}
mutating func addSelfAsSuccessor(of object: Self) {
// self shouldn't be linked
guard self.predecessor == nil && self.successor == nil else { return } // TODO: Add error support
// self shouldn't be already the successor
guard object.successor != self else { return }
let previousSuccessor = object.successor
self.predecessor = object
self.successor = previousSuccessor
}
mutating func addSelfAsPredecessor(of object: Self) {
// self shouldn't be linked
guard self.predecessor == nil && self.successor == nil else { return }
// self shouldn't be the task successor already
guard object.predecessor != self else { return }
let previousPredecessor = object.predecessor
self.successor = object
self.predecessor = previousPredecessor
}
}
I am stuck with a similar problem as well.
Workaround:
Looking at your code, it seems like Chainable would be implemented by a number of NSManagedObject subclasses.
Instead of Self replace it with Chainable and let Chainable contain the minimum requirements that needs to be common with the NSManagedObject subclasses.
In some cases this would be mean, implementing a wrapper for some of the functions implemented by the NSManagedObject subclasses

Swift 2 to 3 Migration for Swift Sequence Protocol

I'm attempting to convert the following code from this library (https://github.com/dankogai/swift-json) into Swift 3 Compatible code.
I'm having a tough time figuring out how to convert the Sequence protocol used in Swift 2 with the correct version for Swift 3. I can't find any documentation on Swift 2 Sequence protocol changes as compared to 3.
Here is the code that I currently have converted as much as possible to Swift 3
extension JSON : Sequence {
public func generate()->AnyIterator<(AnyObject,JSON)> {
switch _value {
case let o as NSArray:
var i = -1
return AnyIterator {
i=i+1
if i == o.count { return nil }
return (i as AnyObject, JSON(o[i]))
}
case let o as NSDictionary:
var ks = Array(o.allKeys.reversed())
return AnyIterator {
if ks.isEmpty { return nil }
if let k = ks.removeLast() as? String {
return (k as AnyObject, JSON(o.value(forKey: k)!))
} else {
return nil
}
}
default:
return AnyIterator{ nil }
}
}
public func mutableCopyOfTheObject() -> AnyObject {
return _value.mutableCopy as AnyObject
}
}
The error I'm getting in specifics is in attached image.
If you want to play around with it the entire code is rather short for the JSON library. Here it is below:
//
// json.swift
// json
//
// Created by Dan Kogai on 7/15/14.
// Copyright (c) 2014 Dan Kogai. All rights reserved.
//
import Foundation
/// init
public class JSON {
public let _value:AnyObject
/// unwraps the JSON object
public class func unwrap(obj:AnyObject) -> AnyObject {
switch obj {
case let json as JSON:
return json._value
case let ary as NSArray:
var ret = [AnyObject]()
for v in ary {
ret.append(unwrap(obj: v as AnyObject))
}
return ret as AnyObject
case let dict as NSDictionary:
var ret = [String:AnyObject]()
for (ko, v) in dict {
if let k = ko as? String {
ret[k] = unwrap(obj: v as AnyObject)
}
}
return ret as AnyObject
default:
return obj
}
}
/// pass the object that was returned from
/// NSJSONSerialization
public init(_ obj:Any) { self._value = JSON.unwrap(obj: obj as AnyObject) }
/// pass the JSON object for another instance
public init(_ json:JSON){ self._value = json._value }
}
/// class properties
extension JSON {
public typealias NSNull = Foundation.NSNull
public typealias NSError = Foundation.NSError
public class var null:NSNull { return NSNull() }
/// constructs JSON object from data
public convenience init(data:NSData) {
var err:NSError?
var obj:Any?
do {
obj = try JSONSerialization.jsonObject(
with: data as Data, options:[])
} catch let error as NSError {
err = error
obj = nil
}
self.init(err != nil ? err! : obj!)
}
/// constructs JSON object from string
public convenience init(string:String) {
let enc:String.Encoding = String.Encoding.utf8
self.init(data: string.data(using: enc)! as NSData)
}
/// parses string to the JSON object
/// same as JSON(string:String)
public class func parse(string:String)->JSON {
return JSON(string:string)
}
/// constructs JSON object from the content of NSURL
public convenience init(nsurl:NSURL) {
var enc:String.Encoding = String.Encoding.utf8
do {
let str = try NSString(contentsOf:nsurl as URL, usedEncoding:&enc.rawValue)
self.init(string:str as String)
} catch let err as NSError {
self.init(err)
}
}
/// fetch the JSON string from NSURL and parse it
/// same as JSON(nsurl:NSURL)
public class func fromNSURL(nsurl:NSURL) -> JSON {
return JSON(nsurl:nsurl)
}
/// constructs JSON object from the content of URL
public convenience init(url:String) {
if let nsurl = NSURL(string:url) as NSURL? {
self.init(nsurl:nsurl)
} else {
self.init(NSError(
domain:"JSONErrorDomain",
code:400,
userInfo:[NSLocalizedDescriptionKey: "malformed URL"]
)
)
}
}
/// fetch the JSON string from URL in the string
public class func fromURL(url:String) -> JSON {
return JSON(url:url)
}
/// does what JSON.stringify in ES5 does.
/// when the 2nd argument is set to true it pretty prints
public class func stringify(obj:AnyObject, pretty:Bool=false) -> String! {
if !JSONSerialization.isValidJSONObject(obj) {
let error = JSON(NSError(
domain:"JSONErrorDomain",
code:422,
userInfo:[NSLocalizedDescriptionKey: "not an JSON object"]
))
return JSON(error).toString(pretty: pretty)
}
return JSON(obj).toString(pretty: pretty)
}
}
/// instance properties
extension JSON {
/// access the element like array
public subscript(idx:Int) -> JSON {
switch _value {
case _ as NSError:
return self
case let ary as NSArray:
if 0 <= idx && idx < ary.count {
return JSON(ary[idx])
}
return JSON(NSError(
domain:"JSONErrorDomain", code:404, userInfo:[
NSLocalizedDescriptionKey:
"[\(idx)] is out of range"
]))
default:
return JSON(NSError(
domain:"JSONErrorDomain", code:500, userInfo:[
NSLocalizedDescriptionKey: "not an array"
]))
}
}
/// access the element like dictionary
public subscript(key:String)->JSON {
switch _value {
case _ as NSError:
return self
case let dic as NSDictionary:
if let val:Any = dic[key] { return JSON(val) }
return JSON(NSError(
domain:"JSONErrorDomain", code:404, userInfo:[
NSLocalizedDescriptionKey:
"[\"\(key)\"] not found"
]))
default:
return JSON(NSError(
domain:"JSONErrorDomain", code:500, userInfo:[
NSLocalizedDescriptionKey: "not an object"
]))
}
}
/// access json data object
public var data:AnyObject? {
return self.isError ? nil : self._value
}
/// Gives the type name as string.
/// e.g. if it returns "Double"
/// .asDouble returns Double
public var type:String {
switch _value {
case is NSError: return "NSError"
case is NSNull: return "NSNull"
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C": return "Bool"
case "q", "l", "i", "s": return "Int"
case "Q", "L", "I", "S": return "UInt"
default: return "Double"
}
case is NSString: return "String"
case is NSArray: return "Array"
case is NSDictionary: return "Dictionary"
default: return "NSError"
}
}
/// check if self is NSError
public var isError: Bool { return _value is NSError }
/// check if self is NSNull
public var isNull: Bool { return _value is NSNull }
/// check if self is Bool
public var isBool: Bool { return type == "Bool" }
/// check if self is Int
public var isInt: Bool { return type == "Int" }
/// check if self is UInt
public var isUInt: Bool { return type == "UInt" }
/// check if self is Double
public var isDouble: Bool { return type == "Double" }
/// check if self is any type of number
public var isNumber: Bool {
if let o = _value as? NSNumber {
let t = String(cString:o.objCType)
return t != "c" && t != "C"
}
return false
}
/// check if self is String
public var isString: Bool { return _value is NSString }
/// check if self is Array
public var isArray: Bool { return _value is NSArray }
/// check if self is Dictionary
public var isDictionary: Bool { return _value is NSDictionary }
/// check if self is a valid leaf node.
public var isLeaf: Bool {
return !(isArray || isDictionary || isError)
}
/// gives NSError if it holds the error. nil otherwise
public var asError:NSError? {
return _value as? NSError
}
/// gives NSNull if self holds it. nil otherwise
public var asNull:NSNull? {
return _value is NSNull ? JSON.null : nil
}
/// gives Bool if self holds it. nil otherwise
public var asBool:Bool? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C": return Bool(o.boolValue)
default:
return nil
}
default: return nil
}
}
/// gives Int if self holds it. nil otherwise
public var asInt:Int? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return nil
default:
return Int(o.int64Value)
}
default: return nil
}
}
/// gives Int32 if self holds it. nil otherwise
public var asInt32:Int32? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return nil
default:
return Int32(o.int64Value)
}
default: return nil
}
}
/// gives Int64 if self holds it. nil otherwise
public var asInt64:Int64? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return nil
default:
return Int64(o.int64Value)
}
default: return nil
}
}
/// gives Float if self holds it. nil otherwise
public var asFloat:Float? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return nil
default:
return Float(o.floatValue)
}
default: return nil
}
}
/// gives Double if self holds it. nil otherwise
public var asDouble:Double? {
switch _value {
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return nil
default:
return Double(o.doubleValue)
}
default: return nil
}
}
// an alias to asDouble
public var asNumber:Double? { return asDouble }
/// gives String if self holds it. nil otherwise
public var asString:String? {
switch _value {
case let o as NSString:
return o as String
default: return nil
}
}
/// if self holds NSArray, gives a [JSON]
/// with elements therein. nil otherwise
public var asArray:[JSON]? {
switch _value {
case let o as NSArray:
var result = [JSON]()
for v:Any in o { result.append(JSON(v)) }
return result
default:
return nil
}
}
/// if self holds NSDictionary, gives a [String:JSON]
/// with elements therein. nil otherwise
public var asDictionary:[String:JSON]? {
switch _value {
case let o as NSDictionary:
var result = [String:JSON]()
for (ko, v): (Any, Any) in o {
if let k = ko as? String {
result[k] = JSON(v)
}
}
return result
default: return nil
}
}
/// Yields date from string
public var asDate:NSDate? {
if let dateString = _value as? String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZ"
return dateFormatter.date(from: dateString) as NSDate?
}
return nil
}
/// gives the number of elements if an array or a dictionary.
/// you can use this to check if you can iterate.
public var count:Int {
switch _value {
case let o as NSArray: return o.count
case let o as NSDictionary: return o.count
default: return 0
}
}
public var length:Int { return self.count }
// gives all values content in JSON object.
public var allValues:JSON{
if(self._value.allValues == nil) {
return JSON([])
}
return JSON(self._value.allValues)
}
// gives all keys content in JSON object.
public var allKeys:JSON{
if(self._value.allKeys == nil) {
return JSON([])
}
return JSON(self._value.allKeys)
}
}
extension JSON : Sequence {
public func generate()->AnyIterator<(AnyObject,JSON)> {
switch _value {
case let o as NSArray:
var i = -1
return AnyIterator {
i=i+1
if i == o.count { return nil }
return (i as AnyObject, JSON(o[i]))
}
case let o as NSDictionary:
var ks = Array(o.allKeys.reversed())
return AnyIterator {
if ks.isEmpty { return nil }
if let k = ks.removeLast() as? String {
return (k as AnyObject, JSON(o.value(forKey: k)!))
} else {
return nil
}
}
default:
return AnyIterator{ nil }
}
}
public func mutableCopyOfTheObject() -> AnyObject {
return _value.mutableCopy as AnyObject
}
}
extension JSON : CustomStringConvertible {
/// stringifies self.
/// if pretty:true it pretty prints
public func toString(pretty:Bool=false)->String {
switch _value {
case is NSError: return "\(_value)"
case is NSNull: return "null"
case let o as NSNumber:
switch String(cString:o.objCType) {
case "c", "C":
return o.boolValue.description
case "q", "l", "i", "s":
return o.int64Value.description
case "Q", "L", "I", "S":
return o.uint64Value.description
default:
switch o.doubleValue {
case 0.0/0.0: return "0.0/0.0" // NaN
case -1.0/0.0: return "-1.0/0.0" // -infinity
case +1.0/0.0: return "+1.0/0.0" // infinity
default:
return o.doubleValue.description
}
}
case let o as NSString:
return o.debugDescription
default:
let opts = pretty ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions()
if let data = (try? JSONSerialization.data(
withJSONObject: _value, options:opts)) as NSData? {
if let result = NSString(
data:data as Data, encoding:String.Encoding.utf8.rawValue
) as? String {
return result
}
}
return "YOU ARE NOT SUPPOSED TO SEE THIS!"
}
}
public var description:String { return toString() }
}
extension JSON : Equatable {}
public func ==(lhs:JSON, rhs:JSON)->Bool {
// print("lhs:\(lhs), rhs:\(rhs)")
if lhs.isError || rhs.isError { return false }
else if lhs.isLeaf {
if lhs.isNull { return lhs.asNull == rhs.asNull }
if lhs.isBool { return lhs.asBool == rhs.asBool }
if lhs.isNumber { return lhs.asNumber == rhs.asNumber }
if lhs.isString { return lhs.asString == rhs.asString }
}
else if lhs.isArray {
for i in 0..<lhs.count {
if lhs[i] != rhs[i] { return false }
}
return true
}
else if lhs.isDictionary {
for (k, v) in lhs.asDictionary! {
if v != rhs[k] { return false }
}
return true
}
fatalError("JSON == JSON failed!")
}
In Swift 3, generate() has been renamed to makeIterator(). Changing the name of your function should fix the problem. (Note that other names have also changed, like AnyGenerator → AnyIterator, but it looks like that one has already been taken care of in your code.)
This change was implemented as part of SE-0006: Apply API Guidelines to the Standard Library.

How can I check if a property has been set using Swift reflection?

Some of my models have optional properties. I'm trying to write a method that can evaluate if they've been set.
Below is an attempt, but I can't figure out how to determine a nil value from an Any object [edit: (the child variable is of type Any)]. It doesn't compile.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value = child.value
if let optionalValue = value as? AnyObject? { //Does not compile
if optionalValue == nil {
return false
}
}
}
return true
}
Edit:
I forgot to clarify that the child value in the above example is always of type Any. The Any type is difficult in that it cannot be compared to nil and a cast to AnyObject always fails. I've tried to illustrate it in the playground below.
var anyArray = [Any]();
var optionalStringWithValue: String? = "foo";
anyArray.append(optionalStringWithValue);
var nilOptional: String?
anyArray.append(nilOptional)
print(anyArray[0]); // "Optional("foo")\n"
print(anyArray[1]); // "nil\n"
if let optionalString = anyArray[0] as? AnyObject {
//will always fail
print("success")
}
//if anyArray[1] == nil { // will not compile
//}
I used #ebluehands technique of reflecting the Any value to modify the original function. It cycles through the properties with an initial mirror, then reflects each one individually using displayStyle to determine if the property is optional.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value: Any = child.value
let subMirror = Mirror(reflecting: value)
if subMirror.displayStyle == .Optional {
if subMirror.children.count == 0 {
return false
}
}
}
return true
}
Obsolete:
You can simply check if the optional value is nil or not :
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
//child.value being an optional
if child.value == nil {
return false
}
}
return true
}
Edit:
To check if an Any object is optional and contains a value or not using reflection :
let optionalString : String? = "optional string"
let any : Any = optionalString
//First you will need to create a mirror of the any object
let mirror = Mirror(reflecting : any)
//Then you can check the display style to see if it's an optional
if mirror.displayStyle == .Optional {
//If it is, check the count of its children to see if there is a value or not
if mirror.children.count == 0 {
print("I don't have a value")
}
else {
print("I have a value")
}
}
Here is a playground example (based on yours):
var anyArray = [Any]()
var optionalStringWithValue: String? = "foo"
anyArray.append(optionalStringWithValue)
var nilOptional: String?
anyArray.append(nilOptional)
let string = "string not optional"
anyArray.append(string)
print(anyArray[0]) // "Optional("foo")\n"
print(anyArray[1]) // "nil\n"
print(anyArray[2]) // "string not optional\n"
let mirrorOptionalWithValue = Mirror(reflecting: anyArray[0])
if mirrorOptionalWithValue.displayStyle == .Optional
&& mirrorOptionalWithValue.children.count == 1 {
print("Is an optional and contains a value")
}
let mirrorOptionalWithoutValue = Mirror(reflecting: anyArray[1])
if mirrorOptionalWithoutValue.displayStyle == .Optional &&
mirrorOptionalWithoutValue.children.count == 0 {
print("Is an optional but is nil")
}
let mirrorNotAnOptional = Mirror(reflecting: anyArray[2])
if mirrorNotAnOptional.displayStyle != .Optional {
print("Is not an optional")
}
Another option is create a extension.
extension NSManagedObject {
func checkIfAllRequiredMembersAreSet() -> Bool {
let attributes = self.entity.attributesByName
for (attribute, value) in attributes {
if value.attributeValueClassName != nil {
let v: AnyObject? = self.valueForKey(attribute)
if !value.optional && v != nil {
return false
}
}
}
return true
}
}
Based on this answer, I recommend using if case Optional<Any>.some(_).
I did something recently to make sure I have at least one optional set. Here's an example to make sure all are set. You can paste into playgrounds:
import Foundation
struct SomeError: Error {
let code: Int?
let message: String?
let errorDescription: String?
var allValuesSet: Bool {
for aChild in Mirror(reflecting: self).children {
if case Optional<Any>.some(_) = aChild.value {
continue
} else {
return false
}
}
return true
}
}
let errorTest = SomeError(code: nil, message: "failed", errorDescription: nil)
let errorTest2 = SomeError(code: -1, message: "failed", errorDescription: "missing information")
print("is valid: \(errorTest.allValuesSet)") //is valid: false
print("is valid: \(errorTest2.allValuesSet)") //is valid: true

Swift variable comparison where type is not known

So I'm very new to Swift at the moment and am working on a class (as below).
The variable selectedOption can be an array of String or Int and the methods need to compare the values received against that array.
The below code works, but I don't like the fact that I'm type checking and replicating the code to compare Strings then again to compare Ints in methods isSelected and removeSelectedItem
Such as
if selectedOption is String {
return (self.selectedOption!.indexOf({ $0 as! String == selectedOption as! String }) != nil)
} else if ( selectedOption is Int ) {
return (self.selectedOption!.indexOf({ $0 as! Int == selectedOption as! Int })) != nil
}
There must be a better way?
public class SearchOption {
private var title: String
private var allowAny: Bool
private var allowMultiple: Bool
private var dependencies: [SearchOption]?
// Store the selected Item in an array of AnyObjects.
private var selectedOption: [AnyObject]?
init(title: String, allowAny: Bool, allowMultiple: Bool, dependencies: [SearchOption]?) {
self.title = title
self.allowAny = allowAny
self.allowMultiple = allowMultiple
self.dependencies = dependencies
}
public func setSelectedItem(selectedOption: AnyObject) -> Void {
if self.selectedOption == nil || !self.allowMultiple{
self.selectedOption = [AnyObject]()
}
self.selectedOption?.append(selectedOption)
}
public func getSelectedItem() -> [AnyObject]? {
return self.selectedOption
}
public func removeSelectedItem(selectedOption: AnyObject) -> Void {
if self.selectedOption != nil {
if selectedOption is String {
self.selectedOption = self.selectedOption!.filter() { $0 as! String != selectedOption as! String }
} else if ( selectedOption is Int ) {
self.selectedOption = self.selectedOption!.filter() { $0 as! Int != selectedOption as! Int }
}
}
}
public func isSelected(selectedOption: AnyObject) -> Bool {
if self.selectedOption != nil {
if selectedOption is String {
return (self.selectedOption!.indexOf({ $0 as! String == selectedOption as! String }) != nil)
} else if ( selectedOption is Int ) {
return (self.selectedOption!.indexOf({ $0 as! Int == selectedOption as! Int })) != nil
}
}
return false
}
public func clearSelectedItems() -> Void {
self.selectedOption = nil
}
public func checkDependencies() -> Bool {
if dependencies != nil {
for dependency in dependencies! {
if dependency.getSelectedItem() == nil {
return false
}
}
}
return true
}
}
var make: SearchOption = SearchOption(title: "Make", allowAny: true, allowMultiple: true, dependencies: nil)
make.setSelectedItem("Vauxhall")
make.setSelectedItem("Mazda")
make.setSelectedItem("Audi")
print(make.getSelectedItem())
make.removeSelectedItem("Mazda")
print(make.getSelectedItem())
print(make.isSelected("Audi"))
Outputs:
Optional([Vauxhall, Mazda, Audi])
Optional([Vauxhall, Audi])
true
Expanding on the comments already made to the question: You should make
the class generic, so that it can be used with String or Int
items. What you need is that the items can be compared with ==,
i.e. that the type is Equatable.
The class declaration would then be
public class SearchOption<T : Equatable> {
// ...
}
and all occurrences of AnyObject have to be replaced by T.
The isSelected method simplifies to
public func isSelected(selectedOption: T) -> Bool {
if self.selectedOption != nil {
return self.selectedOption!.contains( { $0 == selectedOption })
}
return false
}
which can be further simplified using optional chaining
and the nil-coalescing operator ??:
public func isSelected(selectedOption: T) -> Bool {
return self.selectedOption?.contains( { $0 == selectedOption }) ?? false
}
Similarly:
public func removeSelectedItem(selectedOption: T) -> Void {
self.selectedOption = self.selectedOption?.filter( { $0 != selectedOption } )
}
For String items you would then create an instance of the class
with
let make = SearchOption<String>(title: "Make", allowAny: true, allowMultiple: true, dependencies: nil)
Managed to solve the problem using Generics and Equatable (I've never really used these before so forgive me)
In the class T is constrained to a type of Equatable, which seems to keep .filter() and .indexOf happy.
When creating the instance you pass the type you'll be using:
var make: SearchOption = SearchOption<String>(title: "Make", allowAny: true, allowMultiple: true, dependencies: nil)
The resulting class looks as follows
public class SearchOption<T: Equatable> {
private var title: String
private var allowAny: Bool
private var allowMultiple: Bool
private var dependencies: [SearchOption]?
// Store the selected Item in an array of AnyObjects.
private var selectedOption: [T]?
init(title: String, allowAny: Bool, allowMultiple: Bool, dependencies: [SearchOption]?) {
self.title = title
self.allowAny = allowAny
self.allowMultiple = allowMultiple
self.dependencies = dependencies
}
public func setSelectedItem(selectedOption: T) -> Void {
if self.selectedOption == nil || !self.allowMultiple {
self.selectedOption = [T]()
}
self.selectedOption?.append(selectedOption)
}
public func getSelectedItem() -> [T]? {
return self.selectedOption
}
public func removeSelectedItem(selectedOption: T) -> Void {
if self.selectedOption != nil {
self.selectedOption = self.selectedOption!.filter() { $0 != selectedOption }
}
}
public func isSelected(selectedOption: T) -> Bool {
if self.selectedOption != nil {
return (self.selectedOption!.indexOf({ $0 == selectedOption }) != nil)
}
return false
}
public func clearSelectedItems() -> Void {
self.selectedOption = nil
}
public func checkDependencies() -> Bool {
if dependencies != nil {
for dependency in dependencies! {
if dependency.getSelectedItem() == nil {
return false
}
}
}
return true
}
}
var make: SearchOption = SearchOption<String>(title: "Make", allowAny: true, allowMultiple: true, dependencies: nil)
make.setSelectedItem("Vauxhall")
make.setSelectedItem("Mazda")
make.setSelectedItem("Audi")
print(make.getSelectedItem())
make.removeSelectedItem("Audi")
print(make.getSelectedItem())
print(make.isSelected("Mazda"))

Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?

Is there a standard swift class that is a Dictionary, but keeps keys in insertion-order like Java's LinkedHashMap? If not, how would one be implemented?
Didn't know of one and it was an interesting problem to solve (already put it in my standard library of stuff) Mostly it's just a matter of maintaining a dictionary and an array of the keys side-by-side. But standard operations like for (key, value) in od and for key in od.keys will iterate in insertion order rather than a semi random fashion.
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
private var _dictionary:Dictionary<KeyType, ValueType>
private var _keys:Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = map(dictionary.keys) { $0 }
}
subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key)
}
else {
self.updateValue(newValue!, forKey: key)
}
}
}
mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValueForKey(key)
}
mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
var values:GeneratorOf<ValueType> {
get {
var index = 0
return GeneratorOf<ValueType> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return self._dictionary[key]
}
}
}
}
}
extension OrderedDictionary : SequenceType {
func generate() -> GeneratorOf<(KeyType, ValueType)> {
var index = 0
return GeneratorOf<(KeyType, ValueType)> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return (key, self._dictionary[key]!)
}
}
}
}
func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
Swift 5 version:
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType: Hashable, ValueType> {
private var _dictionary: Dictionary<KeyType, ValueType>
private var _keys: Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity: Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary: Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = dictionary.keys.map { $0 }
}
subscript(key: KeyType) -> ValueType? {
get {
_dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key: key)
} else {
_ = self.updateValue(value: newValue!, forKey: key)
}
}
}
mutating func updateValue(value: ValueType, forKey key: KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key: KeyType) {
_keys = _keys.filter {
$0 != key
}
_dictionary.removeValue(forKey: key)
}
mutating func removeAll(keepCapacity: Int) {
_keys = []
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: keepCapacity)
}
var count: Int {
get {
_dictionary.count
}
}
// keys isn't lazy evaluated because it's just an array anyway
var keys: [KeyType] {
get {
_keys
}
}
var values: Array<ValueType> {
get {
_keys.map { _dictionary[$0]! }
}
}
static func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
static func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
}
extension OrderedDictionary: Sequence {
public func makeIterator() -> OrderedDictionaryIterator<KeyType, ValueType> {
OrderedDictionaryIterator<KeyType, ValueType>(sequence: _dictionary, keys: _keys, current: 0)
}
}
struct OrderedDictionaryIterator<KeyType: Hashable, ValueType>: IteratorProtocol {
let sequence: Dictionary<KeyType, ValueType>
let keys: Array<KeyType>
var current = 0
mutating func next() -> (KeyType, ValueType)? {
defer { current += 1 }
guard sequence.count > current else {
return nil
}
let key = keys[current]
guard let value = sequence[key] else {
return nil
}
return (key, value)
}
}
I didn't found way to make values 'lazy'.. need more research