Swift - Can't access struct properties - swift

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;
}

Related

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.

Initializing a variable initialized from IBoutlet variables for a view controller?

I am trying to initialize the variable deckEditorModel using termAndDefTable and copyNPasteTable as inputs. I don't want deckEditorModel to be an optional variable as a know that I would not be able to use that tactic in C. I tried putting deckEditorModel in the init methods for the view controller but it seems that the required termNDefTable: termAndDefTable, otherTable: OtherTable are not available to use as imputs during the init as they are IBOutlets. How would someone go about this
import Cocoa
class DeckEditorViewController: NSViewController {
#IBOutlet weak var importToolsView: NSView!
#IBOutlet weak var termAndDefTable: NSTableView!
#IBOutlet weak var otherTable: NSTableView!
var deckEditorModel:DeckEditorModel?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
self.view.wantsLayer = true
}
override func awakeFromNib() {
deckEditorModel = DeckEditorModel(termNDefTable: termAndDefTable, otherTable: otherTable)
}
I tried adding it to the viewDidLoad but apparently that is to late to define deckEditorModel. The below error dissappears when deckEditorModel is an optional
You could initialize the variable lazily
lazy var deckEditorModel : DeckEditorModel = {
return DeckEditorModel(termNDefTable: self.termAndDefTable, otherTable: self.otherTable)
}()
Place
deckEditorModel = DeckEditorModel(termNDefTable: termAndDefTable, otherTable: otherTable)
In viewDidLoad, then the outlets will have been set.

Reference non-initiated variable at top of file

this is a bit odd to say but essentially here is my code below:
import UIKit
class BarcodeScanPopover: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var navbar: UINavigationItem!
#IBOutlet weak var product: UILabel!
#IBOutlet weak var productimage: UIImageView!
#IBOutlet weak var scanner: UIView!
var scan: MTBBarcodeScanner = MTBBarcodeScanner(previewView: scanner);
override func viewDidLoad() {
So the issue I'm having is I can't declare "scan" without initiating the MTBBarcodeScanner object, but at the same time, I can't initiate the MTBBarcodeScanner object without calling "scanner" which is not possible at the top of the file. Unfortunately MTBBarcodeScanner() is not a valid init and causes crashes so that is not possible either.
I need to do this because I need to access "scan" at different points in the code - not just in one code method.
Any suggestions?
If you are sure you will always have an instance of the MTBBarcodeScanner after the view loaded, declare it as MTBBarcodeScanner!: var scan: MTBBarcodeScanner!. That makes it an implicitly unwrapped optional, which is allowed to be nil unless you try to access some property/function on it.
You should therefore then make sure that you always assign something to it before ever accessing it in any other way. That can and should be done in viewDidLoad:
scan = MTBBarcodeScanner(previewView: scanner)
You can let your MTBBarCodeScanner instance be an optional, and initially set it to nil.
var scan: MTBBarcodeScanner? = nil
Thereafter call your initializer to update its value as soon as scanner instance is available to you (e.g. in viewDidLoad).

Nil value when referring to IBOutlets - swift, Cocoa

I am having an issue where my NSTextField IBOutlets are showing up as nil, even though they are connected to the storyboard. In the simplified example below, I have a button that, when pressed, should list the string value of the 3 text labels.
Here is the code for the button:
import Foundation
import Cocoa
class ViewController: NSObject{
#IBAction func pushButton(sender: AnyObject) {
let oneText = Texts()
oneText.listTextFields()
}
}
Here is the code for the NSTextField list:
import Foundation
import Cocoa
class Texts: NSObject{
#IBOutlet weak var l1: NSTextField!
#IBOutlet weak var l2: NSTextField!
#IBOutlet weak var l3: NSTextField!
var textArray = [NSTextField]()
func listTextFields (){
self.textArray = [self.l1,self.l2,self.l3]
for var i = 0; i < textArray.count; i++ {
let text = textArray[i]
print(text.stringValue)
}
}
}
I have verified that the IBOutlets are all connected, but I get a "fatal error: unexpectedly found nil while unwrapping an Optional value" message when I run the program and press the button. Looking at the debugger, it appears that the tree NSTextfields are "nil."
What am I doing wrong?
You're not loading Texts from your storyboard, so it knows nothing about your outlets. Texts() creates a new instance of the object, which you then call the method on.
You presumably have an existing Texts object somewhere in interface builder, ViewController should have an outlet to that.