Init Super Error

So I'm trying to use an URL as the initialization for class. Here's my script:


  var videoURL : URL
   
    init(videoUrl:URL)
    {     
        self.videoURL = videoUrl     
    }
   
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


I get an error on line 8, "Super.init isn't called on all paths before returning from initializer." I have this in my TableViewCell class so that the controller passes the appropriate URL after some math. Why am I getting this error?

Accepted Reply

I'm afraid that doesn't work Claude. I get an error that says "Nil is incompatable with return type UITableViewCell." What I've ended up doing is giving the "videoURL" variable in my VideoTableViewCell an URL (documents folder), and this in my controller:


override func numberOfSections(in tableView: UITableView) -> Int{
        let videoURLs = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil)
        return videoURLs.count
    }
   
   
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
   
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let videoURLs = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil)
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! VideoTableViewCell
        cell.videoURL = videoURLs[0]     //set to first part of array for testing
        cell.setDate()
        cell.setTitle()
        cell.setImage()
        return cell
       
    }


I have no issues with this. If there's no URLs no cells are built, and if there are, it initializes one cell for each URL. Now I'm working on passing each part of the array to the appropriate cell. Thanks guys for all of your help.

Replies

If this a table view cell, then you need to set its properties every time you provide a cell to the table view, that is, in your "tableView(_:cellForRowAt:)" data source method. Typically, you get a cell using "dequeueReusableCellWithIdentifier". You cast the result to the actual type of the cell, then you can set its videoURL property directly.


You should not use the initializer to set up this value in this situation.


If you have something more complicated going on, you'll have to show (for example) your implementation of "tableView(_:cellForRowAt:)" from the data source.


This subject is discussed here:


developer.apple.com/library/content/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html


It's a few years old, but I don't think anything important has changed since it was written.

Well, I didn't know that I have to have that method [tableView(_:cellForRowAt:)] anywhere. I'm using the storyboard to design everything. I have no clue where to put that method (I did look at the link, thx btw) but don't understand it. Sorry if I'm being difficult, but I'm very very new to this.

This beginner's tutorial by Apple will get you started with table views: https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html

OK, so I've gone through the appropriate section of the tutorial, and come up with nothing. I don't understand what I need to do. I do know that I need the initialization to be dependant on an URL being sent to it from an array that I have setup. I just need someone to point me in the right direction.

This is a bit confusing because there's a lot of flexibility in setting up table views. For current purposes, you need to say whether your table view is using dynamic prototypes or static cells. (See the link I gave you above, under the heading "Choose the Table View’s Content Type".)


Table views with dynamic prototypes show only a single row in the storyboard in IB. The cell you design is the prototype for all the rows of your table, but the number of rows isn't known until runtime. In this scenario, you must implement a data source, including the "tableView(_:cellForRowAt:)" method I mentioned earlier.


Table views with static cells have a fixed number of rows known at design time. Each row can, and usually will, have a different cell structure. In this case, you don't implement a data source. Instead, you create outlets from your view controller to elements of the cells, and you "populate" all the cells in "viewDidLoad". (This is described in the above link, under the heading "Populating a Static Table View With Data".)


So, which kind of table are you using? If you have dynamic prototypes, then you haven't yet implemented a data source, and you'll have to do that before you can configure your cells. If you have static cells, you need to add an outlet to the cell view that needs a URL, and set its videoURL property in viewDidLoad.

Hey Quincy. Again thanks for the help. I've gone through what you said and translated it to Swift. Now I'm getting an error on line 13 that says "Value of type UITableViewCell has no member videoUrl," which I can assure you does. What am I doing wrong???


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       
        let filePath = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) //Returns array of documents files
        let cellID = "cell"
        let cell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellID)!
        if (cell == nil)
        {
       
            let cell: VideoTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellID) as! VideoTableViewCell
           
        }
       
        cell.videoUrl = (filePath[0])     //Using first part of array for testing
        return cell
    }

There are several errors :


1. you declare first


let cell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellID)!


then cell is of type UITableViewCell and not of the type of your subclass where you declared videoUrl


2. Then in the if you redeclare a new cell:

