Regular Expressions on iOS

I’ve decided to take on the masochistic task of getting my hands dirty with NSRegularExpression. Here are some of my thoughts and findings:

– Regex is voodoo. What’s worse, it’s not even standardized. So all the tutorials on the web are not platform agnostic. I could use a recipe to match the things I’m interested in, but when creating an instance of NSRegularExpression on iOS, it fails as “invalid pattern”.
– Because of this, you WILL become a test driven developer. ūüôā
– Start with a simple regex pattern that detects PARTS of what you’re looking to match, then add complexity. This way you can see what part of your pattern is failing.
– Don’t use the Number Sign # as a matchable character. It is protected but not documented! Use \u0023 instead. e.g. “\\b
– When you map/inspect your NSTextCheckingResult objects, it should have the same number of ranges as you have capture groups. You use these to get more specific about which capture groups were involved in the match. (Provides more granularity/control). The NSTextCheckingResult.range represents the range in the text associated with that one result!
– NSRegularExpression really doesn’t deal well with the hash symbol (sharps).

I ultimately reached a solution, but it never felt entirely ‘correct’ despite finding a way to produce the correct results.

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.

Developing for iCloud sucks

iCloud is the thorn in Apple’s side. ¬†They have been trying to get this right forever. ¬†Maybe it works now from an end-user perspective, but as a developer, what a rough, confusing implementation.

I have to say, when you work with the Dropbox API, it is intuitive, it is easy, it is straightforward. Perhaps you have to write a lot of boilerplate code, but then again, do you?  It manages files.  This name, that path, that revision number, that modifiedDate.  You sort out the rest.  How hard can that be?

I’m currently writing a sync engine that allows users to decide which cloud service they want to use to backup their data. ¬†So of course it would make sense to have one common API that wraps the various services.

It went so smoothly with Dropbox. ¬†The only difficulty I had was realizing that the serialization of date strings sometimes results in small differences in the Date that emerges, meaning I had to write a .isTheSameAs(other: Date) method, which checks to see if they are within X seconds of each other, then call those ‘the same’.

Then I moved on to iCloud. ¬†What a clusterf‚ÄĘck. ¬†Talk about over-engineering and complete inflexibility. ¬†I just want a list of files that are in the cloud. ¬†No, Apple has to distinguish whether you have them locally or not, and gives them a different URL. ¬†And all sorts of other weird stuff.

So I tried to find a framework that makes iCloud a little more user-friendly, called iCloudDocumentSync which I found to be pretty straightforward, all things considered. ¬†And yet, it’s still a pain. ¬†Because iCloud uses multiple URLs to represent the same document. ¬†In the end, I have to sync the entire iCloud folder’s contents before I can start working with these with a common URL. ¬†(Sure I could extract the file name I need). ¬†Moreover, iCloud uses a thread to open each document, so if I want to batch download a lot of data, suddenly I have a thread explosion. ¬†Now I have to deal with managing that.

All in all it’s just been a pain to work with it. ¬†It seems opaque in its verbosity. ¬†Meaning, the person who talks a lot doesn’t get listened to and understood. ¬†There are so many callbacks and all sorts of weird things going on, it’s very annoying. ¬†Just because they’re trying to save the developer time, they’ve implemented something weird and unwieldy if you don’t use it according to their use case. ¬†I personally just use a UIDocument as a means to save data. ¬†I don’t work with UIDocuments because my app’s data model uses the data, but not the file. ¬†This just seems silly with iCloud.

Autolayout and Self-sizing UITableViewCell

I’m not going to lie.¬† Autolayout is a massive pain.¬† But.¬† Ultimately it’s very powerful and you’re best to just go through the pain and learn it.

Even so, you should also get a bit familiar with it, then learn about the concept of self-sizing table view cells.¬† It’s quite important.¬† Basically, as long as there is a clearly defined way for a¬†UITableViewCell to determine its own height via the auto-layout constraints, dynamic table view cells are pretty easy.

Unfortunately, there is a lot to learn:

This series

Then this

or even Apple

Quick Reference:

How to make sure a UITableViewCell autosizes when you have a label that you want to wrap around onto multiple lines.

1. Pin the Label at top left, bottom, right.

2. Edit the right constraint to be “Greater than or Equal to”, and then set the constant to be the right-most you want to allow that (probably view margin).

3. Set that right-most constraint’s priority to 750 (high), then set (on the Label!) it’s content compression resistance priority to 749.

Should be fine now.

Alamofire and nostalgic feelings for AFNetworking

So, in the Swift world, I presume a lot of people prefer to remain “Swifty”. ¬†That is, why would you opt for old Objective-C libraries? ¬†Out with the old, in with the new!

So, we adopt Alamofire.  Is it just me, or is this library a bit unwieldy?   I think it has a lot to do with Swift shorthand and often not knowing just what types are actually being passed around, especially given autocomplete.

On top of that, just when you get your head around a specific release, they make a new major release, breaking old APIs. ¬†And if I’m going to have to keep updating my code to stay current because “out with the old, in with the new”, why don’t I just stay with AFNetworking? ¬†I mean come on. ¬†It’s networking. ¬†For the mostpart it’s just “give me that data at that URL. ¬†Here’s my auth info.” ¬†Done. ¬†Or “here’s some data for you. ¬†Here’s my auth info.” ¬†Done.

Anyway, it’s a rant. ¬†I just don’t find Alamofire all that sexy. ¬†It reminds me of dealing with civil servants. ¬†The creators imagined this bureaucratic utopia that functions perfectly as long as everyone understands it. ¬†Furthermore, we must¬†not only understand it, but are fully on board with its vision. ¬†Meanwhile, we the people are busy trying to write our¬†own apps, and couldn’t really care less. ¬†We¬†just want to get data and post data and not have to deal with too much crap in the middle.

(Go ahead, snarky programmer. ¬†Now tell me off, tell me to just use this, that, or the other. ¬†Reject my feedback. ¬†It’s fine. ¬†All I’m saying is that AFNetworking seemed a lot easier to use.)

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.