Moritz
Moritz

Reputation: 766

Change string in other class (without making it global) in swift

I would like to modify a string in another class. This class will then use the variable in a function.

Here is what I've tried so far. I always get an error when unwrapping it: fatal error: unexpectedly found nil while unwrapping an Optional value. Does anyone have an idea how to change the urlString (preferably without making it global)? I couldn't find solutions for swift which also involved functions on stackoverflow... If you think I will have to make it global, please let me know!

In class #1

let videoUrl =  "https:...sometestvideo.mov"
videoPlayerView.urlString = videoUrl

In class #2

var urlString : String?

//setup video player
func setupPlayerView() {

    print(urlString)

    //URL needed here
    if let videoURL = NSURL(string: urlString!){ //here the error occurs

I would like to add that it is very important that the function is called asap in the second class. Therefore I didn't use setupPlayerView(_urlString)...

Accordingly it currently looks like this (class 2, a UIView):

override init(frame: CGRect){

    super.init(frame: frame)

    //function below this override init
    setupPlayerView()

EDIT:

First of all, thank you for your solution! Nonetheless, one little problem remains (and I thought calling the function immediately would solve it... quite new to swift): namely the video player (which is set up using this function) is now above all the other subviews (one can only see the video covering the entire screen) although the opposite is desired (video using entire screen but subviews cover some parts). I will provide more code below regarding the addition of other subviews (all of these are closures) and the function setting up the view. Is there a way I can keep the videoplayer below all the other subviews (even if it needs to be loaded from a server first)? What would you suggest me to do?

Code below incorporates code from the first answer, but does not necessarily have to start from there

Class 2

override init(frame: CGRect){

    super.init(frame: frame)

    setupPlayerView()

    //add subview with controls (e.g. spinner)
    controlsContainerView.frame = frame
    addSubview(controlsContainerView)

    //add to subview and center spinner in subview
    controlsContainerView.addSubview(activityIndicatorView)
    activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
    activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true

    //add various subviews
    controlsContainerView.addSubview(whiteDummyView)
    controlsContainerView.addSubview(userProfilePicture)
    //... add further subviews

    //enable interaction
    controlsContainerView.isUserInteractionEnabled = true
    //... set to true for other subviews as well

    //function below this override init
    defInteractions()

    //backgorund color of player
    backgroundColor = .black

}

//create controls container view (a closure, like the (most of the) other subviews)
let controlsContainerView: UIView = {

    //set properties of controls container view
    let controlView = UIView()
    controlView.backgroundColor = UIColor(white: 0, alpha: 1)

    return controlView

}()

function setupPlayerView()

//setup video player
func setupPlayerView() {

    //check URL if can be converted to NSURL
    if let urlString = self.urlString, let videoURL = NSURL(string: urlString){

        print(urlString)

        //player's video
        if self.player == nil {
            player = AVPlayer(url: videoURL as URL)
        }

        //add sub-layer
        let playerLayer = AVPlayerLayer(player: player)
        self.controlsContainerView.layer.addSublayer(playerLayer)
        playerLayer.frame = self.frame

        //when are frames actually rendered (when is video loaded)
        player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)

        //loop through video
        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
            DispatchQueue.main.async {
                self.player?.seek(to: kCMTimeZero)
                self.player?.play()
            }
        })

    }

}

Upvotes: 0

Views: 77

Answers (2)

Maddy
Maddy

Reputation: 1668

While navigating to second view controller.

 let viewController2 = ViewController2() //viewController2 == class2 
 viewController2.urlString = videoUrl // anything you want to pass to class2.
 navigationController?.pushViewController(viewController2, animated: true)

In your class 2:

var urlString: String = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print(urlString) // use it 
        setupPlayerView()
    }

Your func:

func setupPlayerView() {

        //check URL if can be converted to NSURL
        if  NSURL(string: urlString) != nil{

            print(urlString)

        }
    }

Upvotes: 0

bsarrazin
bsarrazin

Reputation: 4040

Without too much context about the relationship between your classes, looks like a potentially good solution is to use a property observer pattern.

class Class2 {
    var urlString: String? {
        didSet {
            setupPlayerView()
        }
    }

    override init(frame: CGRect){
        super.init(frame: frame)
        setupPlayerView()
    }

    func setupPlayerView() {
        if let urlString = self.urlString, let videoURL = NSURL(string: urlString) {
            // do stuff here
        }
    }
}

Upvotes: 1

Related Questions