SwiftUI: Keep VideoPlayer playing while device is locked - swiftui

title says it all as far as I'm aware. I have the following view:
struct VideoView: View{
#State var FilePath = ""
#State private var player = AVPlayer()
var body: some View{
VideoPlayer(player: player)
.onAppear{
player = AVPlayer(url: URL(string: FilePath)!)
player.play()
}.onDisappear{
player.pause()
}
}
}
I'm calling this in my ContentView, and it works fine. But when I close the app, or when the device is locked, the video stops playback. Is there a way that I can change this so that it will keep playing?

It took a while to figure it out, but I eventually came up with the following solution.
make sure that you enable Audio, AirPlay and Picture in Picture under Signing and Capabilities
When you define your AVPlayer, add the following code:
player = AVPlayer(url: URL(string: FilePath)!)
do{
try AVAudioSession.sharedInstance().setCategory(.playback)
try AVAudioSession.sharedInstance().setActive(true)
}catch{
print(error.localizedDescription)
}
player.audiovisualBackgroundPlaybackPolicy = .continuesIfPossible
You're done, your video will now play when your device is locked or you are in another app. Note that playback controls do not show on the simulator, however it works fine on the device.

Related

SwiftUI open ShareLink from ActionSheet (confirmationDialog) Not Working

I have a ShareLink inside an ActionSheet (confirmationDialog) but it's not opening. Can anyone confirm if this is possible or not? I imagine it's something to do with the action sheet being dismissed at the same time.
.confirmationDialog("", isPresented: $isShowingMoreActionSheet, titleVisibility: .hidden) {
if let url = URL(string: "https://www.google.com") {
ShareLink("Share", item: url)
}
}

SwiftUI Play sound on Apple Watch standalone app

Im making a standalone app for the Apple Watch and Im trying to play sound there, after trying all I could think of I only get errors with it. I wonder if I should import the sound file in a specific way or folder
I made a simple test with the code Im running in app, the test sound is calle 5.wav
The message is not being printed and get some error related to sound not being loaded
"Watch App[53825:678174] void * _Nullable NSMapGet(NSMapTable * _Nonnull, const void * _Nullable): map table argument is NULL
"
import SwiftUI
import AVFoundation
struct ContentView: View {
#State var avp:AVAudioPlayer?
var body: some View {
VStack {
Button("PLAY")
{
guard let url = Bundle.main.url(forResource: "5", withExtension: "wav")else
{
return
}
try? avp=AVAudioPlayer(contentsOf: url)
avp?.play()
print("printing")
}
}
.padding()
}
}

Why are my SwipeActions overflowing the listview SwiftUI

Why is my SwiftUI Swipe Action behaving like this?
I don't now how to add a GIF in stack overflow so here is a imagur link https://imgur.com/a/9MqjIgX.
If you don't want to click on external links here is a image from the GIF:
My View:
struct MyView: View {
#State var shops = [Shop.empty(), Shop.empty(), Shop.empty(), Shop.empty(), Shop.empty()]
var body: some View {
NavigationView {
List($shops) { $shop in
Text(shop.name)
.swipeActions {
Button {
shop.toggleFavourite()
} label: {
Image(systemName: "star")
}
}
}
}
}
}
the shop struct:
struct Shop: Hashable, Identifiable {
var id: UUID
var favourite: Bool
init(id: UUID){
self.id = id
self.favourite = UserDefaults.standard.bool(forKey: id.uuidString)
}
mutating func toggleFavourite() {
favourite.toggle()
UserDefaults.standard.set(favourite, forKey: id.uuidString)
}
static func empty() -> Shop{
Shop(id: UUID())
}
}
But I can't sadly I can't give you a working example, because I tried to run this code in a fresh app and it worked, without the Bug. On the same device. And I don't understand why, because I also put this view in the root of my old project, just for testing, and the bug stayed there.
I was able to figure out, that if I commented out this line:
UserDefaults.standard.set(favourite, forKey: id.uuidString)
my code would work. But unfortunately I can't just leave out this line of code.
I tried several things, including wrapping this line into DispatchQueue.main.async {} and DispatchQueue.main.sync {}, same with the DispatchQueue.global(). I also added delays. Short delays wouldn't work at all (under .5 seconds) and longer delays would just delay the view bug.
Of course I also tried wrapping this line into a separate function, and so on.
There are two mayor points, why I'am so confused:
Why is the line, that sets this to the Userdefaults even influencing the view? I mean I checked with a print statement, that the initializer, which is the only section in my code that checks this Userdefaultvalue, only gets called when the view gets initialized.
Why does the code work in a different project?
I know since I can't provide a working example of my bug it's hard for you to figure out whats wrong. If you have any ideas, I would be very happy!

