getting unresolved identifier 'self' in SwiftUI code trying to use Timer.scheduledTimer
It isn't clear to me that you need a timer for your example, but since there is a great deal of misinformation out there about how to include a Timer in a SwiftUI app, I'll demonstrate.
The key is to put the timer elsewhere and publish each time it fires. We can easily do this by adding a class that holds the timer as a bindable object to our environment (note that you will need to import Combine):
class TimerHolder : BindableObject {
var timer : Timer!
let didChange = PassthroughSubject<TimerHolder,Never>()
var count = 0 {
didSet {
self.didChange.send(self)
}
}
func start() {
self.timer?.invalidate()
self.count = 0
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
[weak self] _ in
guard let self = self else { return }
self.count += 1
}
}
}
We will need to pass an instance of this class down through the environment, so we modify the scene delegate:
window.rootViewController =
UIHostingController(rootView: ContentView())
becomes
window.rootViewController =
UIHostingController(rootView: ContentView()
.environmentObject(TimerHolder()))
Finally, lets put up some UI that starts the timer and displays the count, to prove that it is working:
struct ContentView : View {
@EnvironmentObject var timerHolder : TimerHolder
var body: some View {
VStack {
Button("Start Timer") { self.timerHolder.start() }
Text(String(self.timerHolder.count))
}
}
}
EDIT Update for those who have not been following the plot: BindableObject has migrated into ObservableObject, and there is no need any more for manual signalling. So:
class TimerHolder : ObservableObject {
var timer : Timer!
@Published var count = 0
// ... and the rest is as before ...
Assigning an ObservedState’s published value to a state
If you want to update local state on view model published property (for whatever reason), here is a way to do this
Text(time) // << not binding, just property
.onReceive(timerWrapper.$remainingSeconds) { value in // listen for publisher $
self.time = value
}
SwiftUI - Optional Timer, reset and recreate
There is no need to throw away the TimerPublisher
itself. Timer.publish
creates a Timer.TimerPublisher
instance, which like all other publishers, only starts emitting values when you create a subscription to it - and it stops emitting as soon as the subscription is closed.
So instead of recreating the TimerPublisher
, you just need to recreate the subscription to it - when the need arises.
So assign the Timer.publish
on declaration, but don't autoconnect()
it. Whenever you want to start the timer, call connect
on it and save the Cancellable
in an instance property. Then whenever you want to stop the timer, call cancel
on the Cancellable
and set it to nil
.
You can find below a fully working view with a preview that starts the timer after 5 seconds, updates the view every second and stops streaming after 30 seconds.
This can be improved further by storing the publisher and the subscription on a view model and just injecting that into the view.
struct TimerView: View {
@State private var text: String = "Not started"
private var timerSubscription: Cancellable?
private let timer = Timer.publish(every: 1, on: .main, in: .common)
var body: some View {
Text(text)
.onReceive(timer) {
self.text = "The time is now \($0)"
}
}
mutating func startTimer() {
timerSubscription = timer.connect()
}
mutating func stopTimer() {
timerSubscription?.cancel()
timerSubscription = nil
}
}
struct TimerView_Previews: PreviewProvider {
static var previews: some View {
var timerView = TimerView()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
timerView.startTimer()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
timerView.stopTimer()
}
return timerView
}
}
With a view model, you don't even need to expose a TimerPublisher
(or any Publisher
) to the view, but can simply update an @Published
property and display that in the body
of your view. This enables you to declare timer
as autoconnect
, which means you don't manually need to call cancel
on it, you can simply nil
out the subscription reference to stop the timer.
class TimerViewModel: ObservableObject {
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
private var timerSubscription: Cancellable?
@Published var time: Date = Date()
func startTimer() {
timerSubscription = timer.assign(to: \.time, on: self)
}
func stopTimer() {
timerSubscription = nil
}
}
struct TimerView: View {
@ObservedObject var viewModel: TimerViewModel
var body: some View {
Text(viewModel.time.description)
}
}
struct TimerView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = TimerViewModel()
let timerView = TimerView(viewModel: viewModel)
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
viewModel.startTimer()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
viewModel.stopTimer()
}
return timerView
}
}
How to update text using timer in SwiftUI
i have managed to update text using alert.
i have declared date as State
so whenever date is changed using alert text will also get updated.
struct CurrentDateView : View {
@State var newDate = Date()
let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
var body: some View {
Text("\(newDate)")
.onReceive(timer) {
self.newDate = Date()
}
}
}
How can I make a countdown with NSTimer?
Question 1:
@IBOutlet var countDownLabel: UILabel!
var count = 10
override func viewDidLoad() {
super.viewDidLoad()
var timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)
}
func update() {
if(count > 0) {
countDownLabel.text = String(count--)
}
}
Question 2:
You can do both. SpriteKit is the SDK you use for scene, motion, etc. Simple View Application is the project template. They should not conflict
Retrieve value from function with swift closure
If I'm understanding the structure/question correctly, your goal is to access the WholeDocument
array in another SwiftUI view. The way to do that could be:
class UserData : ObservableObject {
// Use Firebase library to configure APIs
private var db = Firestore.firestore()
@Published var WholeDocument : Array<String> = []
init() {
db.collection("Users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
completion([""])
} else {
for document in querySnapshot!.documents {
// print("\(document.documentID) => \(document.data())")
self.WholeDocument.append(document.documentID)
}
}
}
}
}
struct MyContentView : View {
@ObservedObject var database: UserData
var body: some View {
// access database.WholeDocument
List(database.WholeDocument, id: \.self) {
// do something
}
}
That way, when you initialize MyContentView
with an instance of UserData
, you will be able to observe WholeDocument
in that view.
How Do I Cancel and Restart a Timed Event in Swift?
Use a timer:
weak var clearTimer: Timer?
And:
override func viewDidLoad() {
super.viewDidLoad()
startClearTimer()
}
func startClearTimer() {
clearTimer = Timer.scheduledTimer(
timeInterval: 3.0,
target: self,
selector: #selector(clearLabel(_:)),
userInfo: nil,
repeats: false)
}
func clearLabel(_ timer: Timer) {
label.text = ""
}
func volumeSliderValueChange(sender: UISlider) {
clearTimer?.invalidate() //Kill the timer
//do whatever you need to do with the slider value
startClearTimer() //Start a new timer
}
Related Topics
How to List Swift Types That Conform to Protocol Using Reflection
Swiftui 2 Firebase Push Notification
How to View Value of Swift "Let" Constant in Xcode 6 Debugger
Check for Value or Reference Type in Swift
Understanding Shorthand Closure Syntax for Map Function in Swift
Handle Swiftui and Corelocation with Mvvm-Pattern
Resize a Sklabelnode Font Size to Fit
Swiftui Onhover Doesn't Register Mouse Leaving the Element If Mouse Moves Too Fast
How to Move a Rotated Scnnode in Scenekit
Hstack with Sf Symbols Image Not Aligned Centered
How to Create Text File for Writing
Navigationview Doesn't Display Correctly When Using Tabview in Swiftui
How to Make Alphabetically Section Headers in Table View with a Mutable Data Source
How to Connect Aksequencer to a Akcallbackinstrument
Avspeechutterance - Swift - Initializing with a Phrase
My Data Changes in Uitableviewcell When I Scroll Down and Get Back