Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift NSSet & CoreData

I am trying to move an objective C & CoreData app to Swift and iOS and hit a brick wall with iterating through NSSet objects:

Xcode has generated these classes:

class Response: NSManagedObject {
    @NSManaged var responseText: String
    @NSManaged var score: NSNumber
    @NSManaged var answers: NSSet
    @NSManaged var question: Question
}

class Question: NSManagedObject {
    @NSManaged var questionText: String
    @NSManaged var questionType: String
    @NSManaged var qualifier: Qualifier
    @NSManaged var responses: NSSet
}

then in the 3rd class I am adding a method that needs to iterate though an NSSet..

class Answer: NSManagedObject {

    @NSManaged var evaluation: Evaluation
    @NSManaged var response: Response

    func scoreGap() -> Int {
        var max = 0
        for aresponse in self.response.question.responses {

            // this next line throws an error
            var scoreVal = aresponse.score.integerValue

            if scoreVal > max { max = scoreVal }
        }
        return max - self.response.score.integerValue
    }
}

The line after the comment above gives and error "AnyObject does not have a member named score"

The Objective-C method looks like this and works fine:

// returns the gap between the score of this answer and the max score
-(NSNumber *)scoreGap
{
    long max = 0;
    for(Response *aresponse in self.response.question.responses) {
        if(aresponse.score.longValue > max) max = aresponse.score.longValue;
    }   
    max = max - self.response.score.longValue;
    return @(max);
}

This is about day three on Swift so I may well be missing something really obvious... but at the moment I really don't get it !

like image 905
Ian33 Avatar asked Nov 23 '14 16:11

Ian33


People also ask

What is NSSet in Swift?

NSSet declares the programmatic interface for static sets of distinct objects. You establish a static set's entries when it's created, and can't modify the entries after that. NSMutableSet , on the other hand, declares a programmatic interface for dynamic sets of distinct objects.

What's a difference between NSArray and NSSet?

The main difference is that NSArray is for an ordered collection and NSSet is for an unordered collection. There are several articles out there that talk about the difference in speed between the two, like this one. If you're iterating through an unordered collection, NSSet is great.

What is Nsdictionary in Swift?

An object representing a static collection of key-value pairs, for use instead of a Dictionary constant in cases that require reference semantics.


2 Answers

for aresponse in self.response.question.responses {
    // this next line throws an error
    var scoreVal = aresponse.score.integerValue

The problem is that for aresponse in ... responses is iterating through an NSSet. Swift doesn't know what is in the NSSet, so it treats each value aresponse as an AnyObject, which has no properties. Thus, the compiler complains when you try to get the score property of aresponse.

You, on the other hand, know that each value aresponse is a Response. So you need to tell Swift this, by casting (with as). Note that you were in fact doing that in your Objective-C code:

for(Response *aresponse in self.response.question.responses)

But you have forgotten to do the same thing in Swift. You can do it in various ways; for example you could write this sort of thing:

for aresponse in self.response.question.responses {
    var scoreVal = (aresponse as Response).score.integerValue

That gets us past the compiler, which now shares in our knowledge of what this variable really is.

Once you get used to this, you will be kissing the ground that Swift walks on. All this time, in Objective-C, you were making assumptions about what kind of object you had, but in fact what you had was an id and the compiler was letting you send any message at all to it - and so, you would often crash with "unrecognized selector". In Swift, an "unrecognized selector" crash is basically impossible because the type of everything is specified and the compiler won't let you send an inappropriate message to an object in the first place.

like image 134
matt Avatar answered Nov 15 '22 07:11

matt


Actually I've discovered something that might be useful. As of version Xcode 7.2 and Swift 2.0 the following declaration works:

@NSManaged var responses: Set<Response>

I think it might be related with the fact that Swift automatically bridges between NSSet and NSArray. I haven't found this documented but it's working for me and I would appreciate if someone confirms it.

like image 24
FranMowinckel Avatar answered Nov 15 '22 08:11

FranMowinckel