Swift: Why do I need to unwrap non optional variable in declaration - swift

I can't understand why I need to "force unwrap" variable type in it's declaration in my tests.
Let me give you an example to be more clear:
class testSomething: XCTestCase {
var mockService: MockService!
override func setUp() {
mockService = MockService()
}
...
So the goal obviously is to create a fresh instance of the mock service every time I run the test. I just don't understand why I need to declare this variable as MockService! type. What does the exclamation point after type really mean in this context?
Just to be clear, when I declare mockService: MockService Xcode complains that my test class does not have initializers

A non-optional variable must be initialized in the declaration line
var mockService = MockService()
or in an init() method
var mockService : MockService
init() {
mockService = MockService()
}
If this is not possible declare the variable as forced unwrapped and make sure that the variable is not nil whenever it's used. Then it behaves like a non-optional.

Related

Is it possible to initialize properties at the beginning of a class?

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.

How to call a variable from another class specifically a scnscene to an skoverlay scene

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..)

Understanding optional global variables in swift

I'm working through a book on Swift and I understand the idea of scope in functions so what I'd like to understand next is why we set global variables using optional types in classes. Honestly it looks like we don't set these variables per say but just let the class know that there will be a variable of a specific type somewhere throughout the code base: var sut: ItemManager!.
From what I understand the variable sut is an unwrapped optional of the type ItemManger which definitely has a valid value and not nil. The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class. What isn't clear is since this class doesn't have an initializer, what factors come into play when deciding weather to set this global variable to an optional using a questions mark or implicitly unwrapped with an exclamation mark?
import XCTest
#testable import ToDo
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.doneCount, 0)
}
}
sut is not a global variable.
sut is an instance variable of the ItemManagerTests. Every time a new instance, i.e. an object, of a ItemManagerTests is instantiated, memory is allocated for sut.
It's of type ItemManager!. This is an implicitly unwrapped optional. It's similar to ItemManager? (a.k.a. Optional), with the distinction that it is implicitly forcefully unwrapped everywhere it's used directly, as if the force unwrap operator (!) was used.
Its value is initialized to nil, but is set to a new ItemManager object when setUp is called by Xcode's testing framework.
Firstly, sut is not a global. A global variable in Swift is declared outside any enclosing scope (So, before the class statement). sut is an instance property; each instance of your ItemManagerTests class will have a sut property.
In Swift, all non-optional properties must be initialised by the time the initialiser has completed. In some cases this can be achieved by code inside the initialiser and in other cases by assigning a default value.
In some cases, s you cannot assign or don't want to assign a default value and you can't (or don't want to) override the initialiser.
If you used a normal optional then the compiler would be satisfied that the property wasn't initialised by default or in the initialiser, but each time you referred to the property you would have to unwrap it (e.g. sut?.donecount).
Since your test case is assigning a value to sut in setup, you know that it will have a value and you could use a force unwrap (e.g. sut!.donecount), or, take it one step further and use an implicitly unwrapped optional. This allows you to refer to the property without any unwrapping, but it is still an optional and will still cause a crash if it is nil.
Note that your current code isn't even using the property, since you are allocating a local variable in your test cases. You can use:
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero() {
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero() {
XCTAssertEqual(sut.doneCount, 0)
}
}
The reason we've set it with an exclamation mark or question mark is
because there isn't an initializer within this class
Because when every instance of a class is initialized, it has to allocate memory for its properties/instance var. So with var sut: ItemManager!, the value of sut is nil when init. Without !, ? the compiler can't allocate, init, so you have to init manually in an initializer. That is what compiler told you.
For using ! or ?.
! is used when the property/var always has value after the first assigning. And after the first time it's assigned, it can't be nil later
? is used when the pro/var can have value or not

The strange behaviour of Swift's AnyObject

In messing around with Swift today I came across a strange thing. Here's the unit test I developed which shows some unexpected behaviours when using Swift's AnyObject.
class SwiftLanguageTests: XCTestCase {
class TestClass {
var name:String?
var xyz:String?
}
func testAccessingPropertiesOfAnyObjectInstancesReturnsNils() {
let instance = TestClass()
instance.xyz = "xyz"
instance.name = "name"
let typeAnyObject = instance as AnyObject
// Correct: Won't compile because 'xyz' is an unknown property in any class.
XCTAssertEqual("xyz", typeAnyObject.xyz)
// Unexpected: Will compile because 'name' is a property of NSException
// Strange: But returns a nil when accessed.
XCTAssertEqual("name", typeAnyObject.name)
}
}
This code is a simplification of some other code where there is a Swift function that can only return a AnyObject.
As expected, after creating an instance of TestClass, casting it to AnyObject and setting another variable, accessing the property xyz won't compile because AnyObject does not have such a property.
But surprisingly a property called name is accepted by the compiler because there is a property by that name on NSException. It appears that Swift is quite happy to accept any property name as long as it exists somewhere in the runtime.
The next unexpected behaviour and the thing that got all this started is that attempting to access the name property returns a nil. Watching the various variables in the debugger, I can see that typeAnyObject is pointing at the original TestClass instance and it's name property has a value of "name".
Swift doesn't throw an error when accessing typeAnyObject.name so I would expect it to find and return "name". But instead I get nil.
I would be interested if anyone can shed some light on what is going on here?
My main concern is that I would expect Swift to either throw an error when accessing a property that does not exist on AnyObject, or find and return the correct value. Currently neither is happening.
Similar as in Objective-C, where you can send arbitrary messages to id,
arbitrary properties and methods can be called on an instance of AnyObject
in Swift. The details are different however, and it is documented in
Interacting with Objective-C APIs
in the "Using Swift with Cocoa and Objective-C" book.
Swift includes an AnyObject type that represents some kind of object. This is similar to Objective-C’s id type. Swift imports id as AnyObject, which allows you to write type-safe Swift code while maintaining the flexibility of an untyped object.
...
You can call any Objective-C method and access any property on an AnyObject value without casting to a more specific class type. This includes Objective-C compatible methods and properties marked with the #objc attribute.
...
When you call a method on a value of AnyObject type, that method call behaves like an implicitly unwrapped optional. You can use the same optional chaining syntax you would use for optional methods in protocols to optionally invoke a method on AnyObject.
Here is an example:
func tryToGetTimeInterval(obj : AnyObject) {
let ti = obj.timeIntervalSinceReferenceDate // NSTimeInterval!
if let theTi = ti {
print(theTi)
} else {
print("does not respond to `timeIntervalSinceReferenceDate`")
}
}
tryToGetTimeInterval(NSDate(timeIntervalSinceReferenceDate: 1234))
// 1234.0
tryToGetTimeInterval(NSString(string: "abc"))
// does not respond to `timeIntervalSinceReferenceDate`
obj.timeIntervalSinceReferenceDate is an implicitly unwrapped optional
and nil if the object does not have that property.
Here an example for checking and calling a method:
func tryToGetFirstCharacter(obj : AnyObject) {
let fc = obj.characterAtIndex // ((Int) -> unichar)!
if let theFc = fc {
print(theFc(0))
} else {
print("does not respond to `characterAtIndex`")
}
}
tryToGetFirstCharacter(NSDate(timeIntervalSinceReferenceDate: 1234))
// does not respond to `characterAtIndex`
tryToGetFirstCharacter(NSString(string: "abc"))
// 97
obj.characterAtIndex is an implicitly unwrapped optional closure. That code
can be simplified using optional chaining:
func tryToGetFirstCharacter(obj : AnyObject) {
if let c = obj.characterAtIndex?(0) {
print(c)
} else {
print("does not respond to `characterAtIndex`")
}
}
In your case, TestClass does not have any #objc properties.
let xyz = typeAnyObject.xyz // error: value of type 'AnyObject' has no member 'xyz'
does not compile because the xyz property is unknown to the compiler.
let name = typeAnyObject.name // String!
does compile because – as you noticed – NSException has a name property.
The value however is nil because TestClass does not have an
Objective-C compatible name method. As above, you should use optional
binding to safely unwrap the value (or test against nil).
If your class is derived from NSObject
class TestClass : NSObject {
var name : String?
var xyz : String?
}
then
let xyz = typeAnyObject.xyz // String?!
does compile. (Alternatively, mark the class or the properties with #objc.)
But now
let name = typeAnyObject.name // error: Ambigous use of `name`
does not compile anymore. The reason is that both TestClass and NSException
have a name property, but with different types (String? vs String),
so the type of that expression is ambiguous. This ambiguity can only be
resolved by (optionally) casting the AnyObject back to TestClass:
if let name = (typeAnyObject as? TestClass)?.name {
print(name)
}
Conclusion:
You can call any method/property on an instance of AnyObject if that
method/property is Objective-C compatible.
You have to test the implicitly unwrapped optional against nil or
use optional binding to check that the instance actually has that
method/property.
Ambiguities arise if more than one class has (Objective-C) compatible
methods with the same name but different types.
In particular because of the last point, I would try to avoid this
mechanism if possible, and optionally cast to a known class instead
(as in the last example).
it has nothing with NSException!
from Apple documentation:
protocol AnyObject { ... }
The protocol to which all classes implicitly conform.
When used as a concrete type, all known #objc methods and properties are available, as implicitly-unwrapped-optional methods and properties respectively, on each instance of AnyObject
name is #objc property, xyz is not.
try this :-)
let typeAnyObject = instance as Any
or
#objc class TestClass: NSObject {
var name:String?
var xyz:String? }
let instance = TestClass() instance.xyz = "xyz" instance.name = "name"
let typeAnyObject = instance as AnyObject
typeAnyObject.name // will not compile now

