Swift - Assign newValue to an Object - ios

I know that probably is a very simple question, however I spent a lot of time trying to figure out why is not working, and still doesn't make sense for me, so I need help.
I am learning iOS, I came from Android where I am used to work with objects.
I want to do something similar that I have been doing with Android(maybe this is the problem).
I have created an object with the methods get and set.
private var _token: String!
var error: String {
get {
guard self._error != nil else { return "" }
return _error
}
set {
self._token = newValue
}
}
When I want to manipulate this object, is when I am having the problem.
let objectCreated = ObjectCreated()
guard let errorReceived = (xml["Whatever"]["Whatever"].element?.text) else { return }
print(errorReceived)
objectCreated.error = errorReceived
print(objectCreated.error)
The first print is printing the correct String, but the second is printing "". So the set method is not doing his job.

Isn't it supposed to be
...
guard self._token != nil else { return "" }
return _token
...
This can be simplified with the nil-coalescing operator
var error: String {
get {
return self._token ?? ""
}
set {
self._token = newValue
}
}
Note: Variable names with leading underscores are unusual in Swift.

private var _error: String!
var error: String {
get {
guard self._error != nil else { return "" }
return _error
}
set {
self._error = newValue
}
}
self._token = newValue >>>> self._error = newValue

Related

Role of underscore "_" in conditional statement (if else) in Swift

Can somebody please explain the role of "_" in Swift code below.
var guessWasMade: Bool {
if let _ = game.guesses[currentQuestion] {
return true
} else {
return false
}
}
I understand how it is usually used as unnamed parameter in for-loops and functions.
But could not find any explanation for if else statement.
Tutorial explains it as guessWasMade checks game.guesses for a value. If a value is found we know the user has made a guess at the question.
game.guesses is an array of integers.
This is part of class declaration. Full code looks like this
class GameViewModel: ObservableObject {
// MARK: - Published properties
// 2
#Published private var game = Game()
// MARK: - Internal properties
// 3
var currentQuestion: Question {
game.currentQuestion
}
// 4
var questionProgressText: String {
"\(game.currentQuestionIndex + 1) / \(game.numberOfQuestions)"
}
// 1
var guessWasMade: Bool {
if let _ = game.guesses[currentQuestion] {
return true
} else {
return false
}
}
// MARK: - Internal Methods
// 2
func makeGuess(atIndex index: Int) {
game.makeGuessForCurrentQuestion(atIndex: index)
}
// 3
func displayNextScreen() {
game.updateGameStatus()
}
}
If let statements check to see if the value is nil. If it is, then the statement is false. If it isn't, then the value is assigned to a variable.
var optionalValue: Int? = 42
if let nonOptionalValue = optionalValue {
print(nonOptionalValue) // -> 42
} else {
print("optionalValue is nil")
}
So, when you do if let _ = game.guesses[...], you are checking to see if game.guesses[...] is nil. If it isn't, then you are ignoring the value with a wildcard pattern (_), which will match anything.
Because you are ignoring the value, it is the same as saying
if game.guesses[...] != nil {
return true
} else {
return false
}
Then, because you are simply returning the value of the condition, you can just return the condition itself:
var guessWasMade: Bool {
game.guesses[currentQuestion] != nil
}

How to call a method once two variables have been set

