Parsing XML in Swift 3

Parsing XML has always bothered me in Swift. The NSXMLParser class doesn’t actually parse the XML structure into Swift objects, rather it traverses the XML structure, pinging its delegate whenever it discovers something new, like an XML element or attribute, and then leaves it up to you to make something of it.

There are several solutions out there for parsing XML to Swift objects, such as AEXML or SwiftyXML, but nothing seemed to do exactly what I was after, or had problems in Swift 3.

I decided to write my own XML Parser, and I’ve called it SwiftXML. Check it out here!

 

Sorting strings in Swift

Sorting an array of strings in Swift is a bit of an iceberg topic – it seems fairly manageable, you just call the sort function right? Then you dig a little deeper and find a whole lot more lurking under the surface!

Let’s start with the example in Apple’s Swift Programming Language guide:

(Apple sorts the data in reverse order, for simplicity I’ve swapped it)

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let sortedNames = names.sorted(by: { $0 < $1 } )
print(sortedNames)
// Prints "["Alex", "Barry", "Chris", "Daniella", "Ewa"]"

Great, but what happens if “Alex” starts with a lower case letter? Alex gets shunted to the end of the list – probably not what you’re after!
“Barry”, “Chris”, “Daniella”, “Ewa”, “alex”

To ignore case, compare the lower case versions of the names:

let sortedNames = names.sorted(by: {
    $0.lowercased() < $1.lowercased() } )

The rules for ordering strings can vary for different languages and locales. From Apple’s iOS String Programming Guide:

Important: For user-visible sorted lists, you should always use localized comparisons.

Fortunately, strings have another property called localizedLowercase.

let lowercaseSortedNames = names.sorted(by: {
    $0.localizedLowercase &lt; $1.localizedLowercase } )

Now let’s add some complexity to this problem. Let’s say we have a Person class, that contains a first name and a last name, and an array of people:

struct Person {
    var firstName: String
    var lastName: String
}
let people = [
    Person(firstName: "Kylie", lastName: "Minogue"),
    Person(firstName: "Dannie", lastName: "Minogue"),
    Person(firstName: "Paul", lastName: "Kelly"),
    Person(firstName: "Ned", lastName: "Kelly")
]

Sorting them by last name is easy enough, just be sure to include the lastName property:

let sortedPeople = people.sorted(by: {
    $0.lastName.localizedLowercase < $1.lastName.localizedLowercase } )

Screenshot 2016-08-05 12.57.30

Obviously the first names need to be compared as well. You can achieve this by putting the two comparisons into a tuple:

let sortedPeople = people.sorted(by: {
    ($0.lastName.localizedLowercase,$0.firstName.localizedLowercase) <
        ($1.lastName.localizedLowercase,$1.firstName.localizedLowercase)
} )

Screenshot 2016-08-05 13.02.11

My book: iOS Development with Swift – Early access Deal of the Day!

I’m pleased to announce that I am half way through writing a book on iOS development with Swift, to be published by Manning Publications.

The great news if you have experience in programming is that this book skips over all the basics, and skips straight to the juicy bits – such as “What’s new and different in Swift?” After introductory material, the book will take you through the process of building a sophisticated app in iOS from concept through to beta testing and the App Store.

Extra special news is that it is Deal of the Day for today (July 28) and early access is available for half-price.

Use code dotd072816au at http://bit.ly/2avl1hSGrummitt-iOS-HI.jpg

Keyboards, text views and first responders

If you’re moving your app’s interface from under the keyboard when it appears, you’re probably doing the following:
1. Get a reference to the relevant text field in the UITextFieldDelegate‘s textFieldDidBeginEditing method.
2. Listen to a relevant keyboard notification (possibly UIKeyboardWillShow or UIKeyboardWillHide but probably the best to use is UIKeyboardWillChangeFrame, as this covers all your bases). Get the size of the keyboard from the userInfo property and move the textField up out of the way.

Great, this works because when the user taps on a text field, the events occur in this order:
1. textFieldDidBeginEditing
2. UIKeyboardWillChangeFrame

But then when you happen to implement a text view you’ll discover that the events occur in the opposite order:
1. UIKeyboardWillChangeFrame
2. textViewDidBeginEditing

Whaaa? Well, that messes up the steps we were following – when we get the UIKeyboardWillChangeFrame notification, we don’t yet have a reference to the relevant text view to move it!

How to solve this? Here are three approaches:
1. Use UIKeyboardDidChangeFrame (Did, not Will) instead, to be sure we get it in the right order. Problem – we can no longer animate interface changes simultaneously with the keyboard, rather animations will happen in sequence.
2. Store the keyboard size in a property in the UIKeyboardWillHide selector. Call a method (let’s call it moveInterface() ) after both steps that will move the interface out of the way. The moveInterface() method will only work when it has references to both the keyboard size, and the relevant text field / text view.
3. Here’s another option, thinking outside the box:

