Simple, Elegant Approach to parsing data with API Clients in Swift

Without going into too much detail here, I wanted to highlight an approach I’ve taken to managing parsing of data that comes back from a webservice. It leverages best practices, such as using Codable to parse JSON, but also allows for some flexibility to adapt to your backend implementation.

As they say “Adapt Early” when it comes to dealing with data that comes from outside your app, or even from a 3rd party framework.

As such, I came up with an approach that keeps a lot of business logic associated with an endpoint and less with the API client, and uses Swift generics, and associatedtype on protocols.

I have a hard time explaining this, so you can just copy-paste this into a playground and have a look for yourself:

/// What your request types need to support to work with the APIClient
public protocol APIRequest {
    var baseURL: URL { get }
    var path: String { get }
    
    // other properties here, such as parameters: [String: Any]
    
    func request(authToken: String?) -> URLRequest
}

// then provide a default implementation
extension APIRequest {
    func request(authToken: String?) -> URLRequest {
        let url = baseURL.appendingPathComponent(path)
        return URLRequest(url: url)
    }
}

/// we can group such enums by service.  Consider LoginEndpoint, UserManagementEndpoint, etc.
enum Endpoint {
    case home
}

/// ... then conform separately if you need to
extension Endpoint: APIRequest {
    var baseURL: URL {
        return URL(string: "http://www.google.com")!  // make sure this is a real URL for your backend.
    }
    var path: String {
        switch self {
        case .home:
            return "/home"
        }
    }
    
    // etc.
}

typealias APIResult<T> = Result<T, APIClientError>

enum APIClientError: Error {

    case decodingError(error: Error)
    case noDataToDecode
    case httpError(error: Error)
}

/// A protocol for being able to transform incoming json data before converting it via Codable
/// and finally allows you to pass it, or another type completely back via the result() function.
/// You might have a struct called UsersResponse, with a property .users: [User].
/// this JSONParsing instance could have a ResultType of [User], even though
/// if's the UsersResponse type that is decoded JSON.

public protocol JSONParsing: Codable {
    
    associatedtype ResultType
    
    /// This is where you return a value as a result of the parsing.  Sometimes it could be the instance itself,
    /// or sometimes some derived property from this oftentimes intermediate data model.
    /// it's also in this method that you could do some data operations or fire off some notifications
    func result() -> ResultType
    
    /// if JSON returned from a webservice should be altered somehow before decoding begins
    /// if `true` the method `transform(_ responseJSON: [String: Any], from request: APIRequest?) throws -> [String: Any]` will be invoked.
    /// The return value will then be serialized to data, then deserialized via the `Codable` protocol.
    static var requiresInputTransformation: Bool { get }
    
    /// basically you can massage this incoming data, or if it comes in an unexpected format,
    /// you can throw an error
    static func transform(_ responseJSON: [String: Any], from request: APIRequest?) throws -> [String: Any]
}

// Provide some default implementation for conformance that ultimately results in it working as before
extension JSONParsing {
    
    public static var requiresInputTransformation: Bool { return false }
    
    public static func transform(_ responseJSON: [String: Any], from request: APIRequest? = nil) throws -> [String: Any] {
        return responseJSON
    }
}


/// parse data that was returned from the given request. First it checks if Decoder needs transformation, and transforms it.
/// Then attempts to use the Codable support of the Decoder type, then convert that decoded value to the give result type
/// via the result(from decoded) method
func parseData<Decoder: JSONParsing>(_ data: Data?,
                                     from request: APIRequest?,
                                     decodeJSONWith decoder: Decoder.Type) -> APIResult<Decoder.ResultType> {
    
    do {
        var dataToDecode = data
        if decoder.requiresInputTransformation {
            // then create json from the data if possible.
            // if no data, empty dict.  otherwise make json out of it, if you can't... empty dict.
            let json: [String: Any] = (data == nil) ? [:] : try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] ?? [:]
            
            // transform the payload
            let transformed = try decoder.transform(json, from: request)
            
            // re-serialize
            dataToDecode = try JSONSerialization.data(withJSONObject: transformed, options: [.prettyPrinted])
        }
        
        if let jsonData = dataToDecode {
            let decoded = try JSONDecoder().decode(decoder, from: jsonData)
            return .success(decoded.result())
            
        } else {
            throw APIClientError.noDataToDecode
        }
        
    } catch let e as APIClientError {
        return .failure(e)
    } catch {
        return .failure(.decodingError(error: error))
    }
}



// Example Implementation

struct NamesResponse: JSONParsing {
    typealias ResultType = [String]  // in most cases your conformance requires you to declare a return type, then implement result()
    let names: [String]
    
    func result() -> ResultType {
        return names
    }
}

func print<T>(result: APIResult<T>) {
    switch result {
    case .success(let name):
        print("✅: \(name)")
    case .failure(let error):
        print("❌: \(String(describing: error))")
    }
}


let response = NamesResponse(names: ["Dave", "Steve"])
let responseData = try! JSONEncoder().encode(response)
let request = Endpoint.home
let result = parseData(responseData, from: request, decodeJSONWith: NamesResponse.self)
print(result: result)

print("eof")

I’m not certain many people read this blog, and that’s fine too; I put this here for my memory’s sake. 😉 Hit me up if you have questions.

SwiftUI: iPad-style Popovers on iPhone

One aspect of iOS development that always struck me as funny is how Apple’s default of Popover is different on iPad and iPhone. Basically on iPhone the standard popover is shown like an Action Sheet.

But there are certainly times where you want a popover on iPhone, like a simple delete confirmation, for example. You tap on a trash can icon, and you’d like a quick and tiny confirmation button to appear in context without the jarring experience of the entire screen being taken over by an action sheet. Or even the bottom 1/4 of the screen, especially when that button was on the navigation bar at the top right.

There are plenty of solutions to this in UIKit, but sadly none (yet) with SwiftUI. The .popover view modifier decides for you whether it will be a sheet or a popover, so basically here’s another example of SwiftUI being powerful but not yet fully filled out in terms of components available to us.

Full disclosure: I feel inclined to write a bit of a blog post about this, even though all I or anyone cares about is a solution to the problem. So here we go. I admit I didn’t write this, and so want to give a huge shout out and thank you to Chase Wasden, located here. I’ve taken his gist located here, then modified it to support being able to specify where you’d like your arrows to be, and here it is below:

