6

I'm writing an extension for the Apple Watch which aim is to guide the user through a set of steps. I don't have the watch to test the extension but I need to be sure that the screen will remain always on until all the steps are completed.

Does anyone can tell me when Apple Watch display is turned off or if there any way to prevent the display from turning off (similar idleTimerDisabled from UIApplication in iOS)?

aumanets
  • 3,703
  • 8
  • 39
  • 59

5 Answers5

7

There is currently no way to programmatically prevent the display from being turned off. Otherwise, we can speculate that as long as the user is interacting with your app, the screen will remain on. Worst case, you'll receive a didDeactivate when the app is suspended and a willActivate when the user wakes it up to continue (assuming the Watch app isn't terminated in between).

Mike Swanson
  • 3,337
  • 1
  • 17
  • 15
  • More detail on the WatchKit Controller Life Cycle: http://blog.mikeswanson.com/post/118262770484/watchkit-controller-life-cycle – Mike Swanson May 18 '15 at 05:24
5

As of watchOS 4 you can use this boolean to keep the display on, and this will rotate the UI when the arm is turned.

https://developer.apple.com/documentation/watchkit/wkextension/2868464-isautorotating

tennis779
  • 338
  • 2
  • 6
  • 19
  • Unfortunately this does not really keep the display on. it only works to a certain angle (which is pretty small). For me it is not helpful – benrudhart Jan 14 '18 at 15:21
  • I found that is does keep it on but I agree the angle is awkward, but for my case I was displaying a QR code, which can be used in any orientation for scanning. – tennis779 Jan 15 '18 at 15:38
1

Since I build app similar to Apple' Mindfulness app I was curious how they do it. Then I noticed that there is something which looks like video being played. So I started experimenting with playing video and it worked - display didn't turn off.

Point is, you need to use native VideoPlayer and then play video in loop. It is great that this view doesn't even have to be visible, so you can just put it in the background with nearly zero opacity.

let audioURL = Bundle.main.url(forResource: "video", withExtension: "mp4")

...

let player = AVPlayer(url: audioURL)

...

.background(
   VideoPlayer(player: player)
      .opacity(0)
)

Then you just need to make sure that you loop the video playing. You can achieve this by seeking player to the beginning after some time (e.g. 1 second). This also allows you to have just small video file which doesn't take too much space.

private var avPlayerPlayTask: Task<Void, Never>?

...

func startAVPlayerPlayTask() {
    avPlayerPlayTask?.cancel()
    avPlayerPlayTask = Task {
    await player.seek(to: .zero)
    player.play()
    try? await Task.sleep(nanoseconds: UInt64(1 * 1_000_000_000))
    guard !Task.isCancelled else { return }
    startAVPlayerPlayTask()
}

One last thing. Once you leave the screen when you need this, don't forget to cancel the task and stop the player since having player playing all the time can be heavy in terms of battery effeciency.


Fully working code (don't forget to create some short video called video and add it to bundle):

struct PlayerView: View {

    @ObservedObject var viewModel: PlayerViewModel

    var body: some View {
        Text("Hello World!")
            .background(
                VideoPlayer(player: viewModel.player)
                    .opacity(0)
            )
            .onAppear { viewModel.handleAppear() }
            .onDisappear { viewModel.handleDisappear() }
    }

}

@MainActor final class PlayerViewModel: ObservableObject {

    var player = AVPlayer()

    func handleAppear() {
        guard let url = Bundle.main.url(forResource: "video", withExtension: "mp4") else { return }
        player.replaceCurrentItem(with: AVPlayerItem(url: url))
        startAVPlayerPlayTask()
    }

    func handleDisappear() {
        avPlayerPlayTask?.cancel()
        player.replaceCurrentItem(with: nil)
    }

    private var avPlayerPlayTask: Task<Void, Never>?

    private func startAVPlayerPlayTask() {
        avPlayerPlayTask?.cancel()
        avPlayerPlayTask = Task {
            await player.seek(to: .zero)
            player.play()
            try? await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
            guard !Task.isCancelled else { return }
            startAVPlayerPlayTask()
        }
    }

}
Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
0

Consider HKWorkout.

it ensures that your app appears whenever the user checks their watch

https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings

Fitsyu
  • 860
  • 11
  • 19
0

It seems when the system is using the mic, it forces the screen to stay on — I’ve noticed this even in third party apps that allow sending voice messages. Here is the view that the user sees when screen idling is disabled in this way:

Noise graph

Note that this is a third party app and the screen didn’t turn off even after 2 full minutes!

Shawn Hemelstrand
  • 2,676
  • 4
  • 17
  • 30
Brandon
  • 13
  • 2