Reputation: 103
I have the following code snippet:
struct player : UIViewControllerRepresentable {
var url : String
var player1: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<player>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
player1 = AVPlayer(url: URL(string: url)!)
controller.player = player1
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<player>) {
}
func pause() {
player1.pause()
}
}
This gives the error:
'Cannot assign to property: 'self' is immutable'
I need to have the AVPlayer outside of the makeUIViewController function because I need to access it from the pause function. How can I do this?
Upvotes: 3
Views: 1404
Reputation: 14388
The error you are seeing appears due to the fact that structs are value types and any method on them that changes their properties needs to be marked as mutating
. Unfortunately you cannot mark makeUIViewController
because it is defined in UIViewControllerRepresentable
protocol, but there is a fairly easy solution.
You only actually use the url to construct an AVPlayer
- there is no need to hold on to it. Write and initializer for your struct that takes a url string and constructs an AVPlayer
. I have made the AVPlayer
optional as URL(string: String)
returns an Optional URL
(as you can imagine not all strings are valid urls). The below code works as expected:
struct Player: UIViewControllerRepresentable {
var player1: AVPlayer?
public init(url string: String) {
guard let url = URL(string: string) else {
self.player1 = nil
return
}
self.player1 = AVPlayer(url: url)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<Player>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player1
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController,
context: UIViewControllerRepresentableContext<Player>) {}
func pause() {
player1?.pause()
}
}
A side note: all type names in Swift (classes, structs, enums) are by convention upper-cased: your struct should be called Player
not player
.
You should also look into Cordinator
for UIViewControllerRepresentable
- you need something to act as your AVPlayerViewControllerDelegate
.
Upvotes: 3
Reputation: 257819
There are several possibilities to choose from (actually it is not a complete list of options, but hope it would be helpful)
Option1: Do it in initailizer
struct player : UIViewControllerRepresentable {
private var url : String
private var player1: AVPlayer
init(url: String) {
self.url = url
self.player1 = AVPlayer(url: URL(string: url)!)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<player>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player1
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<player>) {
}
}
Option2: Do it on first request to update controller
struct player : UIViewControllerRepresentable {
var url : String
var player1: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<player>) -> AVPlayerViewController {
return AVPlayerViewController()
}
func updateUIViewController(_ playerController: AVPlayerViewController, context: UIViewControllerRepresentableContext<player>) {
if playerController.player == nil {
playerController.player = AVPlayer(url: URL(string: url)!)
}
}
}
Option3: If you need to modify player1
during lifetime, then you need to keep it in some external entity, say ObservableObject, because your player
is View struct and cannot modify itself, actually what is said in compiler error.
Upvotes: 1