Difficulté avec .setOn pour un Switch [Résolu]

Hello à tous,

Je suis confronté à un soucis sur ma première app que je suis en train de créer pour une webradio. Tout semble bien fonctionner dans le global sauf le petit Switch qui va activer ou pas un autoplay au lancement de l’app, mais je suis confronté à une erreur fatale. :confused:

//
//  ViewController.swift
//  xx
//
//  Created by Didier on 15/08/2017.
//  Copyright © 2017 xxxxxxxxxxxx. All rights reserved.
//

import UIKit
import AVKit
import AVFoundation
import Alamofire

var player : AVPlayer!
var playerRunning = false
let SWITCH_USER_DEFAULTS_KEY:String = "AUTOPLAY"

class ViewController: UIViewController {
    

    @IBOutlet weak var playButton: UIButton!
    @IBOutlet weak var ui_currentArtist: UILabel!
    @IBOutlet weak var ui_currentSong: UILabel!
    @IBOutlet weak var ui_currentJacket: UIImageView!
    @IBOutlet weak var ui_autoplaySwitch: UISwitch!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        getcurrentSong()
        Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(getcurrentSong), userInfo: nil, repeats: true)
        playButton.backgroundColor = .clear
        playButton.layer.cornerRadius = playButton.frame.height / 2
        playButton.layer.borderWidth = 1
        playButton.layer.borderColor = UIColor.black.cgColor
        //playButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            print("AVAudioSession Category Playback OK")
            do {
                try AVAudioSession.sharedInstance().setActive(true)
                print("AVAudioSession is Active")
                
            } catch let error as NSError {
                print("------>" + error.localizedDescription)
            }
        } catch let error as NSError {
            print("------>" + error.localizedDescription)
        }
        // LANCEMENT DE LA RADIO AU DEMARRAGE DE L'APP EN FONCTION DE LA PREFERENCE UTILISATEUR
        if UserDefaults.standard.bool(forKey: SWITCH_USER_DEFAULTS_KEY) == true {
            playPlayer()
            ui_autoplaySwitch.setOn(true, animated: false)
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func autoplaySwitch(_ sender: UISwitch) {
        let userDefaults = UserDefaults.standard
        userDefaults.set(sender.isOn, forKey: SWITCH_USER_DEFAULTS_KEY)
    }
    @IBAction func playRadio() {
        if (playerRunning) {
            stopPlayer()
        } else {
            playPlayer()
        }
    }
    func playPlayer() {
        let url = "http://xxxxxxxxxxxxxxxxxx.net/xxxxxxxxxxx-128.mp3"
        player = AVPlayer(url: URL(string: url)!)
        player.volume = 1.0
        player.rate = 1.0
        player.play()
        playButton.setImage(UIImage(named: "stop"), for: .normal)
        playButton.setTitleColor(UIColor.blue, for: .selected)
        playerRunning = !playerRunning
    }
    func stopPlayer() {
        player.pause()
        playButton.setImage(UIImage(named: "play"), for: .normal)
        playerRunning = !playerRunning
    }
    @objc func getcurrentSong() {
        Alamofire.request("http://xxxxxxxxxxxx.fr/ajax/current").validate().responseJSON {
            (response: DataResponse<Any>) in
            if response.result.isSuccess {
                if let songs = response.result.value as? [String:AnyObject] {
                    if (songs["current"] != nil) {
                        let currentArtist = songs["current"]!["artist"] as? String
                        let currentTrack = songs["current"]!["track"] as? String
                        let urlOfCurrentJacket = songs["current"]!["cover"] as? String
                        self.ui_currentArtist.text = String(currentArtist!)
                        self.ui_currentSong.text = String(currentTrack!)
                        self.ui_currentJacket.downloadedFrom(link: urlOfCurrentJacket!)
                    }
                }
            }
        }
    }
}
extension UIImageView {
    func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
        contentMode = mode
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let image = UIImage(data: data)
                else { return }
            DispatchQueue.main.async() { () -> Void in
                self.image = image
            }
            }.resume()
    }
    func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
        guard let url = URL(string: link) else { return }
        downloadedFrom(url: url, contentMode: mode)
    }
}

Le soucis que je rencontre semble venir de la ligne :
ui_autoplaySwitch.setOn(true, animated: false)

L’erreur :

fatal error: unexpectedly found nil while unwrapping an Optional value

Je ne comprend pas pourquoi j’obtiens cette erreur. Si quelqu’un voulait éclairer ma lanterne… :roll_eyes:

N’hésitez pas à me donner vos commentaires sur le code si besoin, si j’aurais pu faire plus efficace ou je ne sais pas :slightly_smiling_face:

Le message d’erreur indique que tu tentes de faire une opération sur un objet vide.

fatal error: unexpectedly found nil while unwrapping an Optional value

Alors que la variable ui_autoplaySwitch devrais contenir un lien vers un objet de ton interface, elle renferme la valeur nil (nil = vide). Tu as du faire une fausse manœuvre avec Storyboard et effacer l’outlet. Il est encore dans le code, mais ne pointe plus sur un contrôle du Storyboard. Cela se corrige en quelques secondes (si c’est bien ça).

Je pense que @Draken a mis le doigt dessus. Regarde la log d’exécution avec le crash, tu devrais avoir des infos complémentaires sur l’objet et ce genre de chose.

Je ne pense pas, c’est pas une erreur de compilation mais, une erreur d’exécution donc, le problème ne vient pas du storyboard.

Est-ce que quand la fonction autoplay est désactiver et que tu lance la musique ça fonctionne ?

Oh que si… Crée un projet vide avec un label, tire un outlet, écrit une ligne de code utilisant le label, efface-le du Storyboard, et compile le code. Xcode ne vérifiera pas la validité de l’outlet. L’erreur ne sera visible qu’à l’exécution.

C’est surprenant, mais cela se produit bel et bien. Je suis déjà tombé sur cette erreur. D’ailleurs, je viens de la reproduire (voir copie d’écran).

Hello Anthony. Oui, si c’est false, ça fonctionne, j’entend la musique et ça s’execute. Dans tous les cas ça compile correctement. Je n’y comprend rien. :confused:

Affiche la valeur de ui_autoplaySwitch avec un print() avant de l’utiliser. Il est presque certain que l’outlet contient nil.

// LANCEMENT DE LA RADIO AU DEMARRAGE DE L’APP EN FONCTION DE LA PREFERENCE UTILISATEUR
if UserDefaults.standard.bool(forKey: SWITCH_USER_DEFAULTS_KEY) == true {
playPlayer()
// Affichage outlet
print (ui_autoplaySwitch)
ui_autoplaySwitch.setOn(true, animated: false)
}

Merci @Draken, @sylvain et @ThonyF

C’était exactement ça, la connexion de l’outlet qui était manquante…

J’ai regardé l’inspecteur des connexions du ViewController dans le storyboard et ui_autoplaySwitch n’avait aucune connexion (pourquoi? Ça, ça reste encore à déterminer). :hugs:

Petite question au passage, comment on enlève les anciens outlets qui n’ont plus de connexion et qui n’on plus lieu d’exister?

Merci!

Tu as du changer son nom après l’avoir crée, non ? :wink:

Ça, c’est bien probable! @sylvain :rofl:

C’est juste des lignes de code inutiles. Il suffit de les effacer à la main.

2 « J'aime »

Autant pour moi,
Je ne suis jamais tomber sur ce message d’erreur pour un lien entre un élément graphique et le code.
Je saurai pour la prochaine fois en tout cas