the debug report

iOS development: new pandemic skills

Here’s yet another suggestion in the sea of unsolicited advice during this pandemic 2020 – learn to code iOS apps. Trust us, we’re speaking from experience. We’re not full-time “coders”. We’re full-time friends, husbands, wives, and entrepreneurs. While some of us our fortunate enough to work from home during this weird time, others aren’t. Whatever the circumstances, we’ve always more time on our hands than we believe. Whether you have 15 minutes every other day or 1 hour every night, dedicate some of that time to iOS development – you’ll thank yourself later.

In fact, research suggests that chunking is a more effective means to learning. That is, the opposite of cramming – spending small, consistent chunks of time yields more results than large, uninterrupted sessions.

In any case, we’d thought we’d share two courses from Udemy that are absolutely invaluable when it comes to picking up iOS development quickly. In fact, these two resources allowed members of our team to make remarkable improvements to their diverse skill set (again, we’re not all full-time coders). The courses aren’t expensive when discounted (we paid $10 per course per person). Promotions are run fairly frequently, so keep checking the website.

Finally, there’s no need for a disclaimer here. We get no referrals or kick-backs of any sort for recommending these videos. We only get the satisfaction of reviewing quality work, recognizing quality work, and hopefully, converting some of you to coders. If we can do it, anyone can do it – seriously.

So, if there was ever a time to pick up iOS development (as a hobby, a side-gig, or career pivot), now is the time.

iOS 13 & Swift 5 – The Complete iOS App Development Bootcamp

Angry Birds, Crossy Road & more: Game Development in Swift 4

swift 5: using plists to store data

Using plists to store data in Swift doesn’t get much attention on the web. Although not the most elegant (or recommended) solution to store data, reading from and writing to property lists is a convenient way to store small bits of data. For example, simple plists can be used to store level data for games.

In the image above, we’ve generated a level set using an external program (think something like python or even Excel). The plist can be dragged-in to any Xcode project and referenced via a few simple lines of code.

Note: you cannot modify a plist included in the main bundle (i.e. dropped into the project as a resource). You’ll need to copy the file over into the users’ documents directory.

Modifying the plist is pretty straightforward. First, check to see if the plist exists in the documents directory. If not, copy it over, otherwise, reference the file.

func reloadData() -> Bool{
        if (try? levelDataURL().checkResourceIsReachable()) == nil {
            guard let originalFile = Bundle.main.url(forResource: Constants.kLevelDataFileName, withExtension: ".plist") else {
                fatalError("Could not locate level data")
            }
            
            do {
                let originalContents = try Data(contentsOf: originalFile)
                try originalContents.write(to: levelDataURL(), options: .atomic)
                print("Made a writable copy of the level data at \(levelDataURL())")
                return reloadData()
            } catch {
                print("Couldn't copy level data to documents directory")
                return false
            }
        } else {
            if let data = try? Data(contentsOf: levelDataURL()) {
                let decoder = PropertyListDecoder()
                do {
                    levels = try decoder.decode([Level].self, from: data)
                } catch {
                    print("Error loading level data")
                    return false
                }
            }
        }
        return true
    }

You’ll notice a [Level].self reference inside the decoder.code function. In order to decode the property list, you’ll need to define a class that represents the object in the list. Level is the Codable class that can be mapped to the plist.

class Level: Codable {
    var number: Int = 0
    var completed: Bool = false
    var available: Bool = false
    var score: Int = 0
    var speed: Float = 0.0
    var radius: Float = 0.0
    var feedspeed: Int = 0
    var path: [Int] = []
    var time: Int = 0
    var music: String = ""
    var musicStartTime: Int = 0
    var specials: [String] = []
    var orbital_types: [String] = []
}

Now that we’ve successfully copied over the plist and created an object that describes the each element in the plist array, we can also modify this copy.

       let encoder = PropertyListEncoder()
        
        do {
            guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
                fatalError("Documents directory not found in application bundle.")
            }
            let url = documentsDirectory.appendingPathComponent("\(Constants.kLevelDataFileName).plist")
            print(url)
            let data = try encoder.encode(newLevels)
            try data.write(to: url)
        } catch {
            print("Error saving results: \(error)")
        }

You’ll notice a newLevels reference in the encoder.encode function. This is an array of Level objects that conform to the plist format. You’ll be required to save the whole plist any time you make changes (even to a single element). It should be pretty apparent why this is pretty clunky and not recommended, however, it’s a convenient way to store small chunks of data that don’t need to be modified often.

Happy coding!

swift 5: sfsymbols tint color and spritekit

Here’s a minor annoyance. Have you tried changing the color of an SF Symbol and initializing an SKTexture only to have the color change fail? In case you’re unaware, iOS 13 introduced SF Symbols.

https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/

These are handy vector graphics for standardized purposes (think using it for an email button, close button, home button, etc). They’re incredibly convenient and consistent with the overall feel of iOS.

In any case, when we tried to add a home button to our iOS game using SpriteKit SKTexture, we noticed the color wouldn’t change. We assume this a bug considering it behaves properly with UIImage.

To cut to the chase, if you’re applying an SF Symbol to an SKTexture and want to change the tint color, the following is a decent work around (although not perfect). Hopefully this gets updated!

        let systemImageName = "house.fill"
        let largeConfig = UIImage.SymbolConfiguration(pointSize: 16, weight:.ultraLight)
        
        guard let systemImage = UIImage(systemName: systemImageName, withConfiguration: largeConfig)?.withTintColor(UIColor.red) else { return }
        guard let systemImageData = systemImage.pngData() else { return }
        guard let systemImageTinted = UIImage(data: systemImageData) else { return }
    
        let texture = SKTexture(image: systemImage)

swift 5: memory leaks and closures

While certainly nothing compared to conventional memory management, one frustrating aspect of finishing up your iOS app is discovering runaway memory issues.

One primary source is with respect to closures. From the Swift Programming Reference Manual:

Closures are self-contained blocks of functionality that can be passed around and used in your code…

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you.

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

This last bolded-statement is technically correct, but not without caution. Closures can capture and store references. The implication being a possible Automatic Referencing Counting Cycle. Put simply, Swift can inadvertently create a referencing loop between the object passed to the closure and the object calling the closure. Below is a diagram illustrating the point:

We’ve run into this issue when using SpriteKit and SKActions. Take for example, the following function:

func run(_ toPoint: CGPoint speed: TimeInterval) {
        let move = SKAction.move(to: toPoint, duration: speed)
        self.run(move) {
            self.jump()
        }
    }

In many cases, calling self.jump() will result in a memory leak given the strong reference. The solution to this memory leak:

func run(_ toPoint: CGPoint speed: TimeInterval) {
        let move = SKAction.move(to: toPoint, duration: speed)
        self.run(move) {
            [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.jump()
        }
    }

And there you have it. A simple solution to a nagging issue we all face at some point. Hope this helps.