I am using iOS Swift, and I am trying to understand how to execute a method once the value of two variables have been set up (non-null value) once the requests have finished.
After reading some documentation, I have found out some concepts which are interesting. The first one would be didSet, which works as an observer.
I could call the method using this method by simply using didSet if I would require just one variable
didSet
var myVar: String 0 {
didSet {
print("Hello World.")
}
}
Nevertheless, I also need to wait for the second one myVar2, so it would not work.
I have also found DispatchQueue, which I could use to wait a second before calling the method (the requests that I am using are pretty fast)
DispatchQueue
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
print("Hello world")
})
but I consider that this solution is not efficient.
Is there anyway to combine these two variables or requests in order to call a method once they have finishing setting the value?
Update
I have tried to replicate David s answer, which I believe is correct but I get the following error on each \.
Type of expression is ambiguous without more context
I copy here my current code
var propertiesSet: [KeyPath<SearchViewController, Car>:Bool] = [\SearchViewController.firstCar:false, \SearchViewController.secondCar:false] {
didSet {
if propertiesSet.allSatisfy({ $0.value }) {
// Conditions passed, execute your custom logic
print("All Set")
} else {
print("Not yet")
}
}
}
var firstCar: Car? {
didSet {
propertiesSet[\SearchViewController.firstCar] = true
}
}
var secondCar: Car? {
didSet {
propertiesSet[\SearchViewController.secondCar] = true
}
}
The variables are set individually, each one on its own request.
You could make your properties optional and check they both have values set before calling your function.
var varA: String? = nil {
didSet {
if varA != nil && varB != nil {
myFunc()
}
}
}
var varB: String? = nil {
didSet {
if varA != nil && varB != nil {
myFunc()
}
}
}
Or you can call your function on each didSet and use a guard condition at the start of your function to check that both of your properties have values, or bail out:
var varA: String? = nil {
didSet {
myFunc()
}
}
var varB: String? = nil {
didSet {
myFunc()
}
}
func myFunc() {
guard varA != nil && varB != nil else { return }
// your code
}
First, you should think very carefully about what your semantics are here. When you say "set," do you mean "assigned a value" or do you mean "assigned a non-nil value?" (I assume you mean the latter in this case.) You should ask yourself, what should happen if your method has already fired, and then another value is set? What if one of the properties has a value is set, then nil is set, then another value set? Should that fire the method 1, 2, or 3 times?
Whenever possible you should work to make these kinds of issues impossible by requiring that the values be set together, in an init rather than mutable properties, for example.
But obviously there are cases where this is necessary (UI is the most common).
If you're targeting iOS 13+, you should explore Combine for these kinds of problems. As one approach:
class Model: ObservableObject {
#Published var first: String?
#Published var second: String?
#Published var ready = false
private var observers: Set<AnyCancellable> = []
init() {
$first.combineLatest($second)
.map { $0 != nil && $1 != nil }
.assign(to: \.ready, on: self)
.store(in: &observers)
}
}
let model = Model()
var observers: Set<AnyCancellable> = []
model.$ready
.sink { if $0 { print("GO!") } }
.store(in: &observers)
model.first = "ready"
model.second = "set"
// prints "GO!"
Another approach is to separate the incidental state that includes optionals, from the actual object you're constructing, which does not.
// Possible parameters for Thing
struct Parameters {
var first: String?
var second: String?
}
// The thing you're actually constructing that requires all the parameters
struct Thing {
let first: String
let second: String
init?(parameters: Parameters) {
guard let first = parameters.first,
let second = parameters.second
else { return nil }
self.first = first
self.second = second
}
}
class TheUIElement {
// Any time the parameters change, try to make a Thing
var parameters: Parameters = Parameters() {
didSet {
thing = Thing(parameters: parameters)
}
}
// If you can make a Thing, then Go!
var thing: Thing? {
didSet {
if thing != nil { print("GO!") }
}
}
}
let element = TheUIElement()
element.parameters.first = "x"
element.parameters.second = "y"
// Prints "GO!"
You need to add a didSet to all variables that need to be set for your condition to pass. Also create a Dictionary containing KeyPaths to your variables that need to be set and a Bool representing whether they have been set already.
Then you can create a didSet on your Dictionary containing the "set-state" of your required variables and when all of their values are true meaning that all of them have been set, execute your code.
This solution scales well to any number of properties due to the use of a Dictionary rather than manually writing conditions like if aSet && bSet && cSet, which can get out of hand very easily.
class AllSet {
var propertiesSet: [KeyPath<AllSet, String>:Bool] = [\.myVar:false, \.myVar2:false] {
didSet {
if propertiesSet.allSatisfy({ $0.value }) {
// Conditions passed, execute your custom logic
print("All Set")
} else {
print("Not yet")
}
}
}
var myVar: String {
didSet {
propertiesSet[\.myVar] = true
}
}
var myVar2: String {
didSet {
propertiesSet[\.myVar2] = true
}
}
init(myVar: String, myVar2: String) {
self.myVar = myVar
self.myVar2 = myVar2
}
}
let all = AllSet(myVar: "1", myVar2: "2")
all.myVar = "2" // prints "Not yet"
all.myVar2 = "1" // prints "All set"

Best way to compare two any type values in swift

I have a class which has property value of type Any I have implemented a didSet method for the value like this
didSet {
if oldValue == nil && self.value != nil {
// do something
}
else {
if let val = self.value as? AnyHashable, let oldVal = oldValue as? AnyHashable {
if val != oldVal {
// do something
}
}
}
}
My value is optional type so I want to verify the nil case too.
Is there are better way of doing that I do not want to cast to Anyhashasble I want them to be casted to the class they belong.
Well, if you assume that this property is aways, and if only always, a reference type, then you can try to use AnyObject instead of Any and use === operator to check if pointers are pointing to same object.
Something like this:
class Test {
var object: AnyObject? {
didSet {
if oldValue === object {
debugPrint("Old object")
} else {
debugPrint("New object")
}
}
}
}
class SomeClass {}
let test = Test()
let s = SomeClass()
test.object = s
test.object = s
test.object = SomeClass()
The result will be:
"New object" - because previous value was nil
"Old object" - because we assigned same s
"New object" - because we assigned completely new object