let cell: VideoTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellID) as! VideoTableViewCell


This cell is not modifying the one you defined at line 5, but creates a new one

Anyway, it only exist insit the if block.


3. cell cannot be nil :

- either the dequeue returns nil and then you will get a crash error

- otherwise cell is not nil


4. in the if block, you should create a new cell, not retry the same dequeue


So, you should write something like:


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   
        let filePath = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) //Returns array of documents files
        let cellID = "cell"
        var cell: VideoTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellID) as? VideoTableViewCell     // as? may fail, so can be nil
        if (cell == nil)  {
            cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellID) as? VideoTableViewCell     // I assume the style is default, you have to check
        }
   
       if cell == nil { return nil }          // if everything before has failed !
        cell!.videoUrl = (filePath[0])     //Using first part of array for testing. You have now to unwrap cell
        return cell!
    }

Edit: Removed the portion of the post where I said something stupid.

Also, remember that you renamed the "videoUrl" property to "videoURL" in an earlier phase of this discussion, which is why your line 13 is producing an error.

>The current documentation for "dequeueReusableCell" says that it returns a non-nil value


Some clarification:

dequeueReusableCell(withIdentifier:)

returns an Optional UITableViewCell.

dequeueReusableCell(withIdentifier:for:)

returns a straight UITableViewCell.


Batman and Claude were using the former (as did you and the guide to which you linked earlier), so they were correct to consider the possibility of nil being returned.


>Additionally, this is a method that satisfies a protocol requirement, not an override (unless you're doing something very weird in your data source), so it can't have an "override" keyword.


If Batman and Claude were using a subclass of UITableViewController, then `override` was correct and necessary.

Well, obviously I haven't done this recently enough to remember properly. Under the circumstances, I'd use the one that doesn't return an optional. I don't see any reason to prefer the other one, and I don't see any reason to have a fallback

Batman probably used the optional-returning dequeue because that's the one which is used in the guide that you linked. That guide's section "Programmatically Adding Subviews to a Cell’s Content View" has an example of why one might use that method.

Hey guys. First of all, you're all giving me so much help and explaing so much I can't thank you enough. So to answer any questions, I have no subclasses (as I understand them), I have this:


var videos = [Video]()

    let cellReuseIdentifier = "cell"


    // Data Source //
    override func numberOfSections(in tableView: UITableView) -> Int
    {
        return 1
    }


    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print (videos.count)    //debugging
        return 1                // set to one for testing
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let videoURLs = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) /
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! VideoTableViewCell
        cell.videoURL = videoURLs[0]     //Using first part of array for testing
        return cell
    }


in my VideoListTableViewController. And this:


class Video: NSObject{ 
   
    var videoURL: URL 
   
    init?(videoURL: URL)
    {
        self.videoURL = videoURL
    }
   
}


as my data model. I have no error messages in the compiler (YAY). However (Meh), when I launch the app it freezes at the launch screen. The debugging console displays this at the first thread: "VideoListTableViewController.init(coder : NSCoder) -> VideoListTableViewController? Now, to the best of my knowledge the NSCoder is used for saving the data so that everything's where it was when you closed it. Umm, what's going on?


BTW, if my cell style is custom, how do I use it in Claude's solution? Also, goldsdad, I did use the other because it was in the guide (I'm VERY VERY VERY new to this).

You can think of your storyboard as compiling into one big archive containing all the objects in the storyboard. (In fact, it's a lot of small archives with various pieces, but you don't need to deal with them yourself.) Therefore, your VideoListTableViewController is going to be created by unarchiving it from the storyboard, and so it's going to use the init(coder:) initializer. If you've provided a dummy init(coder:) method that aborts with an error message, you're going to crash your app.


Assuming that's what you did, you should delete all init methods to let your VideoListTableViewController inherit its superclass initializers. If you have initialization to do in the subclass, then you'd typically do that in viewDidLoad instead.

I don't have any other initializers. The navigation controller this is connected to is the storyboard entry point. Could that have anything to do with it?

I have no subclasses (as I understand them),


So, what is VideoTableViewCell in your code ? How is it declared ?