//
//  WithPopover.swift
//  PopoverSwiftUI
//

// THANK YOU to this wonderful human:  https://gist.github.com/ccwasden/02cbe25b94eb6e844b43442427127e09

import SwiftUI

// -- Usage
/*
struct Content: View {
    @State var open = false
    @State var popoverSize = CGSize(width: 300, height: 300)
    
    var body: some View {
        WithPopover(
            showPopover: $open,
            arrowDirections: [.down],
            popoverSize: popoverSize,
            content: {
                // The view you want to anchor your popover to.
                Button(action: { self.open.toggle() }) {
                    Text("Tap me")
                }
            },
            popoverContent: {
                VStack {
                    Button(action: { self.popoverSize = CGSize(width: 300, height: 600)}) {
                        Text("Increase size")
                    }
                    Button(action: { self.open = false}) {
                        Text("Close")
                    }
                }
            })
    }
}
 */


// -- Source

struct WithPopover<Content: View, PopoverContent: View>: View {
    
    @Binding var showPopover: Bool
    var popoverSize: CGSize? = nil
    var arrowDirections: UIPopoverArrowDirection = [.down]
    let content: () -> Content
    let popoverContent: () -> PopoverContent
    
    var body: some View {
        content()
            .background(
                Wrapper(showPopover: $showPopover, arrowDirections: arrowDirections, popoverSize: popoverSize, popoverContent: popoverContent)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            )
    }
    
    struct Wrapper<PopoverContent: View> : UIViewControllerRepresentable {
        
        @Binding var showPopover: Bool
        var arrowDirections: UIPopoverArrowDirection
        let popoverSize: CGSize?
        let popoverContent: () -> PopoverContent
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<Wrapper<PopoverContent>>) -> WrapperViewController<PopoverContent> {
            return WrapperViewController(
                popoverSize: popoverSize,
                permittedArrowDirections: arrowDirections,
                popoverContent: popoverContent) {
                self.showPopover = false
            }
        }
        
        func updateUIViewController(_ uiViewController: WrapperViewController<PopoverContent>,
                                    context: UIViewControllerRepresentableContext<Wrapper<PopoverContent>>) {
            uiViewController.updateSize(popoverSize)
            
            if showPopover {
                uiViewController.showPopover()
            }
            else {
                uiViewController.hidePopover()
            }
        }
    }
    
    class WrapperViewController<PopoverContent: View>: UIViewController, UIPopoverPresentationControllerDelegate {
        
        var popoverSize: CGSize?
        let permittedArrowDirections: UIPopoverArrowDirection
        let popoverContent: () -> PopoverContent
        let onDismiss: () -> Void
        
        var popoverVC: UIViewController?
        
        required init?(coder: NSCoder) { fatalError("") }
        init(popoverSize: CGSize?,
             permittedArrowDirections: UIPopoverArrowDirection,
             popoverContent: @escaping () -> PopoverContent,
             onDismiss: @escaping() -> Void) {
            self.popoverSize = popoverSize
            self.permittedArrowDirections = permittedArrowDirections
            self.popoverContent = popoverContent
            self.onDismiss = onDismiss
            super.init(nibName: nil, bundle: nil)
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
        
        func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
            return .none // this is what forces popovers on iPhone
        }
        
        func showPopover() {
            guard popoverVC == nil else { return }
            let vc = UIHostingController(rootView: popoverContent())
            if let size = popoverSize { vc.preferredContentSize = size }
            vc.modalPresentationStyle = UIModalPresentationStyle.popover
            if let popover = vc.popoverPresentationController {
                popover.sourceView = view
                popover.permittedArrowDirections = self.permittedArrowDirections
                popover.delegate = self
            }
            popoverVC = vc
            self.present(vc, animated: true, completion: nil)
        }
        
        func hidePopover() {
            guard let vc = popoverVC, !vc.isBeingDismissed else { return }
            vc.dismiss(animated: true, completion: nil)
            popoverVC = nil
        }
        
        func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
            popoverVC = nil
            self.onDismiss()
        }
        
        func updateSize(_ size: CGSize?) {
            self.popoverSize = size
            if let vc = popoverVC, let size = size {
                vc.preferredContentSize = size
            }
        }
    }
}

This is also an excellent example for learning how to make UIKit and SwiftUI play with each other.

SwiftUI: Animating Panel Content

I’m finally getting my hands dirty with SwiftUI and generally find it more motivating to build components that I actually need. So I thought perhaps that it’s time I re-write my Songbook Simple and Recipebox Simple apps to leverage all the experience I’ve gained with Swift, Design Patterns, Test-driven development, and to see how I might build the app in SwiftUI.

Most of Songbook’s UI is pretty simple and straightforward, with one exception: the Main Container UI. Basically Songbook has 3 principal UI components: The Song / Content View (rendering text or images), a Menu panel that slides in/out, and a toolbar that can show/hide.

I basically took this blog post, married it with this other blog post by the same author, to get an understanding of how to make a side panel that animates in and out, then take this approach and see if it can work in 2 dimensions. It wasn’t as straightforward to do the bottom panel, but it did teach me about the importance of GeometryReader and gave me some insights as to how SwiftUI handles state changes, and how to build a view hierarchy that takes that into consideration.

I am grateful to be able to stand on someone else’s shoulders to get me far enough along, after which I synthesized the rest from the knowledge gained. So I’ll just jump to how I got to the result.

It ultimately yielded these components:

//
//  ControlsOverlayView.swift
//  BookItemsSuite
//
//  Created by Stephen O'Connor on 11.03.21.
//

import SwiftUI

struct ControlsOverlayView<ToolbarContent: View, MenuContent: View>: View {
    
    let menuWidth: CGFloat
    let isMenuActive: Bool
    let onMenuHide: () -> Void
    
    let menuContent: MenuContent
    
    let toolbarHeight: CGFloat
    let isToolbarActive: Bool
    let onToolbarHide: () -> Void
    
    let toolbarContent: ToolbarContent
    
    init(menuWidth: CGFloat = 270,
         isMenuActive: Bool = true,
         onMenuHide: @escaping () -> Void,
         toolbarHeight: CGFloat = 44,
         isToolbarActive: Bool = true,
         onToolbarHide: @escaping () -> Void,
         @ViewBuilder menuContent: () -> MenuContent,
         @ViewBuilder toolbarContent: () -> ToolbarContent) {
        
        self.menuWidth = menuWidth
        self.isMenuActive = isMenuActive
        self.onMenuHide = onMenuHide
        self.menuContent = menuContent()
        
        self.toolbarHeight = toolbarHeight
        self.isToolbarActive = isToolbarActive
        self.onToolbarHide = onToolbarHide
        self.toolbarContent = toolbarContent()
    }
    
