almostalx
almostalx

Reputation: 107

Swift combine not running properly in iOS app

So I'm very new to swift so I might be getting this all wrong.

I'm learning to work with combine so I used the playground to log at an interval like so

var cancellable:AnyCancellable? = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink {
    print($0)
}

PlaygroundPage.current.needsIndefiniteExecution = true

It's very simple and it works like I expect it to, printing a log every second.

Now when I create a new iOS project in Xcode it doesn't seem to work and I can't figure out why. From a blank project, I simply added this in the ContentView

struct ContentView: View {
    func test() {
        var cancellable: AnyCancellable?
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
        .autoconnect()
        .sink {
            print($0)
        }
    }
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
        }
    }
    
    init() {
        test()
    }
}

All of a sudden, it doesn't work and the log won't print. I'm sure it's something dumb that I'm not understanding but I've been looking at this forever now. My real case is with a network call that is not running so I tried simplifying as much as I can and this is as simple as I could get.

Any help appreciated!

Thanks!

Upvotes: 1

Views: 922

Answers (2)

jnpdx
jnpdx

Reputation: 52585

There's a couple of things going on here:

  1. Your cancellable is immediately going out of scope, so it's not being stored. Change it to an @State property on your View instead.

  2. SwiftUI is really picky about what can/can't happen inside init() of Views. Two options to deal with this: a) Move it to onAppear b) Move it to the init() of an @ObservableObject

Working example:

struct ContentView: View {
    @State var cancellable : AnyCancellable?
    
    func test() {
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
        .autoconnect()
        .sink {
            print($0)
        }
    }
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
        }.onAppear {
            test()
        }
    }
}

ObservableObject version:

class ViewModel : ObservableObject {
    private var cancellable : AnyCancellable?
    
    init() {
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
        .autoconnect()
        .sink {
            print($0)
        }
    }
}

struct ContentView: View {
    var vm = ViewModel()
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
        }
    }
}

Upvotes: 2

Steven0351
Steven0351

Reputation: 539

You’re not calling the function. You need to set the cancellable as a property on your view in this scenario.

Upvotes: 0

Related Questions