-1

I'm newbie to Xcode and Swift.

This is my first program.

I include the full code.

I get this error and I don't know how to solve it.

The program builds ok, but when I run it I get this error.

Thread 1: Fatal error: init(coder:) has not been implemented

I appreciate if you provide a coded solution.

This is the ResultView Controller.swift file.

//
//  ResultsViewController.swift
//  PersonalityQuiz
//
//  Created by Ricardo E. Marrero Guzmán on 12/16/21.
//

import UIKit

class ResultsViewController: UIViewController {

    @IBOutlet var resultAnswerLabel: UILabel!
    @IBOutlet var resultDefinitionLabel: UILabel!
    
    var responses: [Answer]  // Esto provoca un error.  Para quitarlo se añade el siguiente init?...
    
    init?(coder: NSCoder, responses: [Answer]) {  // Este init?... provoca otro error.  Se le da fix y se crea el siguiente required init?...
        self.responses = responses
        super.init(coder: coder)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented") // Thread 1: Fatal error: init(coder:) has not been implemented
    }

override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        calculatePersonalityResults()
        navigationItem.hidesBackButton = true
    }
    
    func calculatePersonalityResults() {
        
        let frecuencyOfAnswers = responses.reduce(into: [:]) {
            (counts, answer) in counts[answer.type, default: 0] += 1
        }
let frequentAnswersStored = frecuencyOfAnswers.sorted(by:
        { (pair1, pair2) in
            return pair1.value > pair2.value
        })
        
        let mostCommonAnswer = frequentAnswersStored.first!.key
        
        resultAnswerLabel.text = "You are \(mostCommonAnswer.rawValue)!"
        resultDefinitionLabel.text = mostCommonAnswer.definition

}

This is de QuestionViewController.swift file

//
//  QuestionViewController.swift
//  PersonalityQuiz
//
//  Created by Ricardo E. Marrero Guzmán on 12/16/21.
//

import UIKit

class QuestionViewController: UIViewController {
    
    @IBOutlet var questionLabel: UILabel!
    
    @IBOutlet var singleStackView: UIStackView!
    @IBOutlet var singleButton1: UIButton!
    @IBOutlet var singleButton2: UIButton!
    @IBOutlet var singleButton3: UIButton!
    @IBOutlet var singleButton4: UIButton!
    
    @IBOutlet var multipleStackView: UIStackView!
    @IBOutlet var multiLabel1: UILabel!
    @IBOutlet var multiLabel2: UILabel!
    @IBOutlet var multiLabel3: UILabel!
    @IBOutlet var multiLabel4: UILabel!
    
    @IBOutlet var multiSwitch1: UISwitch!
    @IBOutlet var multiSwitch2: UISwitch!
    @IBOutlet var multiSwitch3: UISwitch!
    @IBOutlet var multiSwitch4: UISwitch!
    
    @IBOutlet var rangedStackView: UIStackView!
    @IBOutlet var rangedLabel1: UILabel!
    @IBOutlet var rangedLabel2: UILabel!
    
    @IBOutlet var rangedSlider: UISlider!
    
    @IBOutlet var questionProgressView: UIProgressView!

var answersChosen: [Answer] = [] // No se donde va esto
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        updateUI()
        
    }
    
    @IBAction func singleAnswerButtonPressed(_ sender: UIButton) {
        let currentAnswers = questions[questionIndex].answers
        
        switch sender {
        case singleButton1:
            answersChosen.append(currentAnswers[0])
        case singleButton2:
            answersChosen.append(currentAnswers[1])
        case singleButton3:
            answersChosen.append(currentAnswers[2])
        case singleButton4:
            answersChosen.append(currentAnswers[3])
        default:
            break
        }
        
        nextQuestion()
    }
    
    @IBAction func multipleAnswerButtonPressed() {
        let currentAnswers = questions[questionIndex].answers
        
        if multiSwitch1.isOn {
            answersChosen.append(currentAnswers[0])
        }
        if multiSwitch2.isOn {
            answersChosen.append(currentAnswers[1])
        }
        if multiSwitch3.isOn {
            answersChosen.append(currentAnswers[2])
        }
        if multiSwitch4.isOn {
            answersChosen.append(currentAnswers[3])
        }
        
        nextQuestion()
    }
    
    @IBAction func rangedAnswerButtonPressed() {
        let currentAnswers = questions[questionIndex].answers
        
        let index = Int(round(rangedSlider.value * Float(currentAnswers.count - 1)))
        
        answersChosen.append(currentAnswers[index])
        
        nextQuestion()
    }
    
    func updateUI() {
        singleStackView.isHidden = true
        multipleStackView.isHidden = true
        rangedStackView.isHidden = true
        
        let currentQuestion = questions[questionIndex]
        let currentAnswers = currentQuestion.answers
        let totalProgress = Float(questionIndex) / Float(questions.count)
        
        navigationItem.title = "Question #\(questionIndex + 1)"
        questionLabel.text = currentQuestion.text
        questionProgressView.setProgress(totalProgress, animated: true)
        
        switch currentQuestion.type {
        case .single:
            singleStackView.isHidden = false
            updateSingleStack(using: currentAnswers)
        case .multiple:
            multipleStackView.isHidden = false
            updateMultipleStack(using: currentAnswers)
        case .ranged:
            rangedStackView.isHidden = false
            updateRangedStack(using: currentAnswers)
        }
    }
    
    func updateSingleStack(using answers: [Answer]) {
        singleStackView.isHidden = false
        singleButton1.setTitle(answers[0].text, for: .normal)
        singleButton2.setTitle(answers[1].text, for: .normal)
        singleButton3.setTitle(answers[2].text, for: .normal)
        singleButton4.setTitle(answers[3].text, for: .normal)
    }
    
    func updateMultipleStack(using answers: [Answer]) {
        multipleStackView.isHidden = false
        
        multiSwitch1.isOn = false
        multiSwitch2.isOn = false
        multiSwitch3.isOn = false
        multiSwitch4.isOn = false
        
        multiLabel1.text = answers[0].text
        multiLabel2.text = answers[1].text
        multiLabel3.text = answers[2].text
        multiLabel4.text = answers[3].text
    }
    
    func updateRangedStack(using answers: [Answer]) {
        rangedStackView.isHidden = false
        
        rangedSlider.setValue(0.5, animated: false)
        
        rangedLabel1.text = answers.first?.text
        rangedLabel2.text = answers.last?.text
    }
    
    func nextQuestion() { // Este es el método más importante del programa y lo que hace correr el app bien.
        questionIndex += 1
        
        if questionIndex < questions.count {
            updateUI()
        } else {
            performSegue(withIdentifier: "Results", sender: nil)
        }
    }

    @IBSegueAction func showResults(_ coder: NSCoder) -> ResultsViewController? {
        return ResultsViewController(coder: coder, responses: answersChosen)
    }