    var body: some View {
        ZStack {
            GeometryReader { _ in
                EmptyView()
            }
            .background(Color.gray.opacity(0.3))
            .opacity(self.isMenuActive ? 1.0 : 0.0)
            .animation(Animation.easeIn.delay(0.25))
            .onTapGesture {
                self.onMenuHide()
            }
            
            GeometryReader { geometry in
                VStack {
                    let toolbarHeight = isToolbarActive ? self.toolbarHeight : 0
                    HStack {
                        Spacer()
                        
                        let space: CGFloat = 0.0 //geometry.size.width - self.menuWidth
                        let offset = self.isMenuActive ? space : space + self.menuWidth
                        self.menuContent
                            .frame(width: self.menuWidth, height: geometry.size.height - toolbarHeight, alignment: .center)
                            .background(Color.red)
                            .offset(x: offset)
                            .animation(.default)
                    }
                    .frame(width: geometry.size.width,
                           height: geometry.size.height - toolbarHeight,
                           alignment: .center)
                    
                    let offset = self.isToolbarActive ? 0 : self.toolbarHeight/2
                    
                    self.toolbarContent
                        .frame(width: geometry.size.width,
                               height: self.toolbarHeight,
                               alignment: .center)
                        .background(Color.yellow)
                        .offset(y: offset)
                        .animation(.default)
                    
                }
            }
        }
    }
}

struct ControlsOverlayView_Previews: PreviewProvider {
    static var previews: some View {
        ControlsOverlayView(menuWidth: 270,
                            isMenuActive: true,
                            onMenuHide: {},
                            toolbarHeight: 44,
                            isToolbarActive: true,
                            onToolbarHide: {}) {
            Text("Menu Content")
        } toolbarContent: {
            Text("Toolbar Content")
        }
    }
}

Which is then a part of the ApplicationView (in UIKit terms, this would equate to a root view controller):

//
//  ApplicationView.swift
//  SidePanelSandbox
//
//  Created by Stephen O'Connor on 10.03.21.
//

import SwiftUI

struct ApplicationView<MainContent: View, MenuContent: View, ToolbarContent: View>: View {
    
    @State var isMenuActive: Bool = true
    @State var isToolbarActive: Bool = false
    
    var menuWidth: CGFloat = 270
    
    let mainContent: MainContent
    let menuContent: MenuContent
    let toolbarContent: ToolbarContent
    
    init(@ViewBuilder mainContent: () -> MainContent,
                      menuContent: () -> MenuContent,
                      toolbarContent: () -> ToolbarContent) {
        self.mainContent = mainContent()
        self.menuContent = menuContent()
        self.toolbarContent = toolbarContent()
    }
    
    var body: some View {
        ZStack {
            GeometryReader { geometry in
                self.mainContent
                    .frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
                   
            }
            if !self.isMenuActive {
                GeometryReader { _ in
                    VStack {
                        Button(action: {
                            self.toggleMenu()
                        }, label: {
                            Text("Menu")
                        })
                        
                        Button(action: {
                            self.toggleToolbar()
                        }, label: {
                            Text("Toolbar")
                        })
                    }
                }
            }
            
            ControlsOverlayView(menuWidth: 270, isMenuActive: isMenuActive, onMenuHide: {
                self.toggleMenu()
                print("did hide menu")
            }, toolbarHeight: 44, isToolbarActive: isToolbarActive, onToolbarHide: {
                print("did hide toolbar")
            }, menuContent: {
                self.menuContent
            }, toolbarContent: {
                self.toolbarContent
            })
        }
    }
    
    func toggleMenu() {
        if self.isMenuActive {
            // if we're about to close the menu, close the toolbar too.
            $isToolbarActive.wrappedValue = false
        }
        self.isMenuActive.toggle()
    }
    
    func toggleToolbar() {
        self.isToolbarActive.toggle()
    }
}

struct ApplicationView_Previews: PreviewProvider {
    static var previews: some View {
        ApplicationView {
            Text("Main Content")
        } menuContent: {
            Text("Menu Content")
        } toolbarContent: {
            Text("Toolbar Content")
        }
    }
}

And that’s essentially how it works. I was and still am a little confused as to why the ControlsOverlayView is rendering as expected, given the way I offset the toolbar, but all in all I think this approach is pretty clean and I’m happy with it.

Swift Error Handling

I often write blog posts for my own sake, but publish them in the event that others may find the information useful.
Today, I wanted to post a quick reference to Error handling in Swift and the syntax for casting and binding an error that is caught in a do-catch statement. I’m finding quite often what you’ll find on the internet is you can catch certain error types, but they don’t assign it to a variable, for example if you want to wrap that error into another Error type that makes your API more contstrained in terms of what kind of errors you should expect to handle in your UI.
For example, it’s not unusual for me to have an error type like this:

public enum SyncError: Error {
    
    /// if for example the pre-conditions for Cloud Sync to work are not met, this could be the result.
    /// actual use cases would be for example if you're not signed into iCloud.
    case initializationFailed(details: String)
    
    /// if you are performing an operation that expects there to be no file at a specific location but there is.
    case fileAlreadyExists(filename: String)
    
    /// if you try to upload a file with no data, or the file doesn't exist in remote
    case noContent(filename: String)
    
    /// wraps an error thrown from FileManager APIs
    case fileManager(error: Error)
    
    /// Just for situations that are unlikely to happen.
    case unexpected(details: String)
}

So there are times where you want to perform a do-catch but then catch certain error types and re-package them before finishing up. In this case below, it’s some body of a method that has to call a completion block on a completion queue with this signature:

(_ success: Bool, _ errors: [SyncError]?) -> Void
do {
      // DO SOME STUFF REQUIRING A try STATEMENT    

} catch let e as SyncError {
    // you see on the line above how you cast and assign the SyncError to a value
    completionQueue.async {
       completion(false, [e])
    }
} catch let e {
    completionQueue.async {
       completion(false, [SyncError.fileManager(error: e)])
    }
}

And that’s it! I only wrote this here because I didn’t want to google around again should I forget that syntax.

