Value of type _ cannot convert to specified type Set<> Swift - swift

I'm trying to create a set of random exercises. I have made my struct Hashable and Equatable following the tutorial here https://medium.com/#JoyceMatos/hashable-protocols-in-swift-baf0cabeaebd and that is working fine so it's ready to be put in a Set<>.
When I use an Array to collect the workout exercises, as per below, it works fine. But when I switch to a Set<> I get an error "cannot convert value of type [_] to specified type 'Set'. What is it about 'Sets' that mean you can't map in the same way as an Array?
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
//error triggers on the line below if I switch [WorkoutExercise]
//for Set<WorkoutExercise> (which conforms to Hashable/Equatable
let workoutSet : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
print (workoutSet)
}
There is an answer here with a similar error message Cannot convert value of type '[_]' to specified type 'Array' but my array wouldn't be empty as in this example so I don't think this is the same root cause?
UPDATE : for anyone having the same problem, you can use Array but then simply convert the Array to a Set afterwards if the correct elements are Hashable/Equatable

If creating the array works create the array and then make the Set from the array. If all involved objects conform to Hashable this is supposed to work.
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
let workoutArray : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
let workoutSet = Set(workoutArray)
print (workoutSet)
}

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

Swift Dictionary access value through subscript throws Error

Accessing a Swift Dictionary value through subscript syntax is gives error Ambiguous reference to member 'subscript'
Here is the code
class Model {
struct Keys {
static let type :String = "type" //RowType
static let details :String = "details"
}
var type :RowType = .None
var details :[Detail] = []
init(with dictionary:Dictionary<String, Any>) {
if let type = dictionary[Keys.type] as? String {
self.type = self.rowTypeFromString(type: type)
}
if let detailsObj = dictionary[Keys.details] as? Array { //Error : Ambiguous reference to member 'subscript'
}
}
}
if i remove the type casting as? Array at the end of optional-binding it compiles fine
I am expecting the value of details key to be an Array, I know that i can use [String,Any] instead of Dictionary<Key, Value>, What is causing the issue ?
Array doesn't work like NSArray, you explicitly need to tell what type your array is storing.
If you want to store Detail objects in it, the proper syntax is Array<Detail> or simply [Detail]
if let detailsObj = dictionary[Keys.details] as? Array<Detail> {
}
Issue is solved by explicitly specifying the Type of objects the array contains, In my case it was Array<[String:Any]>
if let detailsObj = dictionary[Keys.details] as? Array<[String:Any]> { //we should also specify what type is present inside Array
}
Credits: #Hamish, #Dávid Pásztor
Thanks!

Variable used within its own initial value Swift 3

I try to convert my code to swift 3 an I have spent hours on the following error:
Type 'Any' has no subscript members
Here's was my original code:
let data: AnyObject = user.object(forKey: "profilePicture")![0]
I looked at the answers here but I'm still stuck. (I do programming as a hobby, I'm not a pro :/)
I've try that:
let object = object.object(forKey: "profilePicture") as? NSDictionary
let data: AnyObject = object![0] as AnyObject
But now I get this error:
Variable used within its own initial value
Second issue: Use always a different variable name as the method name, basically use more descriptive names than object anyway.
First issue: Tell the compiler the type of the value for profilePicture, apparently an array.
if let profilePictures = user["profilePicture"] as? [[String:Any]], !profilePictures.isEmpty {
let data = profilePictures[0]
}
However, the array might contain Data objects, if so use
if let profilePictures = user["profilePicture"] as? [Data], !profilePictures.isEmpty {
let data = profilePictures[0]
}
Or – what the key implies – the value for profilePicture is a single object, who knows (but you ...)
And finally, as always, don't use NSArray / NSDictionary in Swift.

Subscript range of [UInt8] - Swift

So the issue I'm having is that I have an object with a argument in the init() that requires an [UInt8]. I want to be able to grab a range from another array and use that in the init. See example.
class Test {
init(fromArray: [UInt8]) {
// performs work
}
}
let myStockArray: [UInt8] = [1,2,3,4,5,6,7,8] // reference array
let test = Test(fromArray: myStockArray[1...4]) // doesn't work
How can I get this to work? The error I get is: Cannot subscript a value of type '[UInt8]' with an index of type 'CountableClosedRange'
Subscripting an array with a range doesn't return an array and this is the main issue. You are trying to setArraySlice<UInt8> type data to the constructor that have inside [UInt8] type.
Try this approach:
class Test {
init(fromArray: [UInt8]) {
// performs work
}
}
let myStockArray: [UInt8] = [1,2,3,4,5,6,7,8] // reference array
let test = Test(fromArray: Array(myStockArray[1...4]))