1

I am trying to condense an application I am working on that involves a large directory with lots of UIViewControllers with UITableViews.

My plan is to rewrite the application that will use one-reusuable UIViewController with UITableView that will load in data from the txt files as needed at/after runtime. The txt files will be read at/after runtime instead of having to build all of the swift files before hand.

Currently, my application takes several minutes to build/compile everytime I make a change to the project. Therefore, I am hoping by reading and parsing the txt files as needed, I will be able to build the application within just a few seconds.

Below is my attempt to read and generate the necessary "candies" array using using the example text file provided below (candyTextFile.txt). Please see the commented lines in the class, MasterViewController, for the error message and the code that I am trying to output the candies array as.

Note, I have shortened the lines in my text file down to three lines. My final application will have many of similar text files with each one expanding several hundred lines long.

I am still relatively new to Swift so I apologize if I am missing something simple here and if I am using/not-using the appropraite terminology.

I belive the term CandySearch is just from the project name ( see here ). I am not sure why it is getting outputted though in the candies array. Also, I do see now that my current myArray is established as a class. How can I turn it into an array?

Here is my relevant code in my MasterViewController.swift,

class MasterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    var candies = [myArray]()
    var evenIndicies = [String]()
    var oddIndicies = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        setUpArray()
    }

    private func setUpArray() {
        if let filepath = Bundle.main.path(forResource: "candyTextFile", ofType: "txt") {
            do {
                // let contents = try String(contentsOfFile: "candyTextFile", encoding: String.Encoding.utf8)
                let contents = try String(contentsOfFile: filepath)
                let lines_separatedBy_n : [String] = contents.components(separatedBy: "\n")
                let string = lines_separatedBy_n.map { String($0) }.joined(separator: ", ")
                print("string: \(string)")
                let lines_separatedBy_comma : [String] = string.components(separatedBy: ", ")
                print("lines_separatedBy_comma: \(lines_separatedBy_comma)")

                for (index, element) in lines_separatedBy_comma.enumerated() {
                    if index % 2 == 0 {
                        evenIndicies.append(element)
                        print("evenIndicies: \(evenIndicies)")

                    } else {
                        oddIndicies.append(element)
                        print("oddIndicies: \(oddIndicies)")
                    }
                }
                evenIndicies.remove(at: evenIndicies.count - 1) // the -1 removes the nil element, "", in the array. For some reason, there is a fourth element, "", in the evenIndicies array. Therefore, I remove it by subtracting one index so I get the three indexes. My in project txt file is four lines long where the fourth line is empty. I think this is why it is showing up "" for the fourth index.
                print("evenIndicies outside for-loop: \(evenIndicies)")
                print("oddIndicies outside for-loop: \(oddIndicies)")
                candies = [myArray(category: evenIndicies, name: oddIndicies)]  // 

                print("candies: \(candies)") 
           // This prints as the following:
           // candies: [CandySearch.myArray]

           // HOWEVER, I am trying to get it to print as: 
           // [CandySearch.myArray(category: "Chocolate", name: "Chocolate Bar"), CandySearch.myArray(category: "Chocolate", name: "Chocolate Cookie"), CandySearch.myArray(category: "Hard", name: "Lollipop")]




            } catch let error as NSError {
                print(error.localizedDescription)
            }
        }
    }
}

class myArray {
    let category: [String]
    let name: [String]
    init(category: [String], name: [String]) {
        self.category = category
        self.name = name
    }
}

In my text file, candyTextFile.txt, I have

Chocolate, Chocolate Bar
Chocolate, Chocolate Cookie
Hard, Lollipop
5
  • 1
    Please show the code for Candy class. You define candies to be an array containing myArray types and then try to populate it with a single instance of type Candy hence the error message. Commented Jan 3, 2019 at 4:11
  • Oh wow, that was a big mistake on my end. Please see the updated code! Thank you all for the help. Commented Jan 3, 2019 at 5:12
  • Sorry if I was unclear in my previous comment. The issue is still not resolved. Commented Jan 3, 2019 at 20:19
  • Can you update your answer to remove/explain what CandySearch is in your commented print statements? Irrespective, you are still populating candies with a single instance of myArray which is a class not an array. Your expected print output seems a little off the mark. The category and name properties are not related in any way, beyond being properties of the myArray instance. I mean for instance the elements of each array aren’t paired as in your expected print output. Commented Jan 3, 2019 at 21:10
  • Okay I added a paragraph near to the middle of my original post regarding where I think CandySearch comes from and how I can convert myArray into an array to hold the properties category and name Commented Jan 3, 2019 at 21:54

1 Answer 1

1

I'll take a stab at this assuming that what you want to do is take the info from the text file and end up with an array of your candy products. At the moment you have your myArray class that looks a little bit like some Frankenstein thing. It contains all of the categories and names in two separate arrays inside a single instance of that class. Then you put it into an array called candies. That all looks like a dead-end unless I'm misunderstanding your requirements. (Maybe I am.) But assuming I'm at least half correct...

You want an array of candies so let's make a new class to hold a single candy.

class Candy {
    let category: String!
    let name: String!

    init(category: String, name: String) {
        self.category = category
        self.name = name
    }
}

