iosxcodeswiftsprite-kiton-demand-resources

Debug why SKScene init(fileNamed:) returns nil


TLTR:

Issue: SKScene subclass's init(fileNamed:)returns nil

Reason: The .sks file does not exist at the time of second initialisation call. The issue somehow related to ODR implementation AND/OR behaviour.

DESCRIPTION:

A game built with SpriteKit. My SKScene subclass's init(fileNamed:)returns nil. I am trying to understand "why?" and "how?" to debug this issue.

My code for initialising SKScene subclass:

/// the hierarchy: SKScene -> TBTBaseScene -> TBTLevelScene

let sceneType: TBTBaseScene.Type = TBTLevelScene.self
let fileName = "Level1"

let scene = sceneType.init(fileNamed: fileName)

Few things to note:

  1. This code works at some point during runtime, but does not at another.
  2. When the target is built via Xcode, going to Products -> AppName.app, right click, "Show in Finder", there are NO Level1.sks. Perhaps it is OK, because the app uses ODR and Level1.sks is marked with "Level1" tag, even though it is in Initial Install Tags
  3. When a breakpoint is set to let sceneType:... line, I could easily create other game scenes via respective subclasses and fileNames, but expression TBTLevelScene(fileName:"Level1") returns nil.
  4. I have tried to check is there Level1.sks in the main bundle, but the code below ALWAYS says "FILE NOT AVAILABLE", for all scenes, so the approach seems to be wrong.
  5. The game's architecture for scene loading is driven from DemoBots Sample Code. You can see similar approach in LoadSceneOperation.swift. The initialisation there, though, works fine in all cases.

The code I use to check the "Level1" or "Level1.sks" exist, based on this answer:

let path = NSBundle.mainBundle().bundlePath
let url = NSURL(fileURLWithPath: path)
let filePath = url.URLByAppendingPathComponent(fileName).absoluteString
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath) {
    print("FILE AVAILABLE")
} else {
    print("FILE NOT AVAILABLE")
}

UPDATE:

Approach for checking files provided by Michael Dautermann does work and it proofs the reason: The Level1.sks does not exist during second attempt to load it.

The Level1.sks file in the project is added to "Copy Bundle Resources" area and has "Level1" ODR tag. The tag is in Initial Install Tags prefetched group.

There is definitely some bug in ORD implementation which disposes the resource (Level1.sks) when it is not needed and does not load it back again at the time it is required. I will keep moving to figure it out. (Seems like, SO community has nothing to do with that so far)


Solution

  • A few things are going on here, but ultimately what's happening is that it sounds like your "Level1" file isn't ending up in the built application.

    1)

    Your file existence checking code up there is mixing file URL's and file paths too much. Change your code to look like this:

    // if nodePath is nil, then it's not found in the bundle
    if let nodePath = NSBundle.mainBundle().pathForResource("Level1", ofType: "sks")
    {
        print("FILE AVAILABLE")
    } else {
        print("FILE NOT AVAILABLE")
    }
    

    2)

    Make sure your .sks file is being copied into your built app.

    Open your target's "Build Phases" section and click on the "Copy Bundle Resources" area. Make sure your .sks file is included in there.