Retrieve original argument name in function - swift

I have a function that takes a dictionary key (parsed from a JSON file) and returns the value if it can be cast as a string, or an empty string if it can't be cast or doesn't exist. The function itself works fine, but I want to have it log a warning if the value is invalid/missing and I'm not sure what the best way to do it is. I know I could just pass the text of the key as a second argument, but the function is going to be running many times so I was hoping for something more elegant. Is it possible to retrieve the original argument/key from within the function, or might there be some better way of doing this?
let someDict = [String:Any]()
func getStringObject (fromKey: Any?) -> String {
if let object = fromKey as? String {
return object
} else {
print("Invalid or missing value for ???, using default.") // want to print the original argument or at least the key string
return ""
}
}
let someString = getStringObject(someDict["someInvalidKey"]) // should print someDict["someInvalidKey"] or someInvalidKey

Related

Append Values in NSSet To An Array of Type [String]

I am attempting to get the values from an NSSet in core data and append those values to an array of type String.
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
recipeStep.append(String(i))
}
}
}
entity.steps is the list of steps tied to a core data entity. This is an NSSet. I am trying to copy those values to an array of type String.
#State var recipeStep: [String]
When trying to do this in my for in loop, I receive the following error: No exact matches in call to initializer
If I remove the conversion of "I" to String, I receive the following error:
Cannot convert value of type NSSet.Element (aka Any) to expected argument type String
Any idea on how to get this to work?
NSSet is defined in Objective C, which didn't have generics. It's an untyped collection, so you don't statically know anything about its elements.
As you've noticed, your i variable isn't a String, it's an Any.
You're confusing type coercion ("casting") with type conversion. If i were a Double, you could call String(i) to invoke an initializer which takes a double, and processes into a String.
You tried something similar by calling String(i), where you're making the Swift compiler find an initializer on String with the signitiure init(_: Any).
There is no such initializer. And besides, that's not what you want. You don't want to create a new String from a different kind of value. You already have a string, it's just "hidden" behind an Any reference.
What you're looking for is to do a down-cast, from Any to String:
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
guard let step = i as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
recipeStep.append(i as String)
}
}
}
I'll warn you though, there are several issues/blemishes with this code:
You're using a for loop to do what is ultimately just a mapping operation
Your computation doesn't return its ouput, and instead indirectly achieves its goal through a side-effect on recipeStep
Your computation doesn't take a its input as a parameter, and instead indirectly achieves its goal through a side-effect on entity
i is conventionally expected to be an integer index of a for loop iterating over a sequence of numbers. Here it's an Any (a String at runtime)
Here's what I would suggest instead:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
guard let steps = entity.steps else { return [] }
return steps.map { step in
guard let stringStep = step as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
return step
}
}
Then in the rest of your code (and your tests), you can write self.recipeSteps = getRecipeSteps(from: myEntity). Elegant!
If you're certain that these entity.steps values can only ever be strings, then you can boil this down to a single map with a force-cast:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
entity.steps?.map { $0 as! String } ?? []
}
Just convert directly:
let set = Set(["1", "2", "3"])
let array = Array(set)
DDLog(set)//Set<String>)
DDLog(array)//[String]

How to get value of a NSSingleObjectArrayI

func responseDataHandler(data: NSDictionary) {
let temperature_c = data.value(forKeyPath: "data.current_condition.temp_C")
DispatchQueue.main.async{
self.Temperature.text = temperature_c as? String
}
}
I have the above code where I am accessing a weather API which returns data in the form of an NSDictionary to this function. I need to access the value in temperature_c which when I try to print it, it says that it is: Optional(<__NSSingleObjectArrayI 0x600002147fd0>(
25
)
). Temperature is the outlet for label on my storyboard which I want to take on the value of 25 however as written now, it doesn't work and I have tried everything to try and access the value in the Single Object Array but nothing is working. I found this stack overflow question that was similar but it doesn't work for my situation because I keep getting the error that temperature_c is of type any and doesn't have subscripts.
The issue is that you can't cast to String an array, you should try to convert it to [String]. So could change your code to:
self.Temperature.text = (temperature_c as? [String])?.first ?? "Not available"
Let's go step by step:
temperature_c as? [String] tries to convert the NSDictionary to a String array which is the expectable type.
Since the previous step may return nil we have to use optional chaining ?. If we got a valid array using first return the the arrays first element.
Since both previous steps can return nil we can use nil coalescing operator to return a default value. In this case I use "Not available" but you can set any value.
You could write it in a more verbose way like this:
var text2Display2 = "Not available"
if let theArray = temperature_c as? [String] {
if let element = theArray.first {
text2Display2 = element
}
}
self.Temperature.text = text2Display2