Swift optional inout parameters and nil

Is it possible to have an Optional inout parameter to a function in Swift? I am trying to do this:
func testFunc( inout optionalParam: MyClass? ) {
if optionalParam {
...
}
}
...but when I try to call it and pass nil, it is giving me a strange compile error:
Type 'inout MyClass?' does not conform to protocol 'NilLiteralConvertible'
I don't see why my class should have to conform to some special protocol when it's already declared as an optional.
It won't compile because the function expecting a reference but you passed nil. The problem have nothing to do with optional.
By declaring parameter with inout means that you will assign some value to it inside the function body. How can it assign value to nil?
You need to call it like
var a : MyClass? = nil
testFunc(&a) // value of a can be changed inside the function
If you know C++, this is C++ version of your code without optional
struct MyClass {};
void testFunc(MyClass &p) {}
int main () { testFunc(nullptr); }
and you have this error message
main.cpp:6:6: note: candidate function not viable: no known conversion from 'nullptr_t' to 'MyClass &' for 1st argument
which is kind of equivalent to the on you got (but easier to understand)
Actually what #devios1 needs is "optional pointer".
But inout MyClass? means "pointer to an optional".
The following should work in Swift 4
class MyClass {
// func foo() {}
}
func testFunc(_ optionalParam: UnsafeMutablePointer<MyClass>? ) {
if let optionalParam = optionalParam {
// optionalParam.pointee.foo()
// optionalParam.pointee = MyClass()
}
}
testFunc(nil)
var myClass = MyClass()
testFunc(&myClass)
This is possible, and it may have everything to do with Closure.()
The optional can have a nil value, but it is not expecting nil. So the optional would work if you simply did not pass it to the function or passed a variable of type MyClass? with value nil. Like Bryan states.
When you write the function it has to have a default value to be an optional param like so:
func testFunc(inout optionalParam:MyClass?={var nilRef:MyClass?;return &nilRef}()) {
if optionalParam != nil {
...
}
}
Notice if optionalParam {...} is changed to if optionalParam != nil {...}
you can NOT uwwrap optionalParam without checking it,i.e.if optionalParam? != nil, as uwrapping, optionalParam? fails when optionalParam==nil. ~note, var a :MyClass? has a value of nil until it is assigned.
Call is either, testFunc(optionalParam:&a) or testFunc(), but never testFunc(nil)
Think you may be getting two separate concepts intertwined as they have similar names, optional parameters and optionals. Optionals are variables with an extended nil condition. Optional parameters are function parameters which are optional.
Further reading here. Apple is trying to change the verbiage from optional parameter to 'parameters with default values'. It is unfortunate they didn't incorporate optional parameter behavior within these new optional thingies. What is the point of passing nil optionals as optionals if they will fail when unwrapped. Maybe it gets deeper at the heart of what this optional thing is if it can be passed before unwrapping... Schrodinger's cat
The exact passage from the docs is:
You can only pass a variable as the argument for an in-out parameter.
You cannot pass a constant or a literal value as the argument, because
constants and literals cannot be modified. You place an ampersand (&)
directly before a variable’s name when you pass it as an argument to
an inout parameter, to indicate that it can be modified by the
function.
Note however, that (edited) per #gnasher's comment, you only need to pass a class as inout (aka by reference), if you intend or want to allow for the instance to be / being replaced by another instance, and not just have the original modified. The passage in the documentation that covers this is:
In-Out Parameters
Variable parameters, as described above, can only be changed within
the function itself. If you want a function to modify a parameter’s
value, and you want those changes to persist after the function call
has ended, define that parameter as an in-out parameter instead.
Here are three tests that cover the usage of var and inout.
class Bar : Printable {
var value = 1
init(_ value:Int) { self.value = value }
var description:String { return "Bar is: \(value)" }
}
let bar = Bar(1)
func changeBarWithoutInoutSinceBarIsAClassYaySwift(b:Bar) { b.value = 2 }
changeBarWithoutInoutSinceBarIsAClassYaySwift(bar)
println("after: \(bar)") // 2
var bar2 = Bar(0)
func tryToReplaceLocalBarWithoutInout(var b:Bar) { b = Bar(99) }
tryToReplaceLocalBarWithoutInout(bar2)
println("after: \(bar2)") // 0
var bar3 = Bar(0)
func replaceClassInstanceViaInout(inout b:Bar) { b = Bar(99) }
replaceClassInstanceViaInout(&bar3)
println("after: \(bar3)") // 99