Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2016-12-29

The defer statement | Swift gotcha

I love the defer statement. Use it all the time...

(Note: The defer statement allows you to execute a piece of code just before the scope the statement is in closes)

However today I ran into an inexplicable problem where a file read operation would always fail. And as so often I simply could not see the error until I stepped through. The error turned out to be quite simple: a defer statement at the wrong place.

This was the offending code:

let path = "/Users/Home/Me/Desktop/file.txt"

let file = fopen(path, "r")

if file == nil {
    print("Error opening file at \(path)")
} else {
    defer { fclose(file) }
}

let result = read(from: file)

...

Do you spot the error?

Right! the defer statement is executed immediately because the end-of-scope is the "}" closing the "else" part.

So the file was already closed when the read operation was called.
The solution is obvious:

let path = "/Users/Home/Me/Desktop/file.txt"

let file = fopen(path"r")

if file == nil {
    print("Error opening file at \(path)")
}
defer { fclose(file) }

let result = read(from: file)

...

The rule of thumb is clear: Always place the defer stement at the same "level" or "indent" as the variable it refers to. Which -to be clear- simply means "the same scope".

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2016-12-21

Value of optional type Bool not unwrapped | Swift Gotcha

Ever come across the following error message?



It occurs when we do something like this:

      [weak self]
      ....
      if self?.isValidPrivateKey(atPath: keyfilepath) {

The "Fix-it"solution from xcode is the following:

      if (self?.isValidPrivateKey(atPath: keyfilepath))! {

And indeed the error message goes away, everything fine?

Nope, don't let that fool you. The entire purpose of the weak "self?" reference is now gone. When self is indeed nil you will get an exception at runtime.

The proper thing to do is the following (choosing either "false" or "true" based on the case at hand:

     if self?.isValidPrivateKey(atPath: keyfilepath) ?? false {

Now the choice of what to do if self is indeed nil is made explicit and won't cause any problems at runtime.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2016-12-13

Implementing a delay in Swift

This is a simple post: we often need  delay in our software, so how to do that in Swift now dispatch_after has been changed beyond recognition?

There are multiple possibilities: use the "Thread.sleep" or use a timer instance are two of the often used. But both have drawbacks, the 'sleep' statement delays the actual thread that our code is running on, which degrades performance. And using (NS) Timer is too complex.

When I first looked at DispatchQueue.asyncAfter I hit a brick wall with doing arithmetic on DispatchTime or DispatchWallTime.

A second look at the Dispatch class showed the proper way to do this: simply add the delay not as a double but as a DispatchTimeInterval.

This code is so easy, it does not even need a wrapper:

        DispatchQueue.main.asyncAfter(
            deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(250),
            execute: {
                ...
            }

        )

Or even simpler:

        DispatchQueue.main.asyncAfter(
            deadline: .now() + .milliseconds(250),
            execute: {
                ...
            }
        )

Instead of milliseconds we can also choose from seconds, microseconds or even nanoseconds.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2016-12-05

A general purpose progress bar with label | Swift Code Library

Its been a while as I have been tackling one "urgent" thing after another. No time for blog updates. While I am not assuming that this will change soon, I do have a worthwhile addition to the Swift Code library.

In my current iOS project I needed a progress bar with an associated label. This tracks the rate at which messages are being transferred/received over the internet. Since there is a clear label associated with each message this label should be displayed along with the update of the progress bar. The amount of messages is variable and can even be just one. To give the user useful feedback it was decided that each message should be visible for a small amount of time. Enough to recognize the message, but no more than necessary.

The following class was created:

    final class ProgressBarWithLabel {
        
        struct Info {
            let progress: Float?
            let label: String?
        }
        
        var info: Array<Info> = []
        let timeBetweenUpdates: DispatchTimeInterval
        var timeOfPreviousUpdate: DispatchTime = DispatchTime.now()
        let progressBar: UIProgressView
        let progressLabel: UILabel?
        let mainQueue = DispatchQueue.main // Must be main because the GUI is updated from here
        
        init(progressBar: UIProgressView, progressLabel: UILabel?, timeBetweenUpdates: DispatchTimeInterval) {
            self.timeBetweenUpdates = timeBetweenUpdates
            self.progressBar = progressBar
            self.progressLabel = progressLabel
            self.progressBar.progress = 0.0
            self.progressLabel?.text = ""
        }
        
        func add(info: Info) {
            
            mainQueue.async {
                
                [weak self] in
                
                // Add the new info as the last to be displayed
                self?.info.insert(info, at: 0)
                
                // Make sure there will be an update of the gui
                self?.update()
            }
        }
        
        private func update() {
            
            // Check if a new update can be made
            guard DispatchTime.now() >= timeOfPreviousUpdate + timeBetweenUpdates else {
                

                if info.count > 1 { return }

                // Wait until the necessary delay has expired then try again
                mainQueue.asyncAfter(
                    deadline: timeOfPreviousUpdate + timeBetweenUpdates,
                    execute: {
                        [weak self] () -> () in
                        self?.update()
                    }
                )
                return
            }
            
            // An update will be made, set the time of "previous" to current time
            // This could also be done after the factual update, but doing it before the info test can prevent "gui glitches" if somebody modifies the code after the test. (Defensive Programming)
            timeOfPreviousUpdate = DispatchTime.now().uptimeNanoseconds
                
            // Fetch info to be displayed
            guard let forDisplay = info.popLast() else { return }
            
            // Update GUI, reset the GUI if there is no new data
            progressBar.progress = forDisplay.progress ?? 0.0
            progressLabel?.text = forDisplay.label ?? ""
            
            // If there is more info to be displayed, wait a little
            if info.count > 0 {
                mainQueue.asyncAfter(
                    deadline: timeOfPreviousUpdate + timeBetweenUpdates,
                    execute: {
                        [weak self] () -> () in
                        self?.update()
                    }
                )
            }
        }

    }

Note that this class does not contain the GUI elements itself, they must be specified when an instance is created. Since these GUI elements are assigned by the runtime, special care should be taken to ensure that they are not deallocated when the view goes out of sight and a possible low-memory condition occurs.

I used the following code that ensures the cell stays allocated as long as the table is in use:

var progressCell: FetchAutoQuotesCell?

.... and then in tableView_cellForRowAt:


let cell = progressCell ?? tableView.dequeueReusableCell(withIdentifier: "ProgressCell") as? ProgressCell

if progressCell == nil {
    progressCell = cell
    progressBarWithLabel = ProgressBarWithLabel(progressBar: (cell?.progressBar)!, progressLabel: (cell?.progressLabel)!, timeBetweenUpdates: DispatchTimeInterval.milliseconds(250))
}

This may be overkill, but it won't hurt. (The assignment of the GUI elements themselves is probably enough to keep the cell from being deallocated. OTOH, it might not be enough to ensure that the same cell is reused again and again under all conditions)

Usage of the ProgressBarWithLabel class is relatively easy. The initialization was already shown, the only other call is the add function.

It can be called as follows:

progressBarWithLabel?.add(info: ProgressBarWithLabel.Info(progress: (Float(count) / Float(totalCount)), label: message))

To ensure that the progress indicators are reset, call the add function with an empty Info struct.


Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.