Adrian
Adrian

Reputation: 16715

Singleton not working as desired

I have a Core Data project where I'm retrieving data from multiple sources. Not all the data will be saved, so I created a struct to pass the data around before it's saved.

struct SearchResult {
    var myURL: String?
    var myHeadline: String?

    init() {}

    init(myURL: String, myHeadline: String) {
        self.myURL = myURL
        self.myHeadline = myHeadline
    }
}

Since I want to display sections in a UITableView, I created a struct for that, as well:

struct ResultsObjects {
    var sectionName: String?
    var sectionObjects = [SearchResult]()

    init() {}

    init(sectionName: String) {
        self.sectionName = sectionName
    }
}

Lastly, I created a singleton to keep hold SearchResult objects prior to saving them in the managedObjectContext.

class SearchResultsArray {
    static let sharedInstance = SearchResultsArray()
    var resultsObjectsArray = Array<ResultsObjects>()
    fileprivate init() {}
}

My objective is to have the SearchResultsArray class accessible via multiple classes and be able to dump ResultsObjects into it in one class and see ResultsObjects in another.

On MyViewController, I'm instantiating the SearchResultsArray as follows:

var resultsObjectsArray = SearchResultsArray.sharedInstance.resultsObjectsArray

I have a UITableViewController that I use resultsObjectsArray to populate.

On MyViewController, a button calls a method that uses a method in MyParserClass to parse web pages. This works, too.

MyParserClass declares a resultsObjectsArray in the same manner as MyViewController:

var resultsObjectsArray = SearchResultsArray.sharedInstance.resultsObjectsArray

In MyParserClass, a method is called that creates SearchResult objects, dumps them into ResultsObjects and appends them to SearchResultsArray.resultsObjectsArray. This works fine. I threw in the following line in the method that creates ResultsObjects that are dumped into resultsObjectsArray:

print("\(resultsObjectsArray.count) ResultsObjects in SearchResultsArray")
delegate.updateSearchResults()

I threw a print statement in MyViewController's updateSearchResults delegate method and it returns 0, regardless of what MyParserClass says was in there just prior to calling the delegate method.

The app doesn't crash, but it seems like I've got a problem with the singleton. Any suggestions re: what I'm doing wrong are greatly appreciated.

Upvotes: 1

Views: 329

Answers (2)

Paulw11
Paulw11

Reputation: 114846

Arrays are value types in swift; when you assign an array to a variable and then assign that variable to another, the second variable refers to a copy of the original array, not the original array (technically, for efficiency, the array is copied on modify, not on assignment but the result is the same), so changes made to the original array will not be reflected in the second variable's array.

Consider

var someArray = [Int]()
var someOtherVariable = someArray
someArray.append(2)

someArray will contain a single value while someOtherVariable will still be empty.

One solution always use the SearchResultsArray.sharedinstance.resultsObjectsArray rather than local variable/properties.

Perhaps a better approach would be to encapsulate the array in your singleton class by providing some functions to manipulate it. For example;

class SearchResultsArray {
    static let sharedInstance = SearchResultsArray()
    var resultsObjectsArray = Array<ResultsObjects>()

    public var results: [ResultsObjects] {
       get {
           return resultsObjectsArray
       }
    }


    public func add(results: ResultsObjects) {
        resultsObjectsArray.append(results)
    }


    fileprivate init() {}
}

Since results is now a read-only property and a separate add function exists, the class more clearly defines its semantics.

Upvotes: 1

Russell
Russell

Reputation: 5554

I use a singleton in a very similar way - but I define it quite differently.

EDIT - differently, and now superseded :-(

Within the singleton DataModelInstance in my case, I have this

class DataModelInstance : NSObject, NSCoding
{
    // define all of the variables I need within the singleton

    class var sharedInstance : DataModelInstance
    {
        struct Singleton
        {
            static let instance = DataModelInstance()
        }
        return Singleton.instance
    }

    // everything else that I need in the class...
}

and then, in every class that needs to access it, I define a variable

    var dataModel = DataModelInstance.sharedInstance

Upvotes: 0

Related Questions