How to compare string in swift4

i wanna solve below error
Thanks for help
As the error states you cannot compare a String (mycheck) with an array of Any (localMsg), you must compare the string directly with something like
if let localMsg = oDict_Fail["message"] as? String, localMsg == mycheck {
NSLog("True")
} else {
NSLog("False")
}
Here we first try to access the oDict_Fail dictionary with the key message, if it exists try to cast it as a String and if it is successful only then do the comparison, if any of this fails the else branch will be executed.
You can read more about optional binding here.

Swift: if is let redundancy

I just joined a project that has a lot of existing code. The previous programmer was perhaps unfamiliar with Swift or began development in the early stages of the Swift language. They seemed to be using the if let statement in an odd way. They seemed to want to use the statement as a if is let. Before I edit the code I would like to know if there is any valid use for this:
// In JSON parser
if value is String, let string = value as? String {
document.createdBy = string
}
First checking if value is of type String seems redundant to me. Doesn't Swift check for this in the let string = value as? String portion of the statement?
QUESTION
Why would this need to be checked twice? Or would there be a reason for this?
You're correct, this is redundant. If value is not a string, then value as? String would return nil, and the conditional binding would fail.
To check the type, and not use the casted result:
if value is String {
// Do something that doesn't require `value` as a string
}
To check the type and use the result:
if let value = value as? String { // The new name can shadow the old name
document.createdBy = value
}
Doing both makes no sense.

Error: Expression<String?> is not convertible to String

My code is:
func getTimeStamps( tablename : String) -> String {
let time_stamps = db["time_stamps"]
let t_tabelle = Expression<String?>["tabelle"]
let t_time_stamp = Expression<String?>["TIME_StAMP"]
let query = time_stamps.filter(like(tablename, t_tabelle))
return query[t_time_stamp]
}
But I get an error on conversion:
Expression<String?> is not convertible to String
How can I return a String?
Thanks
Hauke
The error refers to the fact that your function signature for getTimeStamps, String -> String, has return type String, but the value you're returning, query[t_time_stamp], is an Expression<String?>.
Query structures can be subscripted with Expressions to return a namespaced version of the expression:
let id = Expression<Int64>("id") // literally: "id"
let users = db["users"]
let users_id = users[id] // literally: "users"."id"
In your case, subscripting query with t_time_stamp is merely returning a new, namespaced version of the t_time_stamp expression (in your version, "time_stamps"."TIME_StAMP"). This is helpful for disambiguation, but unlikely your intent.
It's tough to tell from the code provided exactly what you want to return from the function, but it looks like you want to execute the query in order to extract a value. Row structures, once fetched, can be subscripted with expressions to retrieve the underlying value.
If you're looking to retrieve a single row, try the following:
if let row = time_stamps.filter(like(tablename, t_tabelle)).first {
return row[t_time_stamp]
}
Your function, however, still returns String, not String?. If it's ever possible for your query to return zero rows or for any of the rows it does return to have a NULL timestamp column, you need to handle your optionals accordingly.
If, however, a NULL timestamp would indicate a programming error/bug, you should update String? to String accordingly:
let t_time_stamp = Expression<String>["TIME_StAMP"]
// ...
return query.first![t_time_stamp]
Please note that the above will crash if you're mishandling the potential for optional values.