I'm trying to move the logic of a viewController to a view model but for some reason it always crashes, saying unexpectedly found nil while unwrapping an Optional value. I constantly get this error no matter what code I try to move, so I must be doing something fundamentally wrong. Here's a sample of the code I have in the viewController:
var recipesViewModel: RecipesViewModel! //VIEW MODEL CLASS REFERENCE
var recipeCategory = recipesViewModel.transformToUpperCase(dataRecieverStringRecipeView: "testString")
Then in the view modelclass:
func transformToUpperCase(dataRecieverStringRecipeView: String) -> String {
var recipeCategory = dataRecieverStringRecipeView
var prefixRecipeCategory = recipeCategory.prefix(1).uppercased()
var dropFirstRecipeCategory = recipeCategory.dropFirst()
var upperCasedRecipeCategory = prefixRecipeCategory + dropFirstRecipeCategory
return upperCasedRecipeCategory
}
...it translates the string to have an uppercase letter as its first letter.
The code works perfectly fine when everything is in the view model, but as soon I move it to another class and call the function through an object it crashes. What am I missing?
You need to fix your view model class reference
Line to change:
var recipesViewModel: RecipesViewModel!
Replacement Line:
var recipesViewModel = RecipesViewModel()
This line of code will properly declare/create recipesViewModel as an object of class RecipesViewModel.
Hope that resolves your inquiry!
var recipesViewModel: RecipesViewModel! does not instantiate a new object. Put the category function back in your RecipesViewModel class, then change the variable declaration to:
Declaration
var recipesViewModel: RecipesViewModel!
Later on
recipesViewModel = RecipesViewModel() //or whatever initializer you need.
Edit
As rmaddy pointed out: you probably can ditch the ! and use var recipesViewModel: RecipesViewModel = RecipesViewModel() instead if you don't have any failable initializers or this isn't a runtime injected property and just put it as a 1-liner:
var recipesViewModel: RecipesViewModel = RecipesViewModel()
Related
I am writing my project and wondered.
When I read literature or watch videos, I see that this is bad practice. Why? Is this bad for the system?
What is the difference between this
class SomeClass {
var someView = SomeView()
var someViewModel = SomeViewModel()
// ...
}
and this
class SomeClass {
var someView: SomeView!
var someViewModel: SomeViewModel?
// ...
}
How to get used to it better?
You have to initialize all instance properties somehow. And you have to do it right up front, either in the declaration line or in your init method.
But what if you don't actually have the initial value until later, like in viewDidLoad? Then it is silly to supply a real heavyweight value only to replace it later:
var v = MyView()
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it in place of that
}
Instead, we use an Optional to mark the fact that we have no value yet; until we obtain and assign one, it will be nil:
var v : MyView? // means it is initially `nil`
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it to our property
}
There's nothing wrong with the first way (which is called a "default property value", by the way), and in fact, often times it's preferable. But of course, the devil is in the details:
How would the initialization of a SomeViewModel work? Without acess the initializer parameters of SomeClass, you're stuck with only being able to construct an instance from a parameter-less init, like SomeViewModel(). What exactly could that do? Suppose it was a person view model, and you had PersonViewModel(). What person? Whats their name? What will this default value do at all?
It's not a great pattern if it requires overwriting the default value with some other value in the initializer
It initializes the value up-front, where sometimes a lazy or computed value might be more appropriate.
There has been same titles of this question but different situations.
In this case this is very simple but I can't find a same problem online.
So here's the code
class ViewController: UIViewController {
#IBOutlet weak var fldTotalUnits: UITextField!
var intTotalUnits:Int? = Int(fldTotalUnits)
The error here says "Cannot use instance member 'fldTotalUnits' within property initializer;..."
I tried replacing var with let, I tried NSString, I tried .toInt() but nothign worked... so how do I this?
String to Int conversion is not complicated. You simply do the conversion at the wrong place. You are trying to reference one member in the initialization of another member, that is not allowed. In this particular case simply because fldTotalUnits has the value nil when you would try to use it via Int(fldTotalUnits). When creating an instance of your class ViewController fldTotalUnits is set to nil and initialized with a useful value later. Therefore what you have to do in the first place is move the line into a separate method:
func doSomething() {
var intTotalUnits:Int? = Int(fldTotalUnits)
}
Now you will see that the compiler complains about there not being a suitable initializer because you have to access the text property of fldTotalUnits instead of using the actual textfield:
func doSomething() {
var intTotalUnits:Int? = Int(fldTotalUnits.text!)
}
Now you can think about moving the declaration of intTotalUnits to somewhere else, but setting its value has to happen in some method.
The code in your question is trying to create an Int from a UITextField, not a String. What you should say is something likeā¦
var intTotalUnits:Int?
func updateTotalUnits()
guard let text = fldTotalUnits.text else { return }
intTotalUnits = Int(text)
}
class AppScene: SCNScene {
static var targetNode: SCNLookAtConstraint
override init() {
super.init()
//other code not shown...
}
}
So this is my current AppScene class and I am trying to be able to make this variable public to be able to use it in other classes. Specifically to call a button press in my SKOverlay Scene and make it do something in my SCNScene. However the error shows
'static var' declaration requires an initializer expression or getter/setter specifier
My guess is that I must have a method the return the variable that is called returnTargetNode.
Make this var an optional
static var targetNode: SCNLookAtConstraint?
Or give it a value (a default value/ a value at the init..)
Newbie question. I am simply trying to declare a class (or even struct) as a separate Swift file and then build it or use it inside a separate class. Consider this:
import Foundation
class PayloadTest{
var label: String
init(label:String) {
self.label = label
}
}
---- then separate file
import WatchKit
import Foundation
class InterfaceController2: WKInterfaceController {
var payloadtest = PayloadTest(label: "test string init")
payloadtest.label = "test" // this line gives error - says it was expecting a declaration
.
.
.
}
I can't figure out why if I make a class or struct at the same level in my watchOS extension, it is not allowed to be accessed or recognized when I try to access the variables.
As dfd mentioned in the comment section this is a scope issue. In many programming languages you just can't write statements (expressions) which is not either a declaration or initialization or a method call outside the function or a method.
Let me explain what I said,
In a class or a structure definition any statements(expressions) apart from declaration & initialization should be present in the function (method) definition.
class PayloadTest{
//The below statement is declaration, which declares label is an property of type string.
var label: String
init(label:String) {
//The below statement is an assignment, and it compiles and execute fine as this is inside a init method.
self.label = label
}
}
However in your second snippet,
import WatchKit
import Foundation
class InterfaceController2: WKInterfaceController {
//The below statement compiles fine even tough it isn't present inside a method coz it is initialization statement.
var payloadtest = PayloadTest(label: "test string init")
//However the compiler complains about the below assignment statement because, this is neither an declaration nor an initialization statement and it should not be outside method.
//So you've to keep this statement inside a method for compiler to stop complaining.
payloadtest.label = "test" // this line gives error - says it was expecting a declaration
....
}
To make the second snippet work put the below line of code in a method and call that method,
payloadtest.label = "test"
So always remember any statements apart from declaration, initialization should be present inside a method or function definition and this applies to most of the languages.
Please go through the various scope levels present. HTH :)
You can't have an expression nested in a class like that. You can get around this by putting your code in a closure which you immediately call:
class InterfaceController2: WKInterfaceController {
var payloadtest = {
let pt = PayloadTest(label: "test string init")
pt.label = "test"
return pt
}()
you can try to move that code in a function. (Swift 3.0 version)
class InterfaceController: WKInterfaceController {
var payloadtest = PayloadTest(label: "test string init")
fileprivate func test() {
payloadtest.label = "test" // tape this line in a function
}
}
Try this below code work with optional type -
class PayloadTest {
var label: String?
init(label:String) {
self.label = label
}
}
Could you please tell me why that code (class) is working in playground?
If I understand properly there should be init or something that can be used by "blank initializer"?
class Shape{
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
var shapeA = Shape()
shapeA.simpleDescription()
shapeA.numberOfSides = 7
var shapeDescription = shapeA.simpleDescription()
shapeA.simpleDescription()
Thank you for your help
If all the stored properties are given default values, as here, the class does not need to have an explicit initializer.
In Swift an init override is only required in certain cases.
For instance, if you hadn't put a default value on that numberOfSides var then you would have to have an init to provide the default value.
Or you would have had to make it optional which gives it a nil default value.
This is all explained in the iBook by apple.