Recipe: Transform the JSON that your API Client decodes

I’m currently having a not-so-fun fight with the Firebase REST API while trying to bypass using the Firebase iOS SDK in a demo project whose purpose it is to not use Firebase, per se, but to implement a REST API Client on iOS. People say “Use Firebase” as it’s easy to get up and running. VERY debatable here. It does however highlight one aspect of my profession that almost seems to be a general rule:

“Mobile devs almost inevitably have to spend a lot of time figuring out why the backend never works as advertised.”

It’s been extremely rare in my career to be given an Backend API spec that can be implemented without any headaches, but I’ll save that for another post as to how best to tackle that. (In fact I did write the beginnings of such a post, many years ago).

Anyway, it seems that the firebase backend doesn’t return arrays of dictionaries. It just returns dictionaries. So if you request a collection, you’ll get a dictionary with a key count equal to the collection size.

This is a problem if you just want to parse an array of objects, because there is no array. And if you are not a backend developer like me, you just have to work with what you are given.

Here’s a use case coming from Firebase. I requested /users.json and got:

{
    "SomeRandomFakeUserId2": {
        "name": "Stephen the Admin"
    },
    "SomeRandomFakeUserId3": {
        "name": "Stephen the Tester"
    }
}

But ideally this would be an array of dictionaries with the key above embedded in the dictionary, like so:

[
    {
        "objectId": "SomeRandomFakeUserId2",
        "name": "Stephen the Admin"
    },
    {
        "objectId": "SomeRandomFakeUserId3",
        "name": "Stephen the Tester"
    }
]

But ideally I would like to keep my paradigm that generally works on my APIClient, which generally works like:

    enum APIResult<T> {
        case success(T)
        case error(Error)
    }
    
    @discardableResult
    public func sendThenDecodeJSON<T: Codable>(_ request: URLRequest,
                                               completion: @escaping (APIResult<T>) -> Void) -> URLSessionTask? {
        // session: URLSession
        let task = session.dataTask(with: request) { (data, response, error) in
            
            do {
                // Omitted:  Handle errors, etc.
                
                // You see here, all I have to do is create a struct for my responses, based on the JSON spec on the server, and I'm good.  Except... How do I use the Codable approach to json if I don't know what the key names will be?
                let result = try JSONDecoder().decode(T.self, from: data)
                
                DispatchQueue.main.async {
                    completion(.success(result))
                }                
            } catch let error {
                // Omitted for brevity: DO BETTER ERROR HANDLING HERE
            
                DispatchQueue.main.async {
                    completion(.failure(error))
                }
            }
        }
        task.resume()
        return task
    }

So what can be done here? I want to make my system flexible, but with a minimum of additional configuration. I know that on my endpoints, my existing solution gets the job done, but on a few, it doesn’t.

So I came up with the PayloadTransformable protocol:

protocol PayloadTransformable: Codable {
    static var requiresTransformation: Bool { get }
    static func transform(_ input: [String: Any]) -> [String: Any]
}

// Provide some default implementation for conformance that ultimately results in it working as before
extension PayloadTransformable {
    static var requiresTransformation: Bool { return false }
    static func transform(_ input: [String: Any]) -> [String: Any] {
        return input
    }
}

And then you have some data models, and just have to implement that:

struct Object: Codable {
    let objectId: String
    let name: String
}

struct ObjectsResponse: PayloadTransformable {
    let objects: [Object]
    
    static var requiresTransformation: Bool { return true }
    
    static func transform(_ input: [String: Any]) -> [String: Any] {
        
        var objectArray = [[String: Any]]()
        
        for key in input.keys {
            var newObject = [String: Any]()
            newObject["objectId"] = key
            guard let values = input[key] as? [String: Any] else {
                print("Failed.")
                return input
            }
            for (key, value) in values {
                newObject[key] = value
            }
            objectArray.append(newObject)
        }
        
        return ["objects": objectArray]
    }
}

And then you modify how the APIClient handles the incoming data:

@discardableResult
public func sendThenDecodeJSON<T: PayloadTransformable>(_ request: URLRequest,
                                               completion: @escaping (APIResult<T>) -> Void) -> URLSessionTask? {

        let task = session.dataTask(with: request) { (data, response, error) in

            do {
                // Omitted:  Handle errors, etc.

                var jsonData = data
                // Due to the default implementation listed in the Protocol extension, in general you have to do nothing in order to adopt this, other than make your relevant Codable types conform to PayloadTransformable (a quick find and replace)
                if T.requiresTransformation {
                    guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                        fatalError()  // you'd throw an error here
                    }
                    let transformed = T.transform(json)
                    jsonData = try JSONSerialization.data(withJSONObject: transformed, options: [.prettyPrinted])
                }
                
                let result = try JSONDecoder().decode(T.self, from: jsonData)

                // we are using a delegateQueue on URLSession, so we want to complete on the main thread
                DispatchQueue.main.async {
                    completion(.success(result))
                }

            } catch let error {

                // Omitted for brevity: DO BETTER ERROR HANDLING HERE>

                // we are using a delegateQueue on URLSession, so we want to complete on the main thread
                DispatchQueue.main.async {
                    completion(.failure(error))
                }
            }
        }
        task.resume()
        return task
    }


And that’s it.

You could copy-paste this into a Playground and check it out:

protocol PayloadTransformable: Codable {
    static var requiresTransformation: Bool { get }
    static func transform(_ input: [String: Any]) -> [String: Any]
}

// Provide some default implementation for conformance that ultimately results in it working as before
extension PayloadTransformable {
    static var requiresTransformation: Bool { return false }
    static func transform(_ input: [String: Any]) -> [String: Any] {
        return input
    }
}
struct Object: Codable {
    let objectId: String
    let name: String
}

struct ObjectsResponse: PayloadTransformable {
    let objects: [Object]
    
    static var requiresTransformation: Bool { return true }
    static func transform(_ input: [String: Any]) -> [String: Any] {  
        var objectArray = [[String: Any]]()
        for key in input.keys {
            var newObject = [String: Any]()
            newObject["objectId"] = key
            guard let values = input[key] as? [String: Any] else {
                print("Failed.")
                return input
            }
            for (key, value) in values {
                newObject[key] = value
            }
            objectArray.append(newObject)
        }
        
        return ["objects": objectArray]
    }
}
func simulatedResponseHandler<T: PayloadTransformable>(_ data: Data) -> T {
        
        do {   
            var jsonData = data
            if T.requiresTransformation {
                guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                    fatalError()
                }
                let transformed = T.transform(json)
                jsonData = try JSONSerialization.data(withJSONObject: transformed, options: [.prettyPrinted])
            } 
            let object = try JSONDecoder().decode(T.self, from: jsonData)
            return object
            
        } catch let e {
            fatalError("Failed!")
        }
}