This is similar to what you had already but when you instantiate an instance of Candy it will hold the details of a single candy. Note class names start with a capital letter by convention.

You want to end up with an array of candies so let's change the definition of your candies array.

var candies: [Candy] = []

All that's left is to change your for in loop to extract one candy's worth of data (category and name) per loop, create a new Candy instance and populate it before adding it into candies.

for (index, element) in lines_separatedBy_comma.enumerated() {
   var newCategory = ""
   var newName = ""
   if index % 2 == 0 {
      newCategory = element
   } else {
      newName = element
   }
   let newCandy = Candy(category: newCategory, name: newName)
   candies.append(newCandy)
}

You don't have any checking in your code that would handle any errors/bad formatting in your text file so I'll assume you are absolutely certain that it is composed of pairs of data with no loose ends, etc. I see you remove the last element of evenIndices for some reason. I'm not sure why so I haven't handled that.

Now your candies array should hold a bunch of Candy objects, so to list them to the console you can do something like this.

for candy in candies {
   print("category: \(String(describing: candy.category)), name: \(String(describing: candy.name))")
}

Let me know if I'm still missing the mark.

EDIT ******************************************************** EDIT

I took a look at the RW tutorial you linked.

The tutorial uses a struct for the Candy element whereas you (and I) have used a class. I guess you're still learning about Swift's value and reference types so won't muddy the water here further. Suffice to say a struct would probably be better in this instance. print also handles them differently and seems able to look inside structs for the values, whereas unless you explicitly extract the properties (as in my for in print statement, it just gives you a list of the classes as we've seen.

I've also sorted the logic (it's ugly and you shouldn't do it this way) so you can at least see that it does work (after a fashion). The whole setupArray method:

private func setupArray() {
     if let filepath = Bundle.main.path(forResource: "candyTextFile", ofType: "txt") {
         do {
             let contents = try String(contentsOfFile: filepath)
             let lines_separatedBy_n : [String] = contents.components(separatedBy: "\n")
             let string = lines_separatedBy_n.map { String($0) }.joined(separator: ", ")
             var lines_separatedBy_comma : [String] = string.components(separatedBy: ", ")

             // I've put this in to remove the last bit of the file that was causing the count to be one too high.
             // I'm guessing that's why you had something similar previously?
             lines_separatedBy_comma.removeLast()

             for (index, element) in lines_separatedBy_comma.enumerated() {
                 if index % 2 == 0 {
                     let newCategory = element
                     let newName = lines_separatedBy_comma[index + 1]
                     let newCandy = Candy(category: newCategory, name: newName)
                     candies.append(newCandy)
                 }
             }
             for candy in candies {
                 print("category: \(candy.category!), name: \(candy.name!)")
             }
             print("\ncandies: \(candies)")
         } catch let error as NSError {
             print(error.localizedDescription)
         }
     }
 }

When I use:

class Candy {
    let category: String!
    let name: String!

    init(category: String, name: String) {
        self.category = category
        self.name = name
    }
}

the output is:

category: Chocolate, name: Chocolate Bar
category: Chocolate, name: Chocolate Cookie
category: Hard, name: Lollipop

candies: [FileTest.Candy, FileTest.Candy, FileTest.Candy]

When I use: (after removing the force unwrapping from the print statement)

struct Candy {
    let category : String
    let name : String
}

the output is:

category: Chocolate, name: Chocolate Bar
category: Chocolate, name: Chocolate Cookie
category: Hard, name: Lollipop

candies: [FileTest.Candy(category: "Chocolate", name: "Chocolate Bar"), FileTest.Candy(category: "Chocolate", name: "Chocolate Cookie"), FileTest.Candy(category: "Hard", name: "Lollipop")]

Can you see that your original code placed everything inside one myArray but the code above (and on the RW site) creates individual Candy elements and stores all of them into candies? It's a fundamental difference in approaches.

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

5 Comments

Your solution is outputing the same thing as mine when I call the print statement, print("candies: \(candies)"), except yours prints out newCandy: [CandySearch.Candy, CandySearch.Candy, CandySearch.Candy, CandySearch.Candy, CandySearch.Candy, CandySearch.Candy, CandySearch.Candy]. This is not what I want. I am trying to get the print statement to print out as [CandySearch.myArray(category: "Chocolate", name: "Chocolate Bar"), CandySearch.myArray(category: "Chocolate", name: "Chocolate Cookie"), CandySearch.myArray(category: "Hard", name: "Lollipop")]. Sorry for any confusing but I think
we are on the right track. I am trying to produce the candy array contents in this tutoral (raywenderlich.com/…). Sorry for leaving this out earlier. If you call print("candies: \(candies)") in the linked tutorial, it outputs candies: Candy(category:"Chocolate", name:"Chocolate Bar"), Candy(category:"Chocolate", name:"Chocolate Chip"), Candy(category:"Chocolate", name:"Dark Chocolate"), Candy(category:"Hard", name:"Lollipop") and so forth to the end. I am trying to mimic this behavior, but by reading in a text file.
Your answer is still very helpful in debugging this issue so thank you for your time. My apologies again for any confusion.
Note, I believe your newCandy is equivalent to my candies in my top comment for this answer.
Please take a look at the 'big' edit to my original answer.

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.