The reason why we need to get a reference to the text field/view in the …didBeginEditing method, is that Apple hasn’t given us a simple way to get a reference to the current field/view being edited (also known as the firstResponder). However, Apple has given us an isFirstResponder() method that will tell you if a view is currently the fist responder. Great! We can use that method to recursively iterative through a view’s subviews and determine the current first responder.

If we know the first responder, we don’t need the …didBeginEditing methods at all, and can skip straight to dealing with moving the interface when we receive the UIKeyboardWillChangeFrame notification.

Here’s a UIView extension to add a computed property that returns the first responder from a view’s subviews:

import UIKit
extension UIView {
    var firstResponder:UIView? {
        if self.isFirstResponder() {
            return self
        }
        for view in self.subviews {
            if let firstResponder = view.firstResponder {
                return firstResponder
            }
        }
        return nil
    }
}

And here’s a UIViewController extension to get the scene’s first responder:

extension UIViewController {
    var firstResponder:UIView? {
        return view.firstResponder
    }
}

Any vs AnyObject vs NSObject

What is the difference between these three enigmatic types? A sometimes confusing topic, let’s get it straight.

You probably know Swift is a type-safe language. For this reason, the compiler won’t permit you to type infer an array of different types that don’t share a common root:

//Error: Type of expression is ambiguous without more context
var test = ["a",0]

Strings and Ints in Swift don’t share a common root, so the compiler doesn’t know what you want it to do when type inferring the array.

There are two tricks for removing this error:

Solution 1: Import UIKit

If you import UIKit the error goes away. Why?

import UIKit
//No error, test inferred to be [NSObject]
var test = ["a",0]

UIKit Framework includes the Foundation framework which automatically bridges common data types to their Foundation Objective-C counterparts. And unlike in Swift, in Objective-C, most data types do have a root: NSObject is an actual class (docs here), that is the root of most classes if you’re using the Foundation framework.

If you option-click on the test variable, you’ll find that it has defaulted to [NSObject].

But then – out of curiosity – what happens if you add another variable to the array that does not have NSObject as a root, such as a class of your own?

import Foundation
class Test {}
//No error, test inferred to be NSArray
var test = [Test(),"a",0]

The compiler can’t find a root class to infer the array’s data type, but instead of displaying an error, it instead infers an alternative data type – the Foundation Objective-C NSArray. NSArray is less strict than its Swift countertype; NSArray doesn’t enforce that elements it contains are the same data type.

Solution 2: [Any] or [AnyObject]

If you specifically define the array as [Any], you are indicating to the compiler that you are aware that the elements are not of the same data type, and you are ok with that.

class Test {}
//No error, test is defined as [Any]
var test:[Any] = [Test(),"a",0]

You may be surprised to learn that unlike NSObject, Any is not actually a concrete data type. You won’t find a type description for it in documentation. Rather Any is an alias for any data type.

Similarly, AnyObject is an alias for any data type derived from a class. You can use it to define an array that contains more than one object derived from a class, that don’t share a common root class:

class Test {}
class Test2 {}
//No error, test is defined as [AnyObject]
var test:[AnyObject] = [Test(),Test2()]