// All that was definitions.  Now the code you execute:

do {
    
    let untransformed =  [
        "SomeID" : ["name": "SomeName"],
        "SomeOtherID" : ["name": "SomeOtherName"]
    ]
    
    let transformed = ["objects": [
        ["objectId": "SomeID", "name": "SomeName"],
        ["objectId": "SomeOtherID", "name": "SomeOtherName"],
    ]]

    let jsonInput = try JSONSerialization.data(withJSONObject: untransformed, options: [.prettyPrinted])

    let response: ObjectsResponse = simulatedResponseHandler(jsonInput)
    print(String(describing: response))
    
} catch let e {
    print(e.localizedDescription)
}



[Swift] Detect Touches on Attributed Text in UILabel

It’s always funny when you google “how do I…” and Google shows you a result that you actually wrote.

I wanted to solve this problem, and it looks like I did it years ago in Objective-C.

So, I re-wrote it in Swift that you can basically just copy-paste-use.  You’re welcome.

//
//  TappableLabel.swift
//
//  Created by Stephen O'Connor on 07.10.20.
//  MIT License.  You will send no lawyers here.  Have fun.
//  taken from here:  https://horseshoe7.wordpress.com/2015/12/10/detect-touches-on-attributed-text-in-uilabel/

import UIKit

typealias LabelLink = (text: String, link: Any?)

protocol TappableLabelDelegate: class {
    func didTapOnLink(_ link: LabelLink, in tappableLabel: TappableLabel)
}

extension NSAttributedString.Key {
    static let custom = NSAttributedString.Key("CustomAttribute")
}

class TappableLabel: UILabel {
    
    weak var delegate: TappableLabelDelegate?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }
    
    private func commonInit() {
        self.textAlignment = .left // has to be left for this to work!
        addGestureRecognizers()
    }
    
    private func addGestureRecognizers() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(tappedLabel(_:)))
        self.isUserInteractionEnabled = true
        self.addGestureRecognizer(tap)
        
    }
    
    @objc
    private func tappedLabel(_ tap: UITapGestureRecognizer) {
        guard let label = tap.view as? TappableLabel, label == self, tap.state == .ended else {
            return
        }
        let location = tap.location(in: label)
        processInteraction(at: location, wasTap: true)
    }
    private func processInteraction(at location: CGPoint, wasTap: Bool) {
        
        let label = self
        
        guard let attributedText = label.attributedText else {
            return // nothing to do
        }
        
        let textStorage = NSTextStorage(attributedString: attributedText)
        let textContainer = NSTextContainer(size: label.bounds.size)
        let layoutManager = NSLayoutManager()
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)
        
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        
        
        let characterIndex = layoutManager.characterIndex(for: location,
                                                          in: textContainer,
                                                          fractionOfDistanceBetweenInsertionPoints: nil)
        if characterIndex < textStorage.length {
            log.info("Character Index: \(characterIndex)")
            let range = NSRange(location: characterIndex, length: 1)
            let substring = (attributedText.string as NSString).substring(with: range)
            
            log.info("Character at Index: \(substring)")
            if let labelLink = attributedText.attribute(.custom,
                                                        at: characterIndex,
                                                        effectiveRange: nil) as? LabelLink {
                
                log.debug("You \(wasTap ? "tapped" : "pressed") on \(labelLink.text) and the value is: \(String(describing: labelLink.link))")
                self.delegate?.didTapOnLink(labelLink, in: self)
            }
        }
    }
    
    // will set the label's text to the given text argument, but for any callbackString it will search the text for that and embed it.
    func setText(_ text: String, withCallbacksOn callbackStrings: [LabelLink] = []) {
        
        self.text = text
        
        let attributedString = NSMutableAttributedString(string: text)
        let coreAttributes: [NSAttributedString.Key: Any] = [
            .foregroundColor : self.textColor!,
            .font: self.font!
        ]
        attributedString.setAttributes(coreAttributes,
                                       range: NSRange(location: 0, length: text.count))
        
        for labelLink in callbackStrings {
            
            let range = (text as NSString).range(of: labelLink.text)
            if range.location != NSNotFound {
                var additionalAttributes = coreAttributes
                additionalAttributes[.custom] = labelLink
                attributedString.setAttributes(additionalAttributes, range: range)
            }
        }
        self.attributedText = attributedString
    }
}

Manual Core Data Migrations – Lessons Learned

So, I’ll try to keep this all brief.  In short, as everyone, including Core Data Ninja Master Marcus Zarra, has said:  Avoid heavy Core Data Migrations!

The tools are buggy, the crash bugs difficult to interpret.  I spent 4-5 hours already just sort of shooting at a black box.   So, I hope to share with you the things I learned and what caught me up.  If this is all too brief or out of context in places, it’s because I’m not a tutorial writer, and keep my blog posts quite often so that I can refer back to them.  So please feel free to write me if you need clarification.

I’ll also give the disclaimer that I’m not the most ‘canonical’ of programmers.  So, these are my findings.  I don’t know if they follow best practices, but I don’t think there are any best practices when it comes to this stuff…. otherwise I probably wouldn’t have made this lengthy blog post!

Scenario

Migration of old data store using an old Core Data model to a new data store with a brand new Core Data model

This documents my personal experience trying to migrate a Data Model I want to retire (i.e. completely separate Core Data Model file) to a brand new data model.

I wanted to move away from my old Objective-C pipeline which involves NSManagedObject subclasses using mogenerator that all have an Objective-C style Namespace and codegen, to a pure Swift version which uses the codegen feature of the Latest Xcode Core Data editor.

I kept these models separate because in a few releases when I know everyone has upgraded, I can eventually just remove the legacy model from the bundle and all the associated classes.

What I learned / experienced:

  • How to set up incremental migrations using a handy little helper from http://www.objc.io
  • How to migrate old data to new using Mapping Models
  • How to write and use custom NSEntityMappingPolicy objects
  • How to use the Mapping Model editor to call functions on these custom policies
  • All the various Pitfalls, Gotchas, and things that Apple really needs to improve on.

