Lightning tour of closures in Swift

lightningTourClosures.png

Check out this video tutorial with a lightning tour of closures in Swift. It covers:

  • Functions as data types
  • Basic closure syntax
  • Trailing closures

 

If you’re interested in more of these sorts of videos, I’ll be posting frequently over on the iOS Development with Swift YouTube channel, you can subscribe here. I’ll be posting about closures next!

You could also join the video course and you’ll have access to all 8 hours of video tutorials, exercises and more!

Tagged with: ,
Posted in Swift

Overloading functions with default parameter values

Here’s a Swifty conundrum for you!

First a quick recap. Here’s a simple function with no parameters. Call it and “Hello World” will print to the console.

Screenshot 2019-01-09 13.27.42.png

Here’s another function with the same name. This time it has a parameter with a default value. We can call it in exactly the same way, and the default value will be printed to the console.

Screenshot 2019-01-09 13.27.25.png

Here’s the conundrum.

What if both functions exist, as overloaded functions?

screenshot 2019-01-09 13.18.55

What would we expect to see in the console?

We have two overloaded functions, one with a parameter with default values and another with no parameters. The call to the function could theoretically call either, but the question is, which does the compiler choose to call?

Need some thinking time?

giphy.gif

So! It turns out that the console will display “Hello Mars”!

The rationale seems to be that the function with fewer parameters is called. I’m not able to find a discussion of the Swift team’s reasoning behind this, but it is consistent with the ‘overload resolution’ decision made by the C# compiler. The philosophy is described here in Microsoft docs:

If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call.

