0

I am trying to get a value from a swift dictionary to display as cell texts. I am getting error:

Type 'Any' has no subscript members

cell.audioLabel.text = audiofiles["filetitle"] <-line producing error

I believe I may have set the variables incorrectly, the value is being passed using segue from another tableview using the didSelectRowAt.

var audios = Array<Any>() <- gets from another controller

This is my current viewcontroller code:

import UIKit

class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var audioTable: UITableView!
    @IBOutlet weak var descText: UITextView!
    @IBOutlet weak var clickButton: UIButton!

    var practitle = String()
    var desc = String()
    var hasAudio = Int()
    var audios = Array<Any>()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.title = practitle

        descText.text = desc

        if hasAudio != 0 {
            clickButton.isHidden = false
        }

        print(audios)
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return audios.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "audiocell", for: indexPath) as! DetailViewCell

        let audiofiles = audios[indexPath.row]

        cell.audioLabel.text = audiofiles["filetitle"]

        return cell
    }

When I use

print(audios)

The result that I get is:

[demoApp.Audiofile(id: 1, filetitle: "Sample file one", filename: "breath5mins", fileformat: "mp3"), demoApp.Audiofile(id: 2, filetitle: "Sample file two", filename: "breath10mins", fileformat: "mp3"), demoApp.Audiofile(id: 3, filetitle: "Sample file three", filename: "breath20mins", fileformat: "mp3")]

How can I use the filetitle as the label texts for my cell?

The goal is to display the title and open another view on cell click and allow the user to click a button on the new view and play the mp3 file.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
Ron Modz
  • 41
  • 6

3 Answers3

2

Apparently the object is not a Swift dictionary, it's a custom class or struct.

You are fighting Swift's strong type system. Don't do that.

Declare audios with the static type

var audios = Array<AudioFile>()

and use dot notation instead of insecure KVC (which does not work anyway in this case).

cell.audioLabel.text = audiofiles.filetitle
vadian
  • 274,689
  • 30
  • 353
  • 361
  • i made the changes but i get - Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value – Ron Modz Sep 10 '18 at 20:52
  • 1
    The crash cannot be related to this code. We are dealing with non-optionals. – vadian Sep 10 '18 at 20:55
  • @RonModz Take a look [at this](https://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu) - You probably haven't connected an outlet between your storyboard and class. – Paulw11 Sep 10 '18 at 21:01
1

You have declared your array as Array<Any> - which means that the array can contain anything; the elements of the array don't even all need to be the same type. As a result, the Swift compiler doesn't know what type of thing it is getting from audios[indexPath.row]; it could be a dictionary, it could be an array or it could be an integer. When you try and use subscripting, the compiler gives you an error because it doesn't know whether the item supports subscripting - i.e. an Int doesn't, and Any could be an Int.

Don't use Any or AnyObject in Swift if you know the actual type; Swift's type safety allows it to eliminate a large number of potential runtime issues at compile time by know what types things are.

From the print statement it appears that your array contains Audiofile instances (presumably a struct or class you have defined, not a dictionary). You should, therefore, declare audios correctly:

var audios = Array<Audiofile>()

You can then access the object's properties in your cellForRow:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "audiocell", for: indexPath) as! DetailViewCell

    let audiofiles = audios[indexPath.row]

    cell.audioLabel.text = audiofiles.filetitle

    return cell
}
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • `type(of: demoApp.Audiofile(id: 1, filetitle: "Sample file one", filename: "breath5mins", fileformat: "mp3"))` is `Array`, maybe recommend declaring a `struct Audiofile` – ielyamani Sep 10 '18 at 20:51
  • The OP has an array of three elements. Those elements are `Audiofile`s (whatever that is, presumably a struct or class) – Paulw11 Sep 10 '18 at 20:53
  • See the last comments under the question – ielyamani Sep 10 '18 at 20:53
  • Yes, because they are printing the details of the property in their receiving object, which they have declared as `Array`, so that is what Swift shows when they print `audios` . What they *sent* to this class was an `Array`. They need to fix the typing on the receiver property as I explained. They already have the struct, they are just "losing" it by using `Any` – Paulw11 Sep 10 '18 at 20:55
  • `demoApp.Audiofile(...)` in itself is `Array` – ielyamani Sep 10 '18 at 20:56
  • No, it's not. You are suggesting that `audios` is *actually* `[[Any]]`, but you don't have key-value pairs in an array. – Paulw11 Sep 10 '18 at 20:58
  • The OP says that `type(of: demoApp.Audiofile(...))` gives them `Array`. – ielyamani Sep 10 '18 at 21:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179770/discussion-between-paulw11-and-carpsen90). – Paulw11 Sep 10 '18 at 21:04
-1

The reason you get the error is Any is an object with no definition. You can cast it into an AudioFile like this:

let audiofiles = audios[indexPath.row] as! AudioFile

If audios is an array of AudioFile only then declare it as such and you can use it the way you described.

Stephen O'Connor
  • 1,465
  • 1
  • 10
  • 20
  • You will need a conditional or forced downcast there; you can't use a simple downcast from `Any` to `Audiofile`; it is better, however, to eliminate the use of `Any` altogether. – Paulw11 Sep 10 '18 at 20:39