Getter Setter With Type of Any Swift - swift

Is it possible to do a getter and setter for an attribute that has a type of 'Any'
Here is my thought:
private var _valueObject: Any?
public var valueObject: Any? {
set {
if newValue is String {
self._valueObject = newValue as? String
} else if newValue is BFSignature {
self._valueObject = newValue as? BFSignature
}
}
get {
if self._valueObject is String {
return self._valueObject as? String
} else if self._valueObject is BFSignature {
return self._valueObject as? BFSignature
} else {
return self._valueObject
}
}
}
When I try to use it through out my code though I get errors stating:
Cannot compare String to type Any
Is there a way to use something like this without casting the 'valueObject' to a string whenever I need it. A way to use it and it already knows its a 'String' or 'BFSignature' instead of 'Any'.
Here is an example of the error:
I would rather it just know that cellValue is a 'String.' Instead of casting it each time I use it.

You shouldn't use Any
In my opinion, you should make a common representation of the API call result instead of using Any. You know exactly what the API is going to return, don't you? It's either a String or something that you turn into your custom object BFSignature.
Therefore, you can make an enum to represent your API call result:
enum APIResult {
case signature(BFASignature)
case justString(String)
}
and use it like
private var _valueObject: APIResult?
if let stringValue = newValue as? String {
self._valueObject = .justString(stringValue)
}
if let signatureValue = newValue as? BFSignature {
self._valueObject = .signature(signatureValue)
}

If there are a fixed number of types that you need to use here, you can use an enum:
struct BFSignature {
var a: Int
}
enum Either {
case bfSig(BFSignature)
case string(String)
}
var a: Either
var b: Either
a = .bfSig(BFSignature(a: 7))
b = .string("Stack Overflow")
a = b
Usage:
switch (b) {
case Either.bfSig(let signature):
print(signature.a) // Output integeral value
case Either.string(let str):
print(str) //Output string value
}

Related

Swift - unwrapping a Double value with a Guard let statement

to unwrap a Double from my default values, I seem to have to do it with two guard let statements to unwrap the value safely such as in what follows:
guard let distanceAwayPreference = self.defaults.string(forKey: "distancePreference") else {
return
}
guard let doubleDAP = Double(distanceAwayPreference) else {
return
}
– because if I do it like this:
guard let DistanceAwayPreference = self.defaults.double(forKey: "distancePreference") else {
return
}
– I get the error:
Initializer for conditional binding must have Optional type, not 'Double'
Is there a better way so i can do it once, or have less code throughout my app?
The method double(forKey:) is not returning an optional at all. According to the documentation it returns:
The double value associated with the specified key. If the key doesn‘t exist, this method returns 0.
So you can't optional bind it to a variable. This is why you've got that error:
Initializer for conditional binding must have Optional type, not 'Double'
For less and cleaner code (when using it), you can use a simple property wrapper like:
#propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
Then you can have a custom helper struct like this:
struct UserDefaultsConfig {
#UserDefault("distancePreference", defaultValue: 0)
static var distancePreference: Double
}
And use it late like:
UserDefaultsConfig.distancePreference = 12
I think it's better to return Any from defaults instead of String. So, this way you can use optional casting.
guard let doubleDAP = self.defaults.object(forKey: "distancePreference") as? Double else {
return
}

Decode string or int value in structure will return string("1") in Swift 4

I have a structure that some of the values will get Integer or string so I used this structure in my structure to parse JSON in the correct way but the problem is that when I want to print the value it will print string("1") instead of 1.
public struct player1_result_sheet : Decodable {
let ans_1 : QuantumValue?
let ans_2 : QuantumValue?
let ans_3 : QuantumValue?
let ans_4 : QuantumValue?
}
enum QuantumValue: Decodable {
case int(Int), string(String)
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self = .int(int)
return
}
if let string = try? decoder.singleValueContainer().decode(String.self) {
self = .string(string)
return
}
throw QuantumError.missingValue
}
enum QuantumError:Error {
case missingValue
}
}
Here is the printing after decode:
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!
If you want to print the value without the enum wrapper, just implement description:
extension QuantumValue: CustomStringConvertible {
public var description: String {
switch self {
case let .string(string):
return string
case let .int(number):
return "\(number)"
}
}
}
Your enum case is string(String) so it prints case(value)
string("1")
You can solve it by creating varible inside enum which returns you a value depends on case of QuantumValue
var value: Any {
switch self {
case .int(let value):
return value
case .string(let value):
return value
}
}
then you can use it like this:
...ans_1!.value)!
1
Also note that type of value is Any so if you want to work with it as String, you have to downcast it
if let string = ...ans_1.value)! as? String {
...
}
QuantumValue is declared as enum, both good cases have associated values.
So printing a value prints both, the case and the associated value.
You could add two properties intValue and stringValue inside QuantumValue
var intValue : Int? {
guard case .int(let num) = self else { return nil }
return num
}
var stringValue : String? {
guard case .string(let string) = self else { return nil }
return string
}
Then you can print
player1_result_sheet?.ans_1?.intValue
By the way the name player1_result_sheet is pretty php-ish.
Please conform to the naming convention.
Structs, classes and enums are UpperCamelCased → Player1ResultSheet
Variables and functions are lowerCamelCased → ans1
And please consolidate your optionals, 6 (six) question and exclamation marks in one line is pretty weird
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!