(You could of course have used [Any] as well – [AnyObject] is just a little more specific.

Passing in a string and an integer for example, to an AnyObject array will cause an error, as these data types are structs in Swift.

//Error: String does not conform to element type 'AnyObject'
var test:[AnyObject] = ["a",0]

Unless you import Foundation! (or UIKit) As we saw earlier, importing UIKit bridges Foundation data types to their Foundation counterparts. Strings are bridged to NSStrings and Ints are bridged to NSNumbers. NSNumbers and NSStrings happen to be defined as classes in Objective-C, so they now fulfill the definition of being an AnyObject:

import UIKit
//No error: test defined as [AnyObject]
var test:[AnyObject] = ["a",0]

Parameter defaults and Optional Function Parameters in Swift 3.0

There are more changes in Swift 3.0 to Optional Function parameters, so I thought I should revisit this blog post.

Functions no longer include the name of the first parameter in the function name, and the first parameter is no different to any other, hooray! For info just on function parameters in Swift 3.0, click on my post here.

Swift has an interesting multi-pronged approach to dealing with default and optional function parameters, based in part on its relationship with Objective C and part inspired by other approaches.
It’s worth exploring the options.

Optional function parameters approach 1

First of all, let’s look at one approach to optional parameters:

func output(firstName:String) {
    print(firstName)
}
func output(firstName:String,surname:String) {
    print("\(firstName) \(surname)")
}
output(firstName:"Joe") //prints Joe
output(firstName:"Joe", surname: "Blow") //prints Joe Blow

Wait – there are two functions with the same name? This is called function overloading, where two functions with the same name are differentiated by different parameters (or return values) and can have different implementations.

Parameters with default values approach 1

This technique can also be a way to define default parameters, where the function with less parameters can call the function with extra parameters, defining defaults.

func output(firstName:String) {
    output(firstName:firstName, surname: "Smith")
}
func output(firstName:String,surname:String) {
    print("\(firstName) \(surname)")
}
output(firstName:"Joe") //prints Joe Smith
output(firstName:"Joe", surname: "Blow") //prints Joe Blow

 

Optional function parameters approach 2

As Swift has an Optional variable type, making a parameter Optional, would obviously make it optional😉 However this doesn’t mean you don’t need to pass anything – you still need to pass nil, and deal with checking the optional contains something in the function.

func output(firstName:String,surname:String?) {
    if let surname = surname {
        print("\(firstName) \(surname)")
    } else {
        print(firstName)
    }

}
output(firstName: "Joe", surname: nil)    //prints Joe
output(firstName: "Joe", surname: "Blow")    //prints Joe Blow

Parameters with default values approach 2

Parameters can be given a default value.Parameters with default values have a few special attributes:

  • to be called in a different order, they need to be declared after any parameters without default values
  • These parameters with defaults can be left out when calling the function
  • If external parameter names are included (the second parameter onwards have external parameter names by default) parameters can be included in any order!

Note: In this example the first parameter specifies an external parameter name so as to be able to called out of order.

func output(firstName:String="Unknown",middleName:String="Unknown",surname:String="Unknown") {
    print("\(firstName) \(middleName) \(surname)")
}
output(firstName: "John", middleName: "Andrew", surname: "Smith")    //prints John Andrew Smith
output(firstName: "John")    //middleName and surname parameters left out! prints John Unknown Unknown
output()        //prints all defaults (Unknown Unknown Unknown)
output(surname: "Smith", middleName: "Julius", firstName: "John")   //parameters out of order! prints John Julius Smith

Optional function parameters approach 3

Which of course makes it possible for an ‘Optional’ variable to have a nil default value. So with a simple tweak to our previous default values example, we can introduce a true optional parameter, using the Optional type.

func output(firstName:String,surname:String?=nil) {
    if let surname = surname {
        print("\(firstName) \(surname)")
    } else {
        print(firstName)
    }

}
output(firstName: "Joe")    //prints Joe
output(firstName: "Joe", surname: "Blow")    //prints Joe Blow

Be careful with naming conflicts mixing these approaches – if you have multiple functions with the same name but different parameters, and one of them has optional parameters, and there’s a possibility that Swift won’t know which function you’re referring to, there’s a naming conflict and the compiler will complain.

Good luck with your Swift functions, hopefully this has been helpful – let us know in comments any other function parameter tips.

For info just on function parameters in Swift 3.0, click on my post here.

Sending push notifications using Parse Server

Finally – phew!

I have migrated Grabble Words from Parse to using Parse server using herokuapp (a platform as a service (PaaS) which you can use to run applications in the cloud.) with the backend hosted by mongodb (database hosting service). Definitely was not a straight-forward migration, especially for someone more comfortable working on front-end applications in nice GUI’s rather than back-end in the terminal.

I came across an issue with very little documentation/tutorials that I thought worth a little write-up: Sending push notifications using Parse Server from your app.

Previously the notification was set up using the PFPush object, something like:

if let pushQuery = PFInstallation.query() {

    pushQuery.whereKey("someKey", equalTo: someKey)
    let data = [
        "badge" : "Increment",
        "alert" : "Some message",
    ]
    // Send push notification to query
    let push = PFPush()
    push.setQuery(pushQuery) // Set our Installation query
    push.setData(data)

    push.sendPushInBackground()
}

PFPush is unfortunately disabled from the client(i.e. your app) now due to security issues. PFPush is, however, still available on server side code, which can be called from your app.

Unfortunately tutorials and guides on notifications using Parse Server (such as this) only go as far as triggering a notification from curl or cloud code, and don’t go into how the app can request the notification, other than to ironically state that ‘the process is fairly straightforward’…

Well, it is straightforward, when you know how to do it! (Follow the tutorial at codepath up until it dispatches notifications using curl or cloud code and then come back here to continue setting up the notification from your app)

When you set up your cloud application on Heroku, you have the option to set up Parse cloud code, which is basically javascript functions defined on the Parse.Cloud object. These are defined in the cloud/main.js file. I set up a function in this file that accepts data and pushes this data to a notification using Parse.Push:

// iOS push testing
Parse.Cloud.define("iosPush", function(request, response) {

  var user = request.user;
  var params = request.params;
  var someKey = params.someKey
  var data = params.data

  var pushQuery = new Parse.Query(Parse.Installation);
  pushQuery.equalTo('deviceType', 'ios'); // targeting iOS devices only
  pushQuery.equalTo("someKey", someKey)

  Parse.Push.send({
    where: pushQuery, // Set our Installation query
    data: data
  }, { success: function() {
      console.log("#### PUSH OK");
  }, error: function(error) {
      console.log("#### PUSH ERROR" + error.message);
  }, useMasterKey: true});

  response.success('success');
});

This cloud function can then be called from your iOS app using the `PFCloud.callFunctionInBackground` method:

let data = [
    "badge" : "Increment",
    "alert" : "Some message",
]
let request = [
    "someKey" : someKey,
    "data" : data
]
PFCloud.callFunctionInBackground("iosPush", withParameters: request as [NSObject : AnyObject])