SwiftUI controls don't respond to input (clicks) in Catalyst if revealed in ScrollView

I'm dealing with what I am nearly certain to be a SwiftUI/Catalyst bug and am looking for a solution to get around it.
In the following code, about 30% of the time (5/15 in my tests), once the controls are revealed, the Toggle elements do not respond to clicks (and thus do not turn on/off).
I'm testing on Xcode 12.3 on Big Sur 11.1, running this code in Catalyst. It does work as expected 100% of the time as far as I can tell on iOS 14.3.
struct ContentView: View {
var body: some View {
ScrollView {
Row()
Row()
}
.padding()
}
}
struct Row : View {
#State private var showControls = false
#State private var toggleOn = false
var body: some View {
VStack {
HStack {
Text("Top section")
Button("\(showControls ? "Hide" : "Show") controls") {
showControls.toggle()
}
}
.frame(height: 100)
if showControls {
HStack {
Toggle("Toggle", isOn: $toggleOn)
Toggle("Toggle", isOn: $toggleOn)
}
}
}
}
}
The problem seems to come from having the Rows embedded in the ScrollView. The problem disappears completely if the controls start in their visible state (ie showControls = true), and only happens when they get revealed (ie showControls.toggle()) after the app starts.
I've also noticed that while Toggle and Slider fail about 30% of the time, a plain Button seems to be responsive 100% of the time.
The view debugger doesn't show anything 'in front' of the views that would be intercepting clicks.
I've tried changing to a List, which solves the problem, but yields other unfortunate side effects in behavior that I'd like to avoid in my real non-trivial app.
Can anyone else think of a reliable solution to avoiding this?

iOS WidgetKit: remote images fails to load

I'm observing a bizarre thing: in the new widgets far too often remote images are not being displayed even though the image has been successfully loaded and placed in cache.
For image downloading I've tried:
SDWebImageSwiftUI
Kingfisher
SwURL
All of them indicate that image loading succeed, but the actual widget is not showing it.
struct TestWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
WebImage(url: URL(string: "https://miro.medium.com/max/3840/0*TLqp5Uwavd-U_xrs.jpg"))
.onSuccess()
.resizable()
}
}
On the second run of the debugger - with image loading from cache - I get the image displayed, but never(?) on initial run.
It feels like that in onSuccess I need to trigger UI-invalidation? But how?
(Since it happens to literally every image-lib I try - I don't think that it's something off in the libs)
Environment:
iOS 14 Beta 3 (both device and simulators)
Xcode 12 Beta 3
During the debug run, memory use is around 15mb
Yes, as mentioned by Konstantin, it is not supported to load images asynchronously. There are 2 options to load network image in widgets
Either fetch all the images in TimelineProvider and inject them to the views directly.
OR
Use Data(contentsOf: url) to fetch the image. This way it still fetches them synchronously but the code is cleaner. Sample code -
struct NetworkImage: View {
private let url: URL?
var body: some View {
Group {
if let url = url, let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fill)
}
else {
Image("placeholder-image")
}
}
}
}
This view can simply be use like this -
NetworkImage(url: url)
Got it: it's simply not supported and you aim to load images inside TimelineProvider:
https://developer.apple.com/forums/thread/652581