0

Using this answer, I would like to assign a string to a variable in my ViewDidLoad, as I will use this array to populate a table.

let downloads : String?

// Get the document directory url
let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

do {
    // Get the directory contents urls (including subfolders urls)
    let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
    print(directoryContents)

    // if you want to filter the directory contents you can do like this:
    let mp3Files = directoryContents.filter{ $0.pathExtension == "mp3" }
    //print("mp3 urls:",mp3Files)
    let mp3FileNames = mp3Files.map{ $0.deletingPathExtension().lastPathComponent }
    //print("mp3 list:", mp3FileNames)

    let downloads = mp3FileNames

} catch {
    print(error.localizedDescription)
}

First, if I print mp3FileNames without the variable, it prints. So the mp3 download extracting code seems good.

Second, when I add the variable for downloads, things break down. XCode flags that I am not using the variable elsewhere even though it reappears in my do loop: Immutable value 'cars' was never used; consider replacing with '_' or removing it

I am new to Swift. What is the proper way to set this variable with the string that will come from mp3FileNames?

5
  • var downloads : String? Commented Feb 13, 2018 at 8:54
  • downloads = mp3FileNames. (remove let) Commented Feb 13, 2018 at 8:54
  • Did u try like this ? Commented Feb 13, 2018 at 8:55
  • @McDonal_11: Doesn't look like it's working. On your line, the error: Variable 'downloads' was never used; consider replacing with '_' or removing it Commented Feb 13, 2018 at 8:57
  • Thats not as error. Its Warning. Commented Feb 13, 2018 at 8:58

3 Answers 3

2

First of all there is a rule:

A variable is only visible in the scope where it has been declared (scope are the pair of braces) and its subscopes.

The local variable

let downloads = mp3FileNames

is declared in the do block so it is not the same object as the variable with the same name on the top level. A local declared variable hides a variable with the same name on a higher level.

There are two other major issues.

  • The variable on the higher level is declared as optional constant. You can't change it!
  • The variable on the higher level is declared as String while mp3FileNames is an array of strings.

If both downloads were the same object you would get compiler errors about those issues.


Solution:

Declare the variable on the higher level as empty array of string

var downloads = [String]()

and assign mp3FileNames to this variable by removing the let keyword

downloads = mp3FileNames
Sign up to request clarification or add additional context in comments.

6 Comments

The variable on the higher level is declared as optional constant. You can't change it! is not true, the compiler is able to detect if the variable is initialized just once in the following control flows.. as an example you can take the code from my answer and use it as a "test" code
OK, I should rephrase, cause the claim is true, but in this case he is not making a change to it, rather initializing it later after it was declared, which is possible..
@MilanNosáľ It's nonsensical anyway. You will get a compiler error in this case because it's not initialized exhaustive (not in the catch scope)
that's why in my answer I included the initialization in catch branch too, so you don't get the error then.. we could argue about non-sensicallity of it, but there are cases in which you want a constant, not a variable, but there is a condition which needs more than a ternary operator (as this case is).. now since we do not know what he is going to do with the downloads variable I believe it is a valid assumption that he really wanted a let constant and not a variable..
I agree there are cases in which you want a constant but in most cases not an optional constant.
|
0

First declare global String var

var downloads: String?
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

Then use func to get all fileNames with mp3 extension

func fileNames() -> String {
    if let directoryContents = try? FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: []) {
        let mp3Files = directoryContents.filter{ $0.pathExtension == "mp3" }
        let mp3FileNames = mp3Files.map{ $0.deletingPathExtension().lastPathComponent }
        return mp3FileNames.joined(separator: ", ")
    }
    return ""
}

And finally

downloads = fileNames()

Comments

0

This is the solution you are looking for:

// not a string, but an array of strings
let downloads : [String]?

let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

do {
    let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
    print(directoryContents)

    let mp3Files = directoryContents.filter{ $0.pathExtension == "mp3" }
    let mp3FileNames = mp3Files.map { $0.deletingPathExtension().lastPathComponent }

    // do not use let, that will create a new variable, just the name of the constant is enough
    downloads = mp3FileNames

} catch {
    // you have to initialize the downloads constant in this control flow branch too, therefore here we assign nil to it
    downloads = nil

    print(error.localizedDescription)
}

// work with downloads
print(downloads)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.