(“Optional parameters” in C# parlance are the equivalent of parameters with default values in Swift.)

If you would like a quick recap of overloading functions and default parameter values, check my previous blog post “Lightning tour of functions in Swift“.

Tagged with: , ,
Posted in Swift

Lightning tour of functions in Swift

lightningTourFunctions.png

Check out this video tutorial with a lightning tour of functions in Swift. It covers:

  • Parameter default values
  • Optional function parameters
  • Overloading a function

livevideo-ios-development-with-swift-in-motion (2)

If you’re interested in more of these sorts of videos, I’ll be posting frequently over on the iOS Development with Swift YouTube channel, you can subscribe here. I’ll be posting about closures next!

You could also join the video course and you’ll have access to all 8 hours of video tutorials, exercises and more!

Tagged with: , , , ,
Posted in Swift

iOS Video Course is live!

After many months of work, my video course iOS Development with Swift in Motion has recently launched over at Manning Publications.

If you’re interested in taking on iOS Development, but aren’t sure if you’re ready to take the plunge, I’d say go for it! I’ve developed this course especially for people with some programming experience, so that we can skip past programming basics and get straight to the interesting stuff – what’s new, different and exciting in Swift. After a lightning tour of Xcode and setting up interfaces in Interface Builder, we get onto the main project of the course. We follow the evolution of this app from the germ of an idea right through to building all sorts of interesting features, such as connecting to a web service, displaying data in a table, taking photos, scanning bar codes, searching and sorting data and drawing custom graphics. We finish off by actually distributing the app to the App Store.

I’m excited to share this course with you. I hope you’ll find it useful and helps you launch your own app on the App Store! Good luck with it. You can find more info on the course here.

livevideo-ios-development-with-swift-in-motion (2).png

 

Tagged with: ,
Posted in Swift

flatMap vs compactMap

In Swift 4.1, the flatMap function was divided into two functions. One kept the same name:

flatMap

This method flattens a sequence.

Let’s say we have a two-dimensional array:

let a = [[0,1,2],[3,4,5]]

We can use the flatMap higher order function to ‘flatten’ this into a one-dimensional array:

let b = a.flatMap{$0} //b = [0, 1, 2, 3, 4, 5]

You could flatten a dictionary too – you would just need to specify what you want to flatten the values. Let’s say we have a dictionary of animals, divided into classes:

let animals = [
  "Mammals":["cat", "dog", "alpaca"],
  "Reptiles":["lizard", "goanna", "snake"]
]

You could flatten this into a one-dimensional array of all animals, ignoring class:

let flatAnimals = animals.values.flatMap{$0}
flatAnimals //["lizard", "goanna", "snake", "cat", "dog", "alpaca"]

compactMap

In Swift 4.1, the second function of flatMap is now called compactMap. The compactMap higher order function will remove any nil elements from a sequence. Imagine you have an array of Int optionals that contains both Ints and nil:

let numbers:[Int?] = [0, nil, 1, nil, nil, 2]

You can use the compactMap method to remove any nil values from this array:

let compactNumbers = numbers.compactMap{$0}
compactNumbers  //[0, 1, 2]

You can compact a dictionary too but you’ll need to use the filter method. Here’s a dictionary with String optional values:

let snakes:[String:String?] = ["A":"Anaconda", "B":nil, "C":"Cobra"]

You can use the filter method to

let compactSnakes = snakes.filter {$0.value != nil }
compactSnakes //["A": "Anaconda", "C": "Cobra"]

You might be wondering why the compactMap function doesn’t accomodate dictionary. This will be addressed in Swift 5 with the new compactMapValues function:

let compactSnakes = snakes.compactMapValues {$0}

Here‘s the Swift evolution proposal for compactMapValues.

Tagged with: , ,
Posted in Swift

Xcode 10 keyboard shortcuts cheat sheet

Getting adjusted to keyboard shortcuts takes some work, but they can boost your productivity significantly. I’ve put together a cheat sheet for a number of frequently used keyboard shortcuts in Xcode 10. I recommend you print it out, stick it on your wall, and in no time you’ll have them committed to memory!

keyboardShortcuts.png

Tagged with: ,
Posted in Swift

The Mysterious Case of the Status Bar

All I wanted was two things for my status bar:

  • I wanted it to have white text by default as it was going to be on a dark background
  • I wanted to be able to hide it temporarily every now and again.

Straightforward right?

Not so much… Let’s take a look at the case.

So if you hunt around, there are so many ways to manipulate the status bar, let’s take a look at some:

General Settings of your Target

In the General tab for the preferences for your project’s target, you’ll find two fields that adjust the default behaviour of your status bar :

  • Status Bar Style – choose between Light (i.e. light text) or Default (i.e. dark text)
  • Hide status bar – a checkbox.

TargetSettings.png

These fields are derived from the info plist, which would be an alternative approach for adjusting these fields:

Info plist fields

If you’re using info.plist to adjust the default behaviour of your status bar, you’ll want to add rows for:

  • Status bar is initially hidden (UIStatusBarHidden) – Choose from YES or NO.
  • Status bar style (UIStatusBarStyle). Choose from either:
    • default (UIStatusBarStyleDefault)
    • light (UIStatusBarStyleLightContent)

Ignore that Xcode wants you to choose between Opaque black style and Transparent black style (alpha of 0.5). These are deprecated styles, it appears as though no-one told the property list editor that yet.

InfoPlistSettings.png

You may have noticed I have another status bar related row set up in the info.plist – another Boolean property called View controller-based status bar appearance – I’ll come back to this one in a moment.

So they are the ways to set up the defaults for the status bar style and whether it is hidden or not.

But how about if you want to temporarily change these defaults?

If you look around the internets, you’ll see two alternative solutions mentioned:

Set properties on the AppDelegate

Some will suggest you to set properties on the AppDelegate:

UIApplication.shared.statusBarStyle = .default //Set Style
UIApplication.shared.isStatusBarHidden = false //Set if hidden

These could be set for example in the didFinishLaunchingWithOptions method of your app’s AppDelegate, or anywhere you like really – for example, if you wanted to change this temporarily for a view controller you could call this in the viewDidAppear and viewDidDisappear methods.

Sounds great, right? Just call a method to update the status bar, easy.

Oh wait, sorry. I forgot to mention – the isStatusBarHidden and statusBarStyle properties were deprecated in iOS 9. Bah!

Screenshot 2018-08-23 13.14.50.png

Let’s take a look at the view controller properties that Xcode are suggesting:

Override properties in your view controller classes

Within your view controller classes, you can override the prefersStatusBarHidden and preferredStatusBarStyle properties:

override var prefersStatusBarHidden: Bool {
  return true
}
override var preferredStatusBarStyle: UIStatusBarStyle {
  return .lightContent
}

These methods return values by default (prefersStatusBarHidden = false, preferredStatusBarStyle = .default), so if you change either of these default values, when the user navigates to another view controller, if the new view controller doesn’t have its own implementation of these methods, it will revert to these defaults. This is a difference in behavior to setting properties on the appdelegate, which changes them for the duration of your app, well, at least until you specifically request they change again.

Changing mid view controller

By the way, you can use these properties to make this change mid view controller too. Just set up a variable, let’s call it statusBarHidden, to contain the current preference for the status bar.
We could then return this variable in the view controller property prefersStatusBarHidden:

var statusBarHidden = true
override var prefersStatusBarHidden: Bool {
  return statusBarHidden
}

Now if we change the statusBarHidden property, nothing will happen yet. We need to tell the system that we want the status bar to change. We can do this in a property observer:

var statusBarHidden = true {
  didSet(newValue) {
    setNeedsStatusBarAppearanceUpdate()
  }
}

Now we should be able to change the statusBarHidden property, and the status bar will automatically update. For example, we could have a button that toggles the status bar, connected to an IBAction:

@IBAction func toggleStatusBar(_ sender: Any) {
  statusBarHidden.toggle()
  //If you're pre Swift 4.2, use: statusBarHidden = !statusBarHidden
}

What do we have so far?

So, let’s summarize what we’ve got:

  • General Settings of your Target / Info plist fields
  • Set properties on the AppDelegate (deprecated, remember)
  • Override properties in your view controller classes

Here’s the stinger in the tail. Remember that View controller-based status bar appearance property we saw in the info plist file? Which options are available to us, depends on what we choose in this field:

If we have View controller-based status bar appearance set to NO, we can make a change to the default status bar in the general settings or info plist. We could also set properties on the AppDelegate, but don’t forget, that property is deprecated.

If we have View controller-based status bar appearance set to YES, we can override properties in our view controller classes.

Back to the problem…

Remember my original problem?

  • “…white text by default…” – the obvious way to do this would be change defaults in General Settings / Info.plist
  • “…hide it temporarily…” – the obvious way to do this would be simply to override properties in a view controller class.

Only problem is, these two solutions are mutually exclusive, I can’t set View controller-based status bar appearance to both NO and YES… Setting properties on AppDelegate would have been a good solution to this problem, but this technique is deprecated.

Which leaves me with the only solution of setting the style of the status bar on each view controller. A bit of a pain!

You could set up a subclass of UIViewController that manages this for you, and then every ViewController in your project would then have to implement this class…

class UIViewControllerWithLightStatusBar:UIViewController {
  override var preferredStatusBarStyle: UIStatusBarStyle {
    return UIStatusBarStyle.lightContent
  }
}
...
class ViewController: UIViewControllerWithLightStatusBar {
...

But this is still an inconvenience and feels a little hacky. If anyone knows of a reliable and safe way to change the status bar behavior project-wide while having View controller-based status bar appearance set to YES, I’m interested to hear it!

Navigation Controllers

I’m afraid we’re not done on the mysterious case of status bars – embed your view controller in a navigation controller, and if you have View controller-based status bar appearance set to YES, suddenly the navigation controller now takes over control of the style of the status bar, and your view controller’s preferredStatusBarStyle property will be ignored.

If you want to adjust the style of the status bar in a navigation controller, you’ll need to do this in the navigation bar’s style property, either in code, eg:

override func viewDidAppear(_ animated: Bool) {
  navigationController?.navigationBar.barStyle = .black
}

Or in the storyboard:
Screenshot 2018-08-23 13.40.18.png

To make things ultra-confusing, if you want, for example, that your status bar has white text, view controllers not embedded in a navigation controller will need to request the .lightContent style (i.e. light colored writing), and view controllers embedded in a navigation controller will need to request the .black style (i.e. a black colored nav bar).

Don’t forget, if you have View controller-based status bar appearance set to NO, any changes you make to the navigation bar style will be ignored.

In summary

For View controller-based status bar appearance = NO:

  • General Settings of your Target / Info.plist fields
  • Set properties on the AppDelegate (deprecated, remember)

For View controller-based status bar appearance = YES:

  • Override properties in your view controller classes
  • Set style of navigation controllers / navigation bars.

So, mystery solved… sort of!  If you ask me, it’s all a bit unnecessarily complicated, but there it is, the status of the status bar!

Tagged with:
Posted in Swift