The iOS SKD gets more Swift-er and better but still there are sometimes situation when it doesn't work so well. Many Foundation and UIKit methods are exposed to Swift with AnyObject parameters signature. AnyObject says that it has to be a class type. And this can be a problem, let's see why.

Swift has many powerful value types, and they are not class types. This means we can't use them with this Foundation API example.

func someSDKAPI(x: AnyObject) {
  print(x)
}

struct Person {
  let name: String
}

let person = Person(name: "Sam")
someSDKAPI(person) //Argument type Person

image

And we get an error. This error says that our person instance does not conform to AnyObject protocol. That is true because it's a struct and not a class.

To solve that problem we need to make an Adapter. Basically we need to wrap our Person type into a class type and pass an instance of that class to the someSDKAPI method

class Adapter {
  let val: Person

  init(val: Person) {
    self.val = val
  }
}

let adapter = Adapter(val: person)
someSDKAPI(adapter)

Now the code compiles and works. Here is the real word problem I've faced and the solution.

Real use-case Example

I want to use UILocalizedIndexedCollation class to sort people by name and group them into sections by alphabet. I can't do that because UILocalizedIndexedCollation can only work with classes, AnyObject type.

// Returns the index of the section that will contain the object.
public func sectionForObject(object: AnyObject, 
  collationStringSelector selector: Selector) -> Int

// Used for sorting objects within the same section.
public func sortedArrayFromArray(array: [AnyObject],
  collationStringSelector selector: Selector) -> [AnyObject]

The solution is same, make an adapter. This time it's a bit more complex because we need to expose name property in the Adapter so UILocalizedIndexedCollation can use it for sorting.

Here is the full code example, It does following things:

  • Create array of people, [Person] type
  • Map them to [Adapter] type
  • Use UILocalizedIndexedCollation to make sections, sort people and group them into section.
  • Map sorted section [[Adapter]] type back to [[Person]] type
struct Person {
  let name: String
}

class Adapter: NSObject {
  let val: Person

  init(val: Person) {
    self.val = val
  }

  var name: String {
    return val.name
  }
}

let collation = UILocalizedIndexedCollation.currentCollation()

let people = [
  Person(name: "Anna"),
  Person(name: "Sara"),
  Person(name: "Alan"),
  Person(name: "Sam")
]

let objects = people.map(Adapter.init)


let selector: Selector = "name"
var sections = Array(count: collation.sectionTitles.count, 
  repeatedValue: [AnyObject]())
let sortedObjects = collation.sortedArrayFromArray(objects, 
  collationStringSelector: selector)

for object in sortedObjects {
  let index = collation.sectionForObject(object, collationStringSelector: selector)
  sections[index].append(object)
}

let groupedPeople = sections.map { $0.map { ($0 as! Adapter).val } }
groupedPeople

Code Example - Playground