how to understand some functions of this code [closed] - swift

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
below are the code. About the button part. Why the if let is needed?
import SwiftUI
struct descView: View {
#ObservedObject var data: Activities
var activity: ActivityItem
var body: some View {
List {
Section {
VStack(alignment: .leading){
Text(activity.desc)
}
}
Section {
Text("How many times completed")
Text("\(activity.amount)")
Button("Make complete once") {
var newActivity = activity
newActivity.amount += 1
if let index = data.activities.firstIndex(of: activity) {
//The firstIndex(of:) method tells you the index of an element in an array if it exists, or returns nil otherwise.
data.activities[index] = newActivity
}
}
}
}
.navigationTitle(activity.name)
}
}
I'm a new beginner of swift.Cannot figure out what is the function of below part of code.Could someone explain it? thanks!
if let index = data.activities.firstIndex(of: activity) {
//The firstIndex(of:) method tells you the index of an element in an array if it exists, or returns nil otherwise.
data.activities[index] = newActivity

To make sure index isn't nil and/or optional value.
firstIndex returns nil if the activity isn't in data.activities
activities[index] // does not work if `index` is optional.
You can do this several different ways such as using guard but the goal is the same when you reach the line that uses index make sure it is not nil.
Per your comment right now the Button is making a copy of the activity, changing the copy, and then replacing the list's activity with the updated one.
This is needed because var activity: ActivityItem is immutable (cannot change). If you add #Binding to the activity and make the connection with the list's object in the parent view you can skip this step and alter it directly.
https://developer.apple.com/wwdc21/10018
Shows a sample around minute 8
and it would look something like this in your code
List($data.activities) { $activity in
descView(activity: $activity)
}

In Swift, variables can be optional and non-optional. When a variable is optional, this means it can be nil. If it's non-optional, it is not allowed to be nil. To set a variable's type to optional, you add a ? to the end of the type (e.g. String?).
For example:
var nonOptionalInt: Int = 5
var optionalInt: Int? = 5
optionalInt = nil // Works fine
nonOptionalInt = nil // This will result in an error (and crash)
Sometimes, you want to check if an optional variable is nil or not before you proceed. In your case, you want to make sure index is not nil. You want to "unwrap" the optional index (Int?) to become a non-optional index (Int).
By using if let you are unwrapping the variable (via a process called optional binding) and if it's not nil, you can use it. If it is nil you will skip the if block and the code inside will not be executed.
A full example:
import Foundation
let optionalInt: Int? = 5
print(type(of: optionalInt)) // Prints Optional<Int> - At this moment, the content of the optionalInt variable MIGHT be nil
if let unwrappedInt = optionalInt { // If optionalInt is NOT nil, this statement will be true and I enter the if-block
print(unwrappedInt) // Prints 5 - Now I know for sure that unwrappedInt is not nil
print(type(of: unwrappedInt)) // Prints Int - Not optional anymore
}
let anotherOptionalInt: Int? = nil
if let unwrappedInt = anotherOptionalInt { // If optionalInt is NOT nil, this statement will be true and I enter the if-block
print(unwrappedInt) // This won't be printed
} else {
print("anotherOptionalInt was nil!") // This will print
}
Please note that you can just use the same variable name if let optionalInt = optionalInt { .. }, and the optionalInt inside the if-block's scope will be a non-optional Int. The optionalInt outside the if-block will remain the optional Int?.
Check this page more information on Optionals in Swift.

Related

I can't understand the swift language question mark operator in dictionary in this situation transitions[prev]?[transition] [duplicate]

This question already has answers here:
What is an optional value in Swift?
(15 answers)
When two optionals are assigned to an if let statement, which one gets unwrapped? Swift language
(1 answer)
Closed 5 years ago.
I've searched in a lot of places and communities among the internet to find what the heck is happening in this programing syntax.
I'm seeking, desperately, for guidance in this code.
What is happening with the compiler in these specific declarations?
transitions[prev]?[transition]
transitions[state]?[transition] != nil
This is how the class is declared
public final class StateMachine<State: Hashable, Transition: Hashable>
This is the variables
public var state: State
private var transitions = [State:[Transition:State]]()
And these are the examples:
First situation - What is happening in the transitions[prev]?[transition]
public final func advance(transition: Transition, observe: Observer? = nil) -> State {
let prev = state
if let next = transitions[prev]?[transition], next != prev {
state = next
observe?(prev, next)
}
return state
Second situation - What is happening in the return transitions[state]?[transition] != nil
public final func canAdvance(transition: Transition) -> Bool {
return transitions[state]?[transition] != nil
}
That's all i want to understand. What is happening in these moments?
The question mark operator signifies optionality in Swift.
You can declare many things a optional, meaning they could be nil or hold a value. This includes for example variable declarations, computed properties, return values of functions and callbacks/closures. Also certain operations like casting or retrieving values from dictionaries will yield optional values.
When you want to use the value contained you have to unwrap them, cause there might not be one and the may be pointing to nil. There are many ways and forms of unwrapping and optionality chaining.
Explaing your particular examples:
In a dictionary retrieving a stored value via a key as in myDict[myKey] returns an optional value. The value for the key stored within your specific dictionary is another dictionary. By declaring transitions[state]?[transition] you say basically "if there is a dictionary found for the key state, go ahead and continue with this dictionary and get the value for the key transition for that dictionary, otherwise use nil".
This code:
return transitions[state]?[transition] != nil
is basically a shorter way of writing this:
if let stateDict = transitions[state] {
return stateDict[transition] != nil
} else {
return false
}
Your other example is about an optional closure passed into a function. You can also pass optional closures into functions and call them via closure?(). The ? signifies that if nil is passed for the closure, nothing should be done, otherwise it should be executed.
This code:
observe?(prev, next)
is basically a shorter way of writing this:
if let observeClosure = observe {
observeClosure(prev, next)
}
Some more optionality explanations:
If you work with an optional value from a declared variable you can safely unwrap it like so:
func square(myValue: Int?) → Int {
guard let myValue = myValue else {
return 0
}
return myValue * myValue
}
or
func square(myValue: Int?) → Int {
if let myValue = myValue {
return myValue * myValue
} else {
return 0
}
}
or you could define a fallback with the ?? operator
func square(myValue: Int?) → Int {
return myValue ?? 0 * myValue ?? 0
}
You could also use the ! operator to unwrap unsafely and if nil is found your app would crash. You should never do that unless you can guarantee that nil cannot be found like in:
func square(myValue: Int?) → Int {
if myValue != nil {
myValue! * mayValue! {
} else {
return 0
}
}
In brief, transitions[state]?[transition] from time to time can be nil.
So, if let next = transitions[prev]?[transition] unwraps this variable moves the algorithm inside if-parenthesis
if let next = transitions[prev]?[transition] {
// this code executes here if 'transitions[prev]?[transition]' is not nil
state = next
observe?(prev, next) // this closure possibly can be nil. if nil Swift just skips this line
}

How can I prevent this kind of logic error? (var x changes between its value is copied to another var y and the use of var y, so that y is outdated)

Summary:
I made a mistake in my Swift code and I've fixed it. Then I asked myself why this happened and how I could avoid it. I tried some ways but nothing helps.
I put the mistake and my thinking below. I hope you could teach me the right method to avoid this kind of mistake, but any idea, hint, or suggestion would be appreciated.
How can I avoid this kind of logic error?
The following is an excerpt from my assignment for Stanford cs193p course.
class SomeClass {
...
var accumulator: Double?
func someFunc() {
// I use `localAccumulator` and write `self.` for clarity.
if let localAccumulator = self.accumulator { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}
}
private func performPendingBinaryOperation() {
accumulator = pendingBinaryOperation.perform(with: accumulator)
}
...
}
The problem here is that the line B has changed the value of instance value self.accumulator, and the line C should use the new value stored in self.accumulator, but it uses the outdated local var localAccumulator which was copied from self.accumulator's old value.
It was easy to find out the logic error via debugger. But then I reflected on my mistake, and was trying to look for a method to avoid this kind of logic error.
Method 1: use nil checking rather than optional binding
if self.accumulator != nil { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: self.accumulator!) // C
}
Actually, what really matters here is the force unwrapped self.accumulator!, it ensures the value comes from the real source. Using nil checking rather than optional binding can force me to force unwrap on self.accumulator.
But in some Swift style guides(GitHub, RayWenderlich, LinkedIn), force unwrapping is not encouraged. They prefer optional binding.
Method 2: use assertions.
if localAccumulator = self.accumulator { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
assert(localAccumulator == self.accumulator) // D
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}
I insert a assertion to check whether the localAccumulator is still equal to self.accumulator. This works, it will stop running once self.accumulator is modified unexpectedly. But it's so easy to forget to add this assertion line.
Method 3: SwiftLint
To find a way to detect this kind of error, I've skimmed SwiftLint's all rules, and also got a basic understanding of
SourceKitten(one of SwiftLint's dependencies). It seems too complicated to detect this kind of error by SwiftLint, especially when I make this pattern more general.
Some similar cases
Case 1: guard optional binding
func someFunc() {
guard let localAccumulator = self.accumulator { // A
return
}
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}
In this case, it's much more difficult for human to notice the error, because localAccumulator has a broader scope with guard optional binding than if optional binding.
Case 2: value copy caused by function passing parameters
// Assume that this function will be called somewhere else with `self.accumulator` as its argument, like `someFunc(self.accumulator)`
func someFunc(_ localAccumulator) {
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}
In this case, localAccumulator copies from self.accumulator when this function is called, then self.accumulator changes in the line B, the line C expects the self.accumulator's new value, but get its old value from localAccumulator.
In fact, the basic pattern is as below,
var x = oldValue
let y = x
functionChangingX() // assign x newValue
functionExpectingX(y) // expecting y is newValue, but it's oldValue
x ~ self.accumulator
y ~ localAccumulator
functionChangingX ~ performPendingBinaryOperation
functionExpectingX ~ PendingBinaryOperation.init
This error pattern looks like so common that I guess there should be a name for this error pattern.
Anyway, back to my question, how can I avoid this kind of logic error?
This example shows what I understood from your problem:
var name: String? = "Mike"
func doSomething() {
// Check if "name" is set (not nil)
if let personName = name {
addLastNameToGlobalName() // modifies global var "name"
//
// Here, you want to use the updated "name" value. As you mention, at this point,
// "name" could/might/should be different than "personName" (it could
// even be nil).
//
greet(person: ???) // use "name"? (which is optional), "personName"?, or what?
}
}
To me, the general approach is the problem here. The fact that you are:
Checking that a global optional-value is not nil
Calling a function that alters this global optional-value
Wanting to use the updated global optional-value as a non-optional value
A simple change in the approach/design of greeting this person would allow you to avoid the type of "logic error" that you mention.
Example 1:
var name: String? = "Mike"
func doSomething() {
// Check if "name" is set (not nil)
if let personName = name {
greet(person: fullName(for: personName))
}
}
Example 2:
var name: String? = "Mike"
func doSomething() {
// Check if "name" is set (not nil)
if let personName = name {
let fullName = addLastNameToGlobalName() // modifies global var "name" and returns the new value
greet(person: fullName)
}
}

Why are two exclamation points needed in optional type not unwrapped?

I was presenting a controller from a noncontroller class so I grabbed the root view, and got the common error of "not unwrapped" so I put in ? and !'s to try or forcibly unwrap, still said window was not unwrapped, so it auto-fixed it by inserting another.
UIApplication.sharedApplication().delegate?.window!!.rootViewController!.presentViewController(blah blah blah... { () -> Void in
});
Title says it all. My only guess is the window is basically a computed property that gives an optional, of which you must unwrap it?!? (grammar not a typo, just ensuring I end the sentence without an error)
You need two ! because the type is a nested optional (UIWindow??).
Like this:
let nested: Int?? = 3
// the safe way
if let innerValue = nested {
// innerValue is of type Int?
if let unwrapped = innerValue {
// unwrapped is of type Int
}
}

Swift: handling an unexpected nil value, when variable is not optional [duplicate]

This question already has answers here:
Check if property is set in Core Data?
(2 answers)
Closed 7 years ago.
I have a UITableViewController loading its entries from Core Data via a NSFetchedResultsController. Like this:
let historyItem = fetchedResults.objectAtIndexPath(indexPath) as HistoryItem
historyItem has a title property defined like this:
#NSManaged var title: String
So in cellForRowAtIndexPath the code says
cell?.textLabel?.text = historyItem.title
and that should all be fine. title is not an optional and does not need unwrapping.
However, in the past, the stored Core Data has acquired some objects where the value of the title property is nil. They are there stored, waiting to cause errors. If one of these objects is displayed in a cell, I will see a runtime address exception on the above Swift code line, where the address is 0.
For robustness, I need to write code to make sure the stored data delivered to my program does not cause crashes. However, I cannot write
if historyItem.title == nil { } // gives compiler error
because title is not an optional and the compiler will not let me. If I write
let optionalTitle:String? = historyItem.title
I still get a runtime EXC_BAD_ACCESS on that line.
How can I check that title is not erroneously nil?
Thanks!
Since you have nil values, the title property should be optional and you should declare it as optional in your core data model and in your NSManagedObject historyItem class.
When I do this in editor I dont get an error so might be the answer:
let a = ""
if a as String? == nil {
println("Nil")
}
else {
println("Not nil")
}
#NSManaged var title: String should be #NSManaged var title: String? if there is possibility for a nil value. Then, you cannot go wrong with optional binding:
if let historyItemTitle = historyItem.title {
cell?.textLabel?.text = historyItemTitle
} else {
cell?.textLabel?.text = "Title missing"
}

Printing optional variable

I am trying with these lines of code
class Student {
var name: String
var age: Int?
init(name: String) {
self.name = name
}
func description() -> String {
return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
}
}
var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())
Above code produces as follow
Daniel hides his age.
Daniel is Optional(18) years old.
My question is why there is Optional (18) there, how can I remove the optional and just printing
Daniel is 18 years old.
You have to understand what an Optional really is. Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
Inside your description() function you don't print the Int, but instead you print the Optional. If you want to print the Int you have to unwrap the Optional. You can use "optional binding" to unwrap an Optional:
if let a = age {
// a is an Int
}
If you are sure that the Optional holds an object, you can use "forced unwrapping":
let a = age!
Or in your example, since you already have a test for nil in the description function, you can just change it to:
func description() -> String {
return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
To remove it, there are three methods you could employ.
If you are absolutely sure of the type, you can use an exclamation mark to force unwrap it, like this:
// Here is an optional variable:
var age: Int?
// Here is how you would force unwrap it:
var unwrappedAge = age!
If you do force unwrap an optional and it is equal to nil, you may encounter this crash error:
This is not necessarily safe, so here's a method that might prevent crashing in case you are not certain of the type and value:
Methods 2 and three safeguard against this problem.
The Implicitly Unwrapped Optional
if let unwrappedAge = age {
// continue in here
}
Note that the unwrapped type is now Int, rather than Int?.
The guard statement
guard let unwrappedAge = age else {
// continue in here
}
From here, you can go ahead and use the unwrapped variable. Make sure only to force unwrap (with an !), if you are sure of the type of the variable.
Good luck with your project!
For testing/debugging purposes I often want to output optionals as strings without always having to test for nil values, so I created a custom operator.
I improved things even further after reading this answer in another question.
fileprivate protocol _Optional {
func unwrappedString() -> String
}
extension Optional: _Optional {
fileprivate func unwrappedString() -> String {
switch self {
case .some(let wrapped as _Optional): return wrapped.unwrappedString()
case .some(let wrapped): return String(describing: wrapped)
case .none: return String(describing: self)
}
}
}
postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
return x.unwrappedString
}
Obviously the operator (and its attributes) can be tweaked to your liking, or you could make it a function instead. Anyway, this enables you to write simple code like this:
var d: Double? = 12.34
print(d) // Optional(12.34)
print(d~?) // 12.34
d = nil
print(d~?) // nil
Integrating the other guy's protocol idea made it so this even works with nested optionals, which often occur when using optional chaining. For example:
let i: Int??? = 5
print(i) // Optional(Optional(Optional(5)))
print("i: \(i~?)") // i: 5
Update
Simply use me.age ?? "Unknown age!". It works in 3.0.2.
Old Answer
Without force unwrapping (no mach signal/crash if nil) another nice way of doing this would be:
(result["ip"] ?? "unavailable").description.
result["ip"] ?? "unavailable" should have work too, but it doesn't, not in 2.2 at least
Of course, replace "unavailable" with whatever suits you: "nil", "not found" etc
To unwrap optional use age! instead of age. Currently your are printing optional value that could be nil. Thats why it wrapped with Optional.
In swift Optional is something which can be nil in some cases. If you are 100% sure that a variable will have some value always and will not return nil the add ! with the variable to force unwrap it.
In other case if you are not much sure of value then add an if let block or guard to make sure that value exists otherwise it can result in a crash.
For if let block :
if let abc = any_variable {
// do anything you want with 'abc' variable no need to force unwrap now.
}
For guard statement :
guard is a conditional structure to return control if condition is not met.
I prefer to use guard over if let block in many situations as it allows us to return the function if a particular value does not exist.
Like when there is a function where a variable is integral to exist, we can check for it in guard statement and return of it does not exist.
i-e;
guard let abc = any_variable else { return }
We if variable exists the we can use 'abc' in the function outside guard scope.
age is optional type: Optional<Int> so if you compare it to nil it returns false every time if it has a value or if it hasn't. You need to unwrap the optional to get the value.
In your example you don't know is it contains any value so you can use this instead:
if let myAge = age {
// there is a value and it's currently undraped and is stored in a constant
}
else {
// no value
}
I did this to print the value of string (property) from another view controller.
ViewController.swift
var testString:NSString = "I am iOS Developer"
SecondViewController.swift
var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")
Result :
The Value of String is I am iOS Developer
Check out the guard statement:
for student in class {
guard let age = student.age else {
continue
}
// do something with age
}
When having a default value:
print("\(name) is \(age ?? 0) years old")
or when the name is optional:
print("\(name ?? "unknown") is \(age) years old")
I was getting the Optional("String") in my tableview cells.
The first answer is great. And helped me figure it out. Here is what I did, to help the rookies out there like me.
Since I am creating an array in my custom object, I know that it will always have items in the first position, so I can force unwrap it into another variable. Then use that variable to print, or in my case, set to the tableview cell text.
let description = workout.listOfStrings.first!
cell.textLabel?.text = description
Seems so simple now, but took me a while to figure out.
This is not the exact answer to this question, but one reason for this kind of issue.
In my case,
I was not able to remove Optional from a String with "if let" and "guard let".
So use AnyObject instead of Any to remove optional from a string in swift.
Please refer link for the answer.
https://stackoverflow.com/a/51356716/8334818
If you just want to get rid of strings like Optional(xxx) and instead get xxx or nil when you print some values somewhere (like logs), you can add the following extension to your code:
extension Optional {
var orNil: String {
if self == nil {
return "nil"
}
return "\(self!)"
}
}
Then the following code:
var x: Int?
print("x is \(x.orNil)")
x = 10
print("x is \(x.orNil)")
will give you:
x is nil
x is 10
PS. Property naming (orNil) is obviously not the best, but I can't come up with something more clear.
With the following code you can print it or print some default value. That's what XCode generally recommend I think
var someString: String?
print("Some string is \(someString ?? String("Some default"))")
If you are printing some optional which is not directly printable but has a 'to-printable' type method, such as UUID, you can do something like this:
print("value is: \(myOptionalUUID?.uuidString ?? "nil")")
eg
let uuid1 : UUID? = nil
let uuid2 : UUID? = UUID.init()
print("uuid1: \(uuid1?.uuidString ?? "nil")")
print("uuid2: \(uuid2?.uuidString ?? "nil")")
-->
uuid1: nil
uuid2: 0576137D-C6E6-4804-848E-7B4011B40C11