Converting a rawValue into a string in Swift

Apple has made changes from Swift 3 to 4. When I run the following code:
let metadata = [ PDFDocumentAttribute.titleAttribute,
PDFDocumentAttribute.authorAttribute,
PDFDocumentAttribute.subjectAttribute,
PDFDocumentAttribute.creatorAttribute,
PDFDocumentAttribute.producerAttribute,
PDFDocumentAttribute.creationDateAttribute,
PDFDocumentAttribute.modificationDateAttribute,
PDFDocumentAttribute.keywordsAttribute ]
if var attributes = pdfDoc.documentAttributes {
for (index, value) in metadata.enumerated() {
if attributes[value] != nil {
print("\(metadata[index])): \(String(describing: attributes[value]!))")
} else {
print("\(metadata[index]): nil")
}
}
I now get: PDFDocumentAttribute(_rawValue: Title) instead of "Title", which I got before as the value of metadata[index].
How do I get rid of the rawValue stuff?
The PDFDocumentAttribute type has a property called rawValue that contains the old string value. So you can say
print("\(metadata[index].rawValue): \(String(describing: attributes[value]!))")
As an aside, instead of force-unwrapping the attribute you can use an if let, as in
if let attr = attributes[value] {
print("\(metadata[index].rawValue): \(attr)")
} else {
print("\(metadata[index].rawValue): nil")
}
If you add this extension:
extension PDFDocumentAttribute: CustomStringConvertible {
public var description: String {
return self.rawValue
}
}
Now you can just do:
// Forcing the downcast has little risk here
// but you may want to use `as?` and test for the optional instead
let attributes = pdfDoc.documentAttributes as! [PDFDocumentAttribute:Any]
for meta in metadata {
print("\(meta): \(attributes[meta] ?? "nil")")
}
Note that you can also do:
for attribute in attributes {
print("\(attribute.key): \(attribute.value)")
}
Which will just print out the attributes that exist on the document.

Swift enum get an associated value without passing through a cases [duplicate]

I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.

Reflection in swift 2

I have a class User:
import UIKit
import ObjectMapper
class User: NSObject, CustomStringConvertible, Mappable {
var FirstName: NSString! ;
var LastName: NSString! ;
required init?(_ map: Map){
}
func mapping(map: Map) {
FirstName <- map["FirstName"]
LastName <- map["LastName"]
}
override var description:String {
var s:String=""
//USE REFLECTION TO GET NAME AND VALUE OF DATA MEMBERS
for var index=1; index<reflect(self).count; ++index {
s += (reflect(self)[index].0 + ": "+reflect(self)[index].1.summary+"\t")
}
return s
}
}
In swift 1.2, I was using reflect() method to get array of all the data members with their names and values.
Now, after I have updated to swift 2, I am getting the following error:
'reflect' is unavailable: call the 'Mirror(reflecting:)' initializer
With some trials, I was able to get the count of data members by this: Int(Mirror(reflecting: self).children.count), but still, I am unable to get the member name and its value.
I have looked into the following resources:
https://netguru.co/blog/reflection-swift
http://nshipster.com/mirrortype/
UPDATE
I have found the an answer here: https://stackoverflow.com/a/32846514/4959077. But this doesn't tell how to find out the type of reflected value. If the value is int and we parse it into String then it gives error.
You may access the reflected attribute "label" name, value and type as follows:
let mirror = Mirror(reflecting: SomeObject)
var dictionary = [String: Any]()
for child in mirror.children {
guard let key = child.label else { continue }
let value: Any = child.value
dictionary[key] = value
switch value {
case is Int: print("integer = \(anyValue)")
case is String: print("string = \(anyValue)")
default: print("other type = \(anyValue)")
}
switch value {
case let i as Int: print("• integer = \(i)")
case let s as String: print("• string = \(s)")
default: print("• other type = \(anyValue)")
}
if let i = value as? Int {
print("•• integer = \(i)")
}
}
Note: per the question followup, three approaches to determine the type of the reflected value are shown.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}