Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: nil requires a contextual type

I'd like to achieve the following in code:

class MyService {

    let mySubject = BehaviorSubject<MyData>(value: nil)

    //....

}

Unfortunately, I get the "nil requires a contextual type" error. I want the subject to be "empty" till I actually put something in there. How can I pass nil as the argument then? Can I cast it to my own type to make it work?

like image 384
breakline Avatar asked Jul 22 '18 12:07

breakline


People also ask

What is contextual type in Swift?

Contextual objects characterize an object M together with the typing context Ψ in which it is meaningful. This idea is then also internalized within the type theory itself using the notion of a contextual type which pairs the type A of an object together with the context Ψ in which the object is well-typed.

Can any be nil Swift?

Swift assumes by default that all variables have a value. Unless you tell it otherwise by declaring an optional you can't have a nil value in a place where your program expects there to be a value.


2 Answers

Based on the reference for RxSwift BehaviorSubject, the init(value:) initializer is declared as

public init(value: Element)

Where the value parameter is described as:

value

Initial value sent to observers when no other value has been received by the subject yet.

And where Element is the placeholder type of BehaviorSubject:

public final class BehaviorSubject<Element> ...

This means you need to specify the placeholder type Element as an Optional type if you are to be able to set the initial value (used when no other value has been received) to nil. E.g.:

class MyService {

    let mySubject = BehaviorSubject<MyData?>(value: nil)

    //....
}

Or, letting the compiler infer the placeholder as MyData? by using the non-sugared .none form for the nil argument:

class MyService {

    let mySubject = BehaviorSubject(value: Optional<MyData>.none)

    //....
}

As for understanding the actual error message better, consider the following self-contained example:

struct Foo<T> {
    init(value: T) {}
}

struct Bar {}

let bar = Bar()

_ = Foo<Bar>(value: bar)           // OK
_ = Foo(value: bar)                // OK, T inferred as Bar
_ = Foo<Bar>(value: nil)           // Error: error: 'nil' requires a contextual type
_ = Foo<Bar?>(value: nil)          // OK
_ = Foo(value: Optional<Bar>.none) // OK, T inferred as Bar?
like image 70
dfrib Avatar answered Oct 20 '22 08:10

dfrib


While dfri's answer is technically correct, you might want to consider a different type when working with RxSwift. Since you want your subject to be empty only at the beginning, I'd suggest to use ReplaySubject or PublishSubject.

A similar question has also been asked on RxSwift's GitHub issue page. Allow BehaviorSubject without initial value. There, kzaher suggests the ReplaySubject.

Your subject would then look like this, without any initial value and without MyData being Optional.

let subject = ReplaySubject<MyData>().create(bufferSize: 1)
like image 28
Yannick Avatar answered Oct 20 '22 07:10

Yannick