Is there any way of sharing getter and setter through properties in Swift?

I'm building a helper to enable typed access to NSUserDefaults properties. Something like this:
struct UserDefaults {
private static var standardUserDefaults: NSUserDefaults = {
return NSUserDefaults.standardUserDefaults()
}()
private static let propKey = "PROP"
static var prop: Bool {
get {
return standardUserDefaults.boolForKey(propKey)
}
set {
standardUserDefaults.setBool(newValue, forKey: propKey)
standardUserDefaults.synchronize()
}
}
}
This way I can have a nice syntax for reading and writing to NSUserDefaults:
UserDefaults.prop // read
UserDefaults.prop = false // write
The problem is that there's a lot of boilerplate code for this, I need 10 lines for each aditional property.
Is there any way of reducing the amount of lines needed for each new property? Reusing getter and setter? Any kind of run time generator?
You can try wrapping the actual value in a class that handles all the dirty work for you:
class WrappedUserDefault<T> {
let key : String
let defaultValue : T
var value : T {
get {
if let value = UserDefaults.standardUserDefaults.objectForKey(key) as? T {
return value
} else {
return defaultValue
}
}
set {
if let value = newValue as? AnyObject {
UserDefaults.standardUserDefaults.setValue(value, forKey: key)
} else {
UserDefaults.standardUserDefaults.removeObjectForKey(key)
}
UserDefaults.standardUserDefaults.synchronize()
}
}
init(key:String, defaultValue:T) {
self.key = key
self.defaultValue = defaultValue
}
}
struct UserDefaults {
static let standardUserDefaults = NSUserDefaults.standardUserDefaults()
static let ready = WrappedUserDefault<Bool>(key:"ready", defaultValue: true)
static let count = WrappedUserDefault<Int>(key: "count", defaultValue: 0)
}
Then with just a little bit more code you wind up with:
UserDefaults.count.value++
UserDefaults.ready.value = true
UserDefaults.ready.value
If the verbosity of ready.value bothers you, you can somewhat hide that, although then you're back to you're back to having a fair amount of copy/paste code:
struct UserDefaults {
static let standardUserDefaults = NSUserDefaults.standardUserDefaults()
private static let readyWrapper = WrappedUserDefault<Bool>(key:"ready", defaultValue: true)
static var ready : Bool {
get { return readyWrapper.value }
set { readyWrapper.value = newValue }
}
}
At least in this case though, the copy/paste code is fairly trivial, so unlikely to need to be altered in the future.
I like David's answer much better, but here's another option. Drops your 10 lines per variable down to 5 (mainly because of new line removal...)
struct UserDefaults {
private static var standardUserDefaults: NSUserDefaults = {
return NSUserDefaults.standardUserDefaults()
}()
//Repeate these 5 lines for all new variables,
//changing the as? to the proper variable type
//Adding in a default value for the return in
//case the as? cast fails for any reason
private static let propKey = "PROP"
static var prop: Bool {
get { return (getVar(propKey) as? Bool) ?? false }
set { setVar(newValue, key:propKey) }
}
//The generic set/get
private static func getVar(key : String) -> AnyObject?
{
return standardUserDefaults.objectForKey(key)
}
private static func setVar(newValue : AnyObject, key : String)
{
if(newValue is Bool)
{
standardUserDefaults.setBool((newValue as? Bool)!, forKey: key)
}
//... More cases here
else if(newValue == nil)
{
standardUserDefaults.removeObjectForKey(key)
}
else
{
standardUserDefaults.setObject(newValue, forKey: key)
}
standardUserDefaults.synchronize()
}
}

Loop through Realm Object fields in Swift

I have a Realm Object
class CoursesModel: Object {
dynamic var courseName = ""
dynamic var par3Field = 0
dynamic var par4Field = 0
dynamic var par5Field = 0
}
When somebody enters the course name I want to check whether it already exists before writing it to Realm.
Can you please tell me what I'm doing wrong because it doesn't seem to loop through.
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
if course == c {
print("course = \(course)")
print("c = \(c)")
return true
}
else {
return false
}
}
}
catch
{
// return nil
}
return false
}
Any help will be greatly appreciated.
EDIT - WORKING CODE HERE
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
let a = course.courseName
print("Model Course = \(a)")
print("Passed Course = \(c)")
if a == c {
return true
}
}
}
catch
{
// return nil
}
return false
}
You are returning in both branches of the loop, which immediately exits out of the function. You do not want to return false on the first failure, but only after all have failed (I think).

Resources