Useful Pre-requisite Reading:

How to set up incremental migrations

This is really what you want to do.  It makes your life easy in that you only need to make mapping models for each change to your data model.  There is a lot of code floating around the internet that helps you make an incremental migration manager.  I took one from objc.io and then modified it so it could support my use case where I am migrating a completely different store to a new one, and that new one should have its own URL on disk that may not follow from the original store.

Code for that migration manager can be found in a gist here.  Don’t just copy-paste:  You have to copy-paste into the correct files!  Also, it’s in objective-c so you’ll want to make a bridging header… It’s really not hard.

What is different in my version of MHWMigrationManager than from on objc.io:

  • It has a hack fix for a dubious migration error that has minimal debug help: ‘Mismatch between mapping and source/destination models’
  • It allows you to specify via the delegate a “Final Destination URL” once the Migration has successfully completed.  This can be useful as in my case where I have 2 different Stores and Models and want them to stay that way.

Set up your Core Data Stack

Basically, you start with Apple’s boilerplate code for Core Data and a persistent container, then you modify in a way that I’ve done here.  These 2 gists I’ve provided are all just starting points.  We haven’t even got to the annoying stuff yet.

If you create a brand new data model like I have done, be sure that it uses a different name than your old one.  You can’t rename the old one because you want the new one to have the same nice name.  Leave everything about the old one in place.

Pro Tip:  If you didn’t do this but already did the work of creating this new model version, if you create a new model either via duplication or copy-paste of entities, keep in mind that delete rules are not copied over, NOR are the inverse relationships.  So you’ll have to go through and set these by hand again.

Now Create Mapping Models

You can now proceed to create a mapping model in the ways you’ve probably read about on the internet already.   Pro Tips:

  • Only write your mapping model, once your Core Data Object Model is complete.  If you make a modification to your Core Object Model (.xcdatamodeld), any mappings that reference it will no longer be found by the Migration Manager.
  • Due to the nature of the migration process, you won’t have access to any subclass of NSManagedObject, and so this can make setting up NEW relationships difficult.  It is therefore a good idea to keep ‘foreign keys’ on your models with relationships.  (i.e. you extend your Post model to have an Attachment.  Attachment should have an attribute postId, or something identifiable)
  • If you use a subclass of NSEntityMigrationPolicy and you specify it in the Mapping Model editor, make sure that you also provide the Module name.  So, instead of LegacyToModernPolicy, I would put SongbookSimple.LegacyToModernPolicy, because my app’s module name is SongbookSimple.

Mapping Models are useful in that they allow a lot of customization, and you can sort of elect what you want to be done “automagically” and what you’d like to have more control over.  You get more control over these by using custom subclasses of NSEntityMigrationPolicy.

In general, for property or relationship mappings, the auto-generated functionality can get you pretty far.  Otherwise, when you have specified a custom Policy, you can make method calls on that policy.

When to use custom policy methods

