I'm trying to understand why the following is valid in Swift. I'm assuming it has to do with the way things are scoped in Swift.
let name = "test" //assigns "test" to name
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName { //assigns "John Appleseed" to name
let name = "no error" //assigns "no error" to name
greeting = "Hello, \(name)" //assigns "Hello, no error" to greeting
}
println(name) //prints "test"
What I believe is happening is this is creating 3 separate name constants all in different scopes. The first let name is in the global scope. Then the optional binding let name is another scope, and then within the if the let name is another scope. Then the final print goes back out to the global scope.
You have already understood:)
It uses the first variable it finds with such name, starting from the inner scope and going up.
Related
I'm having issues with appending an optional value to an array in Swift. The view I'm writing is for the creation of a routine for the gym. However my Routine object is not being instantiated as it should be.
I have experience with other programming languages but I am fairly new to Swift, and optionals.
My ViewController contains an optional variable:
var routine: Routine?
Where the Routine class contains:
name: String
exerciseList: [String]()
numOfSets: [Int]()
When I am preparing it to send the newly created routine to my other ViewController, I take the values from user input to edit the fields of the object.
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0") //numOfSetsOne is a text label
routine?.exerciseList.append(selectedExerciseOne!) //Haven't tested to see if this works yet
routine?.numOfSets[0] = numberOne! //This line is not working
routine = Routine(name: name)
To try a little debugging I put print statements on either side of the line like so:
print ("numberOne Value: \(numberOne!)")
routine?.numOfSets[0] = numberOne!
print ("numOfSets[0] Value: \(routine?.numOfSets[0])")
I expected the output from the second print statement to be identical to the first. However the terminal output:
numberOne Value: 3
numOfSets[0] Value: nil
Does anyone know what has gone wrong here?
Thanks
You have declared a property that may contain a Routine, but you have not assigned an instance of Routine to that property before trying to use it.
This means that, for example,
routine?.numSets[0] = numberOne!
doesn't do anything - routine is nil and so the statement is skipped.
You should create an appropriate init function for your Routine class and use that to create a new Routine and assign it to routine
For example:
class Routine {
var name: String
var exerciseList = [String]()
var numberOfSets = [Int]()
init(named: String) {
self.name = named
}
}
Then you can say
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
self.routine = Routine(named: name)
self.routine?.numberOfSets.append(numberOne!)
Coordinating related arrays can get a bit messy, so I would use a single array:
struct ExerciseSet {
let exerciseName: String
let sets: Int
}
class Routine {
var name: String
var exerciseList = [ExerciseSet]()
init(named: String) {
self.name = named
}
}
Your Routine is not initialised before its being assigned value
try
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
routine = Routine(name: name)
routine?.exerciseList.append(selectedExerciseOne!)
routine?.numOfSets[0] = numberOne!
I've read through numerous tutorials and tried countless things to get this to work but can't seem to find a solution.
All I want to do is use reflection to set the value for a named member variable. I can read the values just fine, but how do I write a value?
Here is my sample code:
class MyObject
{
public var myString : String = "Not working"
}
func test()
{
let value = "It works!"
let member = "myString"
var myObject = MyObject()
let mirror = Mirror(reflecting: myObject)
for (_, var attr) in mirror.children.enumerated() {
if attr.label == member {
print("Setting value of \(member)")
// attempt to set the member variable value
attr.value = value
break
}
}
print("New value: \(myObject.myString)")
}
Running this example, the output prints the old value of myString. I'm using Swift 4 for iOS.
Any help would be appreciated. Thank you!
Unfortunately, you cannot modify value with built-in Mirror.
Maybe try Runtime named library instead (which's the successor of now deprecated Reflection library).
OR
You can make it without reflection if it is acceptable for you, like:
class MyObject: NSObject {
#objc public var myString : String = "Not working"
}
func test() {
let value = "It works!"
let member = "myString"
let myObject = MyObject()
myObject.setValue(value, forKey: member)
print("New value: \(myObject.myString)")
}
Hope it helps you.
Swift's reflection capabilities are based around a struct called
Mirror. You create a mirror for a particular subject and the
mirror will then let you query it.
Means with Mirror you can not modify the subject in your case MyObject.
Read HERE for more info.
EDIT:
You can simply update your class property as shown below:
func test() {
let myObject = MyObject()
myObject.myString = "It works!"
print("New value: \(myObject.myString)") // "New value: It works!"
}
I'm new to Swift and worried I'm defining my variables incorrectly here. I'm sure it's a dumb mistake I'm making because I'm not totally understanding Swift syntax.
I am getting the error that my User type doesn't have any subscript members when I'm referencing them in a function:
let item = userDataSource[indexPath.row]
cell.leftLabel.text = item[0]
cell.rightLabel.text = item[1]
However, I am extending
extension AccountViewController: GetUserDelegate {
func getUserSuccess(user: User) {
self.userDataSource = [user]
}
}
And userDataSource is defined at the top of the class:
fileprivate var userDataSource: [User]
Am I not casting my variables correctly? The User model is just a mapped JSON obj:
guard let user = Mapper<User>().map(JSONObject: value)
What am I missing here? Am I correct in that I am casting my variables incorrectly?
This is ultimately how I'd like the array to look:
["Name", "Bob Jim"],
["MC #", "1234567"],
["Company Name", "Bob's Truckin"],
["Truck Type", "Flat Bed"],
["Cell", "(555) 555-5555"],
["Dispatch", "(999) 999-9999"],
["Favorite Destinations", "Los Angeles"]
Your userDataSource is an array of users like you mentioned.
fileprivate var userDataSource: [User]
You'll get back an user of any specified index by
let item = userDataSource[indexPath.row] as User
Here, item is user object which has no subscript member, if you use item[0].
you should use something like below:
item.name // or any property
or
cell.leftLabel.text = item.name
In Apple's "Swift Tour" introduction to swift, they give the following example of optionals:
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Does the second line serve any purpose?
optionalString == nil
is an expression, and in a compiled program the expressions's value (in this case: false)
is ignored.
However, the Swift Guided Tour is downloadable as a Playground file, and in a Playground
any expression value is displayed in the right column.
You are encouraged to edit the code listings and see the the results immediately.
The first bit of code
var optionalString: String? = "Hello"
optionalString == nil
Is just comparing the optional to nil, when this code is run in a playground false will show up on the right hand side.
The second chunk.
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Is using what is called optional binding. The author could just as easily have written
var optionalName: String? = "John Appleseed"
var greeting = "Hello, \(optionalName!)"
Note the exclamation mark at the end of optionalName! this is whats known as Forced Unwrapping, In this particular case it would have been fine since we set the value of the variable immediately before hand. In general though Forced Unwrapping is considered to be unsafe since you're basically saying that you know better than the compiler. Forced Unwrapping is sometimes affectionally referred to as the crash operator ;)
You can find the section on optionals here
var optionalString: String? = nil
optionalString == nil
change your code as above,and you can see the use of second line.
It tell you the optionalValue can be set to nil,and compare to nil.Most of the classes can't do these.
While reading the The Swift Programming Language, I came across this snippet:
You can use if and let together to work with values that might be
missing. These values are represented as optionals. An optional value
either contains a value or contains nil to indicate that the value is
missing. Write a question mark (?) after the type of a value to mark
the value as optional.
// Snippet #1
var optionalString: String? = "Hello"
optionalString == nil
// Snippet #2
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Snippet #1 is clear enough, but what is happening in the Snippet #2? Can someone break it down and explain? Is it just an alternative to using an if - else block? what is the exact role of let in this case?
I did read this page, but still a little confused.
if let name = optionalName {
greeting = "Hello, \(name)"
}
This does two things:
it checks if optionalName has a value
if it does, it "unwraps" that value and assigns it to the String called name (which is only available inside of the conditional block).
Note that the type of name is String (not String?).
Without the let (i.e. with just if optionalName), it would still enter the block only if there is a value, but you'd have to manually/explicitly access the String as optionalName!.
// this line declares the variable optionalName which as a String optional which can contain either nil or a string.
//We have it store a string here
var optionalName: String? = "John Appleseed"
//this sets a variable greeting to hello. It is implicity a String. It is not an optional so it can never be nil
var greeting = "Hello!"
//now lets split this into two lines to make it easier. the first just copies optionalName into name. name is now a String optional as well.
let name = optionalName
//now this line checks if name is nil or has a value. if it has a value it executes the if block.
//You can only do this check on optionals. If you try using greeting in an if condition you will get an error
if name{
greeting = "Hello, \(name)"
}
String? is a boxed-type, variable optionalName either contains a String value or nothing(that is nil).
if let name = optionalName is an idiom, it unboxes the value out of optionalName and assign it to name. In the meanwhile, if the name is non-nil, the if branch is executed, otherwise the else branch is executed.