Swift: Unable to access weak ref to func() - swift

I'm getting the following error:
~.swift:89:50: 'TreeContainerView?' does not have a member named
'updateValues'
..even though 'updateValues' does exist as shown below.
class TreeContainerView:UIView {
override func awakeFromNib() {
weak var weakSelf:TreeContainerView? = self
let delayInSeconds:Double = 0.25
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
...
dispatch_source_set_event_handler(timer, weakSelf.updateValues);
dispatch_resume(timer);
}
func updateValues() {
}
...
}
However it does compile if I replace 'weakSelf.updateValues' with 'self.updateValues'.
Question:
1) Is the following the correct Swift version (the ObjC uses a block)?
dispatch_source_set_event_handler(timer, weakSelf.updateValues);
2) How do I implement a weak reference in Swift (per this context)?

The issue is that weakSelf is an optional TreeContainerView. In order to access its methods you have unwrap weak self to access the actual TreeContainerView if it exists like this:
dispatch_source_set_event_handler(timer, weakSelf!.updateValues);

Related

Swift - Can't access struct properties

Learning swift but a little confused as to the below. I have struct created and instantiated, but I cannot access the property of the struct inside of my view controller class unless the struct is inside one my my class methods. See below, why would this be the case?
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
//create new weather manager struct
var weatherManager = WeatherManager()
//can't access property here, but I can access it inside of functions within this
//class, see below under viewDidLoad()
weatherManager.delegate = self
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//can access property here
weatherManager.delegate = self
}
The problem isn't where the WeatherManager object is declared and created. The problem is that this line:
weatherManager.delegate = self
is a command (technically a statement), not a declaration. (The line above it is a declaration, one that happens to also set the weatherManager property's default value). This is a pretty universal rule in most languages in the C++/Java family -- see short C++ example below. A command (statement) must be inside some method (or function, in non-OOP programming), not at the top level of a file or class. In Swift, actions like setting an object's delegate would typically go in the view controller's viewDidLoad.
int x = 0; // legal: declaring a global variable
x = x + 42; // NOT legal: this is a statement, not a declaraiton
int main()
{
x = x + 42; // legal: now we're inside a function
return 0;
}

Why array's append method cannot be used in viewController?

I am beginner of swift. I tried to use array's append method in my code but it doesn't work. How should I implement the array correctly?
The error messages:
Swift Compiler Error Group
ViewController.swift:16:5: Expected declaration
ViewController.swift:11:7: In declaration of 'ViewController'
I tried to use array's append method in my code but it doesn't work.
import UIKit
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
dices.append("Hi") //Error: Expected declaration
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
I expect I can add "hi" into the array dices.
You should call the append inside a function after the vc is fully initated
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
dices.append("Hi") // e.x here
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
Or replace
var dices : [String] = []
with
var dices = ["Hi"]
SH_Khan is right. I'll explain why though.
When defining a class, the first level of indentation is only for its methods and properties, aka func, var, and let. (You can also define other classes/structs/enums in there too)
Calling those functions or system functions like Array.append() or print("dog sweat") must happen inside of another function. The reason why is that your application's live logic is literally just functions all the way down. No function gets called unless it's inside of another function first. (The only exceptions are Swift's quick and dirty initializations like setting a default value to a var outside of an init() { } or another function.)
A dog doesn't wake up from its nap unless you make some noise. It won't do it on its own. (crappy metaphor, but yeah)
I hope that made any sense.

initialization of immutable value 'rate' was never used

I am brand new to Swift and Xcode and I am trying to build a financial calculator for Mac and I have encountered two issues.
The first issue is that in my code, it says that I initialized a value, but never used it. I believe that I have actually done that, but I keep getting the error. Here is my entire code for the class I'm working on:
import Cocoa
class PresentValueController: NSViewController {
#IBOutlet weak var answer_label: NSTextField!
#IBOutlet weak var payment_field: NSTextField!
#IBOutlet weak var rate_field: NSTextField!
#IBOutlet weak var periods_field: NSTextField!
#IBOutlet weak var compounding_popup: NSPopUpButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
#IBAction func popupValueChanged(_ sender: NSPopUpButton) {
let rate = (rate_field.integerValue)/sender.selectedTag()
return
}
#IBAction func calculate_button(_ sender: Any) {
let payment = (payment_field.integerValue)
let present_value = (payment / (1 + rate)^12)
answer_label.stringValue = "$\(present_value)"
}
}
As you can see, I have defined rate and tried to use it, but I keep getting the error.
My next question has to do with aesthetics. This is what I want my output to look like, but this is what ends up happening when I comment out the errors and run the code. Could I please get some help?
Thanks!!!
The problem is here
let rate = (rate_field.integerValue)/sender.selectedTag()
you don't use , you may think it's visible inside the other method but it's not , also remove the return
//
#IBAction func calculate_button(_ sender: Any) {
let rate = (rate_field.integerValue)/ popButton.selectedTag()
let payment = (payment_field.integerValue)
let present_value = (payment / (1 + rate)^12)
answer_label.stringValue = "$\(present_value)"
}
and declare
#IBOutlet weak var popButton: NSPopUpButton!
if it's not already
#IBOutlet weak var compounding_popup: NSPopUpButton!
Your rate variable is local to popupValueChanged(_:), and your reference to rate in calculate_button(_:) is a reference to an undefined variable.
You could save rate as an instance variable of your PresentValueController objects, but I would advise against it, because you would be duplicating data, and it's easy for it to go out of sync as the complexity of your app grows.
Instead, I recommend you just compute the rate within your calculate_button(_:) function, and drop the popupValueChanged(_:) function completely:
class PresentValueController: NSViewController {
#IBOutlet weak var answerLabel: NSTextField!
#IBOutlet weak var paymentField: NSTextField!
#IBOutlet weak var rateField: NSTextField!
#IBOutlet weak var periodsField: NSTextField!
#IBOutlet weak var compoundingPopup: NSPopUpButton!
#IBAction func calculateButton(_ sender: NSButton) {
let rate = rateField.integerValue / sender.selectedTag()
let payment = paymentField.doubleValue
let presentValue = payment / pow(1 + Double(rate), 12)
let formatter: NumberFormatter = {
let f = NumberFormatter()
f.locale = NSLocale.current
f.numberStyle = .currency
return f
}()
answerLabel.stringValue = formatter.string(from: NSNumber(value: presentValue))!
}
}
You need to understand the concepts of scope and local variables.
A variable (or a let constant) you define inside a function only exists inside the function. (For the rest of this post I'm going to use the term "variable", "immutable variable" and "let constant" interchangeably, even though that's a bit sloppy.)
Your function popupValueChanged() creates a local variable rate that only exists inside the function. As soon as you exit the function, the local variables you define inside the function go out of scope and cease to exist.
Based on the code you posted I would expect your function calculate_button() to have another error about an undefined expression rate.
Consider this code:
class AClass {
let rate = 3
func foo() {
let rate = 6
print("inside function, rate =", rate)
print("inside function, self.rate = ", self.rate)
}
}
let anObject = AClass()
anObject.foo()
print("outside function, anObject.rate = ", anObject.rate)
print("rate =", rate) //This will give an error.
(pretend it's entered into a playground)
The code will do the following:
Create an instance of AClass. The initialization of the AClass object will create an immutable instance variable rate with a value of 3.
Then we call the AClass object's foo() method, which defines a different, local variable, also named rate, and assigns the value 6 to that local variable.
The output of that code will be
inside function, rate = 6
inside function, anObject.rate = 3
outside function, anObject.rate = 3
Inside the function, the local variable rate hides the instance variable rate, so referring to rate inside the function refers to the local immutable variable. Since there are 2 different variables named rate defined at different scopes, we have to use self.rate to refer to the instance variable rate inside the function.
Outside the foo() function the function's local variable rate no longer exists so rate refers to the instance variable rate, who's value has always been 3.
The line:
print("rate =", rate) //This will give an error.
Will give a compile error since outside the function there is no variable at that scope with the name rate.

Swift 2.0 : infer closure type error

I get error
Unable to infer closure type in the current context
In code which was working in Swift 1.2
private lazy var _messagesVC = { return MessagesViewController(nibName:"MessagesViewController",bundle:nil)}()
Whole View Controller where I get this error
import UIKit
class FriendsViewController: UIViewController {
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var segmentContainerView: UIView!
private lazy var _connectionVC = { return FriendsConnectionViewController(nibName:"FriendsConnectionViewController",bundle:nil)}()
private lazy var _messagesVC = { return MessagesViewController(nibName:"MessagesViewController",bundle:nil)}()
override func viewDidLoad() {
super.viewDidLoad()
self.selectedControllerFrom(index: 0)
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
func selectedControllerFrom(index index:UInt)
{
var vc:UIViewController?
switch index{
case 0: vc = _connectionVC
case 1: vc = _messagesVC
default : vc = nil
}
if vc != nil{
self.showViewController(vc!,containerView: containerView);
}
}
I found two ways to get rid of this error.
First, explicitly annotate the property with its type. I find this very strange because Swift is supposed to just infer this from the initialization.
lazy var embeddedViewController: CustomViewController = CustomViewController()
The second is just to remove the lazy keyword.
var embeddedViewController = CustomViewController()
So I guess this is an error currently plaguing lazy properties in Swift 2.0?

Swift weak self function retention

If I have a closure that references a weak var weakSelf = self, can I change that closure to a direct function reference, through weakSelf?
struct ClosureHolder {
let closure: () -> Void
}
class ClosureSource {
func hello() {
NSLog("hello")
}
func createWeakSelfWithinInnerClosureClosureHolder() -> ClosureHolder {
weak var weakSelf = self
return ClosureHolder(closure: {
weakSelf?.hello()
})
}
func createWeakSelfDotHelloClosureHolder() -> ClosureHolder {
weak var weakSelf = self
// The code below won't compile because weakSelf is an Optional.
// Once I unwrap the optional, I no longer have a weak reference.
// return ClosureHolder(closure: weakSelf.hello)
// this strongifies the weak reference. :(
return ClosureHolder(closure: weakSelf!.hello)
}
}
Instead of createWeakSelfWithinInnerClosureClosureHolder, I'd prefer something like createWeakSelfDotHelloClosureHolder.
No you can't. Saying self.foo (if foo is a method) is exactly the same thing as to saying MyClass.foo(self). And methods curried in this fashion always keep a strong reference to the receiver object. If you want to maintain a weak reference, then you need to stick with the { weakSelf?.hello() } approach.