If you have a somewhat strange migration for a specific entity, you can just remove all Attribute mappings in for an Entity Mapping, then in your subclass, override the method below.  The method signature is misleading because you are actually asked to create the equivalent version of the old object in the new object.  So you’re manually specifying how one object “becomes” the new object, and in my case where a Song started to store its data in a separate SongData object, you have to create this SongData object and find a way to link the two (i.e. songFilename) because you create the actual relationships later.  EDIT:  What I’ve learned however is that ideally this createDestinationInstances(…) method should really be about 1:1 mapping.  If your migration ends up taking one object and splitting it into two, you should write to Policy Subclasses and have 2 Entity Mappings:  i.e OldSong -> Song and OldSong -> SongData, then in the 2nd pass of the migration where the relationships are re-established, that’s where you can associate them again.

    override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
        
        let dInstance = NSEntityDescription.insertNewObject(forEntityName: mapping.destinationEntityName!, into: manager.destinationContext)
        
        let text = sInstance.value(forKey: "text") as! String?
        
        dInstance.setValue(sInstance.value(forKey: "lastModified"), forKey: #keyPath(Song.createdAt))
        dInstance.setValue(sInstance.value(forKey: "lastModified"), forKey: #keyPath(Song.updatedAt))
        dInstance.setValue("txt",                                   forKey: #keyPath(Song.fileExtension))
        dInstance.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(Song.filename))
        dInstance.setValue(sInstance.value(forKey: "firstLetterUppercase"), forKey: #keyPath(Song.firstLetterUppercase))
        dInstance.setValue(sInstance.value(forKey: "name"), forKey: #keyPath(Song.title))
        dInstance.setValue(sInstance.value(forKey: "oldFilename"), forKey: #keyPath(Song.oldFilename))
        dInstance.setValue(self.songDataLengthOfText(text), forKey: #keyPath(Song.songDataLength))
        dInstance.setValue(text, forKey: #keyPath(Song.text))
        dInstance.setValue(SongDataType.plainText.rawValue, forKey: #keyPath(Song.typeRaw))
        dInstance.setValue(nil, forKey: #keyPath(Song.userInfoData))
        
        // NOTE: DON'T DO THIS.  Set up a OldSong -> SongData entity mapping!
        // Then fetch this instance later via its songFilename
        //let songData = NSEntityDescription.insertNewObject(forEntityName: SongData.entity().name!, into: manager.destinationContext)
        //songData.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(SongData.songFilename))
        //let structure = SongDataStructure()
        //structure.set(text: text)
        //songData.setValue(structure.serialize(), forKey: #keyPath(SongData.nsdata))
        
        // NOTE: DON'T DO THIS.  Set up a OldSong -> SongViewPreferences entity mapping!
        //let viewPreferences = NSEntityDescription.insertNewObject(forEntityName: SongViewPreferences.entity().name!, into: manager.destinationContext)
        //viewPreferences.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(SongViewPreferences.songFilename))
        
        manager.associate(sourceInstance: sInstance, withDestinationInstance: dInstance, for: mapping)
        return
    }

I will say this:  this method above is used to create instances.  You don’t associate them yet!  That’s why you can see I set songFilename on these 2 new data types (in my new model), so that I can create their relationship later…  this is for the second pass in the migration, where relationship mappings are created (more on that).

Quick Reference for Mapping Editor FUNCTION arguments:

NSMigrationManagerKey: $manager
NSMigrationSourceObjectKey: $source
NSMigrationDestinationObjectKey: $destination
NSMigrationEntityMappingKey: $entityMapping
NSMigrationPropertyMappingKey: $propertyMapping
NSMigrationEntityPolicyKey: $entityPolicy

If I don’t need such a detailed mapping of an object, I can also use custom methods to set a property in the editor for any attribute.  Pro Tip: Make sure you change the the “Source Fetch” on a Relationship mapping to “Use Custom Value Expression” if you do this.

FUNCTION($entityPolicy, "filenameForLibraryWith:" , $manager)

This is syntax in the editor.  It’s saying “to get the value for this destination attribute, call a method on the (custom) policy called

func filename(forLibraryWith manager: NSMigrationManager) -> String

Remember you can use the arguments listed about in the Quick Reference section for knowing what kind of arguments you can pass into these methods!

You can also use these custom methods when performing relationship mappings.  Now, I personally haven’t had to override the method:

func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws

I’ve got by with the Relationship mappings in the Mapping Model editor and custom methods.

Do not reference subclasses of NSManagedObject during migration!

It would seem that this is how Core Data ensures that the migration can work reliably.  It sucks because you need to remember:

  • awakeToInsert is not called, because no instances of your subclass are used in migration, so whatever you would set in awakeToInsert, you should do here too.
  • The relationship accessors aren’t present either.  So be safe and specify the relationship and its inverse.

I created a handy helper method to find NSManagedObject instances:

class LegacyToModernPolicy: NSEntityMigrationPolicy {
    
    static func find(entityName: String,
                     in context: NSManagedObjectContext,
                     sortDescriptors: [NSSortDescriptor],
                     with predicate: NSPredicate? = nil,
                     limit: Int? = nil) throws -> [NSManagedObject] {
        
        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entityName)
        fetchRequest.predicate = predicate
        fetchRequest.sortDescriptors = sortDescriptors
        if let limit = limit {
            fetchRequest.fetchLimit = limit
        }
        
        do {
            let results = try context.fetch(fetchRequest)
            return results
        } catch {
            log.error("Error fetching: \(error.localizedDescription)")
            throw error
        }
    }
  }

Pitfalls, Gotchas, Other Tips, Things I’ve already said

Here’s a list of things I might have mentioned already, but in this modern age where attention spans are low, and we need headings to actually read something, here is a list of things that I discovered in this long journey over 2 days:

    1. If you created your new Data Model and it started with the same name as your legacy one (i.e. SongbookSimple), it’s better to just create a new Core Data Model file with a new name (e.g. SongbookSimpleDataModel), then rebuild your data model by hand. This in practice means copy-pasting all the entities from the one to the other. Note! The Delete rules are not copied over, NOR are the inverse relationships. So you’ll have to go in and for each Entity, connect these up again.
    2. If you get an error during Migration such as:
      Couldn’t create mapping policy for class named (LegacyToModernArtistPolicy)
      It’s probably because you have Swift module-related errors. See Sandeep’s comment in this Question:
      So LegacyToModernArtistPolicy becomes SongbookSimple.LegacyToModernArtistPolicy and it works
    3. If you change your current model to accommodate the migration process, AND you’ve already run the app, you’ve altered the data model in a bad way. You don’t know if this is a typical state to be in, so better play it safe. You’ve got to remove the app from the simulator, re-install an old version so to populate an old data store, then run your new version in development.
    4. If you are doing custom fetch requests so to satisfy creating relationships (in your NSEntityMigrationPolicy), it would seem that fetch requests are returning NSManagedObject objects and not the subclasses. Nor can they be casted. For example, you might have seen a crash error:Could not cast value of type 'NSManagedObject_Library_' (0x6100000504d0) to 'SongbookSimple.Library' (0x101679180).This is why the helper above can be useful.  I fixed the bug I mention here with:
          func libraryForManager(_ manager: NSMigrationManager) -> NSManagedObject {
              
              do {
                  var library: NSManagedObject? = try LegacyToModernPolicy.find(entityName: Library.entity().name!,
                                                          in: manager.destinationContext,
                                                          sortDescriptors: [NSSortDescriptor(key: "filename", ascending: true)],
                                                          with: nil,
                                                          limit: 1).first
                  if library == nil {
                      let dInstance = NSEntityDescription.insertNewObject(forEntityName: Library.entity().name!, into: manager.destinationContext)
                      
                      // awakeFromInsert is not called, so I have to do the things I did there, here:
                      dInstance.setValue(Library.libraryFilename, forKey: #keyPath(Library.filename))
                      dInstance.setValue(NSDate(timeIntervalSince1970: 0), forKey: #keyPath(Library.updatedAt))
                      library = dInstance
                  }
                  
                  return library!
                  
              } catch {
                  fatalError("Not sure why this is failing!")
              }
          }
      
    5. You think you are far along because all of your NSEntityMigrationPolicy objects are having their custom methods called, nothing is failing, but at the end of the migration process you’re still getting errors. SpecificallyCocoa error 1570. This implies that data validations are failing.It could easily be that it’s because you made a false assumption that awakeFromInsert will get called on your new instances during the migration process. For example you added a non-optional attribute to your model who gets his default value set in awakeFromInsert. You should specifically make sure these are getting set either via the mapping editor and your custom policy (with a custom method), or try to handle the errors in performCustomValidationForEntityMapping:manager:error:I prefer graphic tools. So I just write some boring methods that will set default values and it’s all good.
    6. IF YOU CHANGE YOUR MODEL, THEN THE MAPPING FILE WILL BREAK AND NOT BE FOUND DURING MIGRATION.IF YOU CHANGE YOUR MODEL, THEN THE MAPPING FILE WILL BREAK AND NOT BE FOUND DURING MIGRATION.This means you have to rebuild the Mapping File.
    7. In all of the NSEntityMigrationPolicy subclass, make sure you are always dealing with NSManagedObject and not their subclasses!! If you don’t, your automatic mappings (that use custom methods on the policy subclass) will fail.
    8. You don’t HAVE to create mappings in the Mapping Model editor. Basically, the part that has attribute mappings is a way of NOT overriding the method
      func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws

      and the Relationship Mappings editor is a way of not having to subclass

      func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws

      ONE THING you DO need to understand is that during the migration process, you are dealing with NSManagedObject instances, and not instances of your (likely) subclasses. So you don’t get all the benefits of the accessor methods that would ensure object graph integrity. What does this mean? You should make sure that you connect both sides of a relationship via relationship mappings. This means, in your data model it makes sense to keep a field to a ‘primary key’, so that you can use one object to find another.

    9. MAKE SURE you keep an eye on ‘optionality’ of your properties when moving from source -> destination.  If in your source you had a property that was optional, but in the destination it is not, the entire migration can fail if it tries to migrate a nil value to a property that can no longer be nil.  In this case, you may even need to create a custom policy method to generate a non-nil value if the source value for that property is nil.  Remember, if it CAN happen, it WILL happen to someone.  This is hard to believe sometimes.
    10. YAY! The Migration manager says it migrated… except… I got an exception:
      'Mismatch between mapping and source/destination models'

      I have to say, Core Data documentation sucks. There’s just not enough of it, and no explanation of why it fails during migration. Totally crap. I’ve spent a whole day just trying to shoot into the dark, hoping I’ll figure out what’s going on with this giant black box.

      The fix for this occurs in my modified MHWMigrationManager gist, and it’s what sorted me out!

Still Missing from everything that I learned:

  • I don’t really know how one is supposed to work with the createRelationships(…) method.  I only do that aspect of migration via the Mapping Model Editor and custom methods on the Policy
  • I haven’t done anything with validation yet.  So I don’t know how that comes into play.

Wrapping Up

This was a super long post.  But hopefully it can be seen as a good reference guide or at least things to think about when trying to do heavyweight migrations.  Now that I understand how they’re done, they’re not AS scary anymore.  What was annoying was trying to figure this all out.

If you benefit from this, I would be happy if you let me know!

Useful “Finder” methods on NSManagedObject in Swift

I don’t know about you, but CoreData seemed insane to me before I discovered MagicalRecord, back in the days of Mogenerator and Objective-C.

But since Swift 3 has come out, and the tools have improved to support Swift development (remember poor compiler warnings, if not just a “segmentation fault 11” error), I’m finding that I like to work with the Xcode tools again, and forego these old approaches.

My old way of doing things worked very well, and in some ways I miss some aspects of that, but ultimately I quickly (swiftly… cough cough) became a lover of Swift and simply prefer developing in that language.

What I miss most were the “MagicalFinders” categories present in MagicalRecord.  I found quite a concise way to do that however in Swift, and the code looks like this:

import Foundation
import CoreData

@objc public protocol CoreDataFinders {

    /// Because we are doing fetch requests on a data model,
    /// Fetch requests require sort descriptors.
    static func defaultSortDescriptors() -> [NSSortDescriptor]
}

extension CoreDataFinders where Self: NSManagedObject {
    
    public static func findAll(with predicate: NSPredicate?, context: NSManagedObjectContext) -> [Self] {
        
        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: Self.entity().name!)
        
        let predicate = predicate
        
        fetchRequest.predicate = predicate
        fetchRequest.sortDescriptors = self.defaultSortDescriptors()
        
        do {
            let results = try context.fetch(fetchRequest)
            return results
        } catch {
            if predicate != nil {
                print("Failed to fetch objects with predicate:\(predicate!.description) error:\(error)")
            } else {
                print("Failed to fetch objects with no predicate.  error:\(error)")
            }
        }
        return []
    }
    
    public static func findFirst(with predicate: NSPredicate?, context: NSManagedObjectContext) -> Self? {
        
        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: Self.entity().name!)
        
        let predicate = predicate
        
        fetchRequest.predicate = predicate
        fetchRequest.sortDescriptors = self.defaultSortDescriptors()
        fetchRequest.fetchLimit = 1
        
        do {
            let results = try context.fetch(fetchRequest)
            return results.first
        } catch {
            if predicate != nil {
                print("Failed to fetch objects with predicate:\(predicate!.description) error:\(error)")
            } else {
                print("Failed to fetch objects with no predicate.  error:\(error)")
            }
            
        }
        return nil
    }
}

And then you can add these to any object by either making a new baseclass in your app that subclasses NSManagedObject, or you just declare protocol support in your class definition and then these methods are added:

import Foundation
import CoreData

@objc(RecentItem)
class RecentItem: NSManagedObject, CoreDataFinders {
  static func defaultSortDescriptors() -> [NSSortDescriptor] {
    return [NSSortDescriptor(key: #keyPath(RecentItem.lastViewed), ascending: false)]
  }
  // ...
}

You can see here that you can extend this approach to pass in your own sort descriptors or limit fetch sizes, etc. This should be enough to get you started! The idea is that you can write the code once and have it apply to all instances of NSManagedObject on an opt-in basis.

Today was the first day that Test Driven Development actually justified its existence

I’ve been making apps since iOS 2.2.  People tell me I’m good at it.  Meh.  There’s always a bigger fish.  I love what I do, so chances are I’m not horrible at what I do.

Today was the first day where I used Test Driven Development to actually develop code.  Don’t get me wrong; it’s not like I don’t write unit tests.  I do.  But what I’m referring to right now is where you actually are given the start and passing conditions of a test before there is any code written at all.  In my field of work, this never happens.  The design is ALWAYS a moving target.  Nothing in the startup world is ever known in advance, so although you could write unit tests, it doesn’t always make sense.

I’ve recently been working on the implementation of the rules of Canadian Ice Hockey.  (Don’t ask.)  The sport itself seems pretty straightforward.  Put the puck in the net.  Goal.  Increase Score.  No way!  There are a lot of complicated rules surrounding penalties, but thankfully there is a referee’s handbook that goes over all the complicated scenarios and tells you what the result should be.

Perfect for TDD.  I literally wrote all the unit tests before I wrote the code that would produce the expected results.  I love it because I have to be honest; the solver code I wrote just “feels bad”.  I’m not even certain how parts of it work, and I only wrote it this past week.

What unit tests tell me is: It doesn’t matter!  As long as the tests pass, the code does what it’s supposed to do.  Very satisfying.

 

What I miss about Objective-C

Don’t get me wrong; I love Swift and it’s my preferred language.  It really didn’t take long to love it.

That being said, I really miss the idea of protected and private declarations, and creating Category Headers to expose private API’s.

It’s like giving a class “special permission” to interact with the private API’s of a different class, if there are a few classes that operate more closely, but still limiting the exposure of some properties to objects that aren’t coupled as tightly.

In Swift you just expose them all and write comments I guess.  Perhaps I’ll figure that out when I become more experienced with Swift.