small medium large xlarge

Avatar_pragsmall
05 Jan 2017, 13:01
Kai Mechel (3 posts)

I am currently at Chapter 9 / Presenting Data with Tables / Creating Table Views

Is there something like a race condition in PodcastFeedParser?

In AppDelegate we create our PodcastFeedParser, which immediately starts to parse the given URL in it’s init(contentsOf url: URL). After that, we set the onParserFinished closure in AppDelegate. What if the parsing is fast enough to finish before the onParserFinished closure was set?

Would it be safer if we extract the parsing in it’s own function, so we can start the parsing after the closure is set?

N614592285_2406_pragsmall
06 Jan 2017, 02:44
Chris Adamson (345 posts)

Yeah, there’s maybe a little slight-of-hand here, since the init is going to throw things over to URLSession, and that’s always going to operate on another thread and return control to the calling thread immediately, so it’s a realistic certainty that we’ll be able to set the closure before it’s needed.

But, in a program-proving sort of way, you’re right — if the init were written in such a way that it blocked until it downloaded the URL and parsed it, this wouldn’t work.

As you say, one option would be to not automatically kick off the download in PodcastFeedParser.init(), and instead give that class a method like start() to manually do it. Another would be to take the closure as an argument to init, so that it would be impossible to have a PodcastFeedParser that didn’t have an onParserFinished property (this would also allow us to make it non-optional). But that’s still kind of a weird code smell, and the start() might be clearer.

I’ve got a lot to do to get the final edition out in the next week or so, but this might be a change worth making if I can get the time…

Kubi_small_pragsmall
16 Apr 2017, 22:49
Wolfgang Kubisiak (2 posts)

You could detect race conditions if you introduce a var Bool finished in PodcastFeedParser and set it to true in parserDidEndDocument

I added a unit test before going on reading Creating Table Views just to make sure I had no error in my parsing code leaving some fields unset and I experienced the same problematic occurred there ( you need to make parser an optional var in AppDelegate to use this code):

func testXMLParsingResults_WhenParsingFinished_AllFilled() {
    guard let myAppDelegate = UIApplication.shared.delegate as? AppDelegate else {
        XCTFail("Couldn't get app delegate")
        return
    }

    startedParsingExpectation = expectation(description: "player starts playing when tapped")
    startedParsingTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(timedParsingChecker), userInfo: nil, repeats: true)
    waitForExpectations(timeout: 10.0, handler: nil)

    guard let parser = myAppDelegate.parser else {
        XCTFail("Couldn't get parser")
        return
    }

    XCTAssertNotNil(parser.currentFeed)
    if let podcastFeed = parser.currentFeed  {
        XCTAssertNotNil(podcastFeed.title)
        XCTAssertFalse((podcastFeed.title?.isEmpty)!)
        XCTAssertNotNil(podcastFeed.description)
        XCTAssertFalse((podcastFeed.description?.isEmpty)!)
        XCTAssertNotNil(podcastFeed.link)
        XCTAssertNotNil(podcastFeed.iTunesAuthor)
        XCTAssertFalse((podcastFeed.iTunesAuthor?.isEmpty)!)
        XCTAssertNotNil(podcastFeed.iTunesUmageURL)
        XCTAssertGreaterThanOrEqual(podcastFeed.episodes.count, 1) // expected: around 22
        for episode in podcastFeed.episodes {
            XCTAssertNotNil(episode.title)
            XCTAssertFalse((episode.title?.isEmpty)!)
            XCTAssertNotNil(episode.enclosureURL)
            XCTAssertNotNil(episode.iTunesDuration)
            XCTAssertFalse((episode.iTunesDuration?.isEmpty)!)
            XCTAssertNotNil(episode.iTunesImageURL)
            
        }
    }
}

func timedParsingChecker(timer: Timer) {
    if let myAppDelegate = UIApplication.shared.delegate as? AppDelegate, let parser = myAppDelegate.parser, parser.finished {
        startedParsingExpectation?.fulfill()
        startedParsingTimer?.invalidate()
    }
}
You must be logged in to comment