This is the Question.swift file.

//
//  Question.swift
//  PersonalityQuiz
//
//  Created by Ricardo E. Marrero Guzmán on 12/17/21.
//

import Foundation

struct Question {
    var text: String
    var type: ResponseType
    var answers: [Answer]
}

enum ResponseType {
    case single, multiple, ranged
}

struct Answer {
    var text: String
    var type: AnimalType
}

enum AnimalType: Character {
    case dog = "", cat = "", rabbit = "", turtle = ""
    
    var definition: String {
        switch self {
        case .dog:
            return "You are incredibly outgoing.  You sorround yourself with the people you love and enjoy activities with your friends."
        case .cat:
            return "Mischievous, yet mild-tempered, you enjoy doing things on your own terms."
        case .rabbit:
            return "You love everithings that's soft.  You are healthy and full of energy."
        case .turtle:
            return "You are wise beyond your years, and you focus on the details.  Slow and steady wins the race."
        }
    }
}

var questions: [Question] = [
    Question(
        text: "Which food do you like the most?",
        type: .single,
        answers: [
            Answer(text: "Steak", type: .dog),
            Answer(text: "Fish", type: .cat),
            Answer(text: "Carrots", type: .rabbit),
            Answer(text: "Corn", type: .turtle)
        ]
    ),
    
    Question(
        text: "Which activities do you enjoy?",
        type: .multiple,
        answers: [
            Answer(text: "Swimming", type: .turtle),
            Answer(text: "Sleeping", type: .cat),
            Answer(text: "Cuddling", type: .rabbit),
            Answer(text: "Eating", type: .dog)
        ]
    ),
    
    Question(
        text: "How much do you enjoy car rides?",
        type: .ranged,
        answers: [
            Answer(text: "I dislike them", type: .cat),
            Answer(text: "I get a little nervous", type: .rabbit),
            Answer(text: "I barely notice them", type: .turtle),
            Answer(text: "I love them", type: .dog)
        ]
    )
]

var questionIndex = 0

This is de introductionViewController file.

//
//  ViewController.swift
//  PersonalityQuiz
//
//  Created by Ricardo E. Marrero Guzmán on 12/16/21.
//

import UIKit

class IntroductionViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func unwindToQuizIntroduction(segue: UIStoryboardSegue) {
        
    }

}

Richard
  • 39
  • 5
  • 1
    Your custom init will not be called if you are using a storyboard, so you can delete it and the other init that calls fatalError. Instead use prepare for segue instead to set your Answer array. – Joakim Danielson Dec 21 '21 at 18:58
  • see [here](https://stackoverflow.com/questions/26923003/how-do-i-make-a-custom-initializer-for-a-uiviewcontroller-subclass-in-swift/58017901#58017901) and the comment below it – mfaani Dec 21 '21 at 21:41

4 Answers4

1

When UIViewController is instantiated from the Storyboard, it will call required init?(coder: NSCoder), which currently just throw a fatal error. You can do one of three things:

  1. Either delete both required init?(coder: NSCoder) and init?(coder: NSCoder, responses: [Answer]) like Joakim said, and use segue to instantiate it

  2. Or delete init?(coder: NSCoder, responses: [Answer]) and implement required init?(coder: NSCoder) correctly like this:

required init?(coder: NSCoder) {
    self.responses = [Answer]()
    super.init(coder: coder)
}
  1. You can also initialize [Answer] at declaration, that way you don't need required init?(coder: NSCoder), and can delete both inits, just like option 1:
 var responses = [Answer]()
timbre timbre
  • 12,648
  • 10
  • 46
  • 77
1

You need to implement prepare(for: segue) in your QuestionViewController and use this function to pass your array of Answer

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == "Results") {
        let viewController = segue.destination as? MyOrderDetailsVC
        viewController!.responses = answersChosen
    }
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
0

Looks like you wrongly instantiate your view controller You didn't share enough code but I can assume that you forgot to load Xcode form xib Try this:

let storyboard = UIStoryboard(name: "myStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "myVCIdentifier")
present(vc, animated: true)
-1

Finally I found a solution. The problem was a lost segue connection. I reconnect the segue without changing anything in the program and the solution was come.

Thanks, I appreciate your solution recommendations.

Richard
  • 39
  • 5