Notes

June 2017
Regex Nightmare

I am a big fan of the Swift language, and I am so excited about where its heading. There’s not a lot to complaints besides some of the rough edges. It’s still young after all. Dealing with Regular Expression is one of them that annoys me the most.

Lots of love to String, Great, what about regex?

The swift team have given String lots of love in Swift 4. One thing that is missing out for me is that the regex part remains untouched. In my opinion, regex is an essential part of string manipulation in any language. Ruby, JavaScript, Python … you name it. They all have built in, easy to use regex APIs. NSRegeularExpression is the worst regex API I have ever used across the all languages (and I use a LOT of languages).

The current problems

There are two 2 main issues here.

NSRegeularExpression is not defined on Linux version

Swift is ditching NS prefix for all the new types. But there’s still no RegularExpression. However, on the Linux version, you can’t find NSRegeularExpression. So here is the workaround I did.

#if !os(Linux)
    typealias RegularExpression = NSRegularExpression
    typealias TextCheckingResult = NSTextCheckingResult
#else
    extension TextCheckingResult {
        // yeah, you have to deal with this
        func rangeAt(_ idx: Int) -> NSRange {
            return range(at: idx)
        }
    }
#endif

It works with NSRange instead of Range<String.Index>

The above one is just annoying, but we can live with it. This one is a little bit trick. With a String, all the functions that accept or return a range will give you Range<String.Index>. NSRegeularExpression only accept NSRange for a range. The trick part is, it accept String instead of NSString for the string. The following code will demo the issue.

var str = "Hello😀"
var nsStr = str as NSString
str.characters.count // 6
nsStr.length // 7

Yes, you can freely cast between NSString and String with minimal performance penalty. But the fact that the length of the string is not consistent causes big problems. Consider the following code:

var str = "Hello😀World"

str.characters.count     // 11
(str as NSString).length // 12

var regex = try! NSRegularExpression(pattern: "World", options: [])

let match = regex.firstMatch(
  in: str,
  options: [],
  range: NSMakeRange(0, str.characters.count))

// the match would be nil.

The most confusing part of this code is the fact that NSRegeularExpression accept String as input, but not respect it’s characters.count. The workaround would be:

let match = regex.firstMatch(
  in: str,
  options: [],
  range: NSMakeRange(0, (str as NSString).length))

I really hope that’s the end of the story, and I we need to do is to put up with the ugly code until swift team fix this for us. Unfortunately, it is not. Well, it can end here. The only thing you need to do is to remember to convert String to NSString every time you use NSRegeularExpression, or use NSRange with them. But that would be so error pron since majority of the functions accept and return type String, even NSRegeularExpression itself.

let nsRange = match!.range // got the range from a regex match
let substring = ( str as NSString ).substring(with: nsRange)
// "World"

If you plan on using String all the time, which you should, then here is how to convert NSRange to Range<String.Index>.

extension String {
    func range(from nsRange: NSRange) -> Range<String.Index>? {
        guard
          let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
          let to16 = utf16.index(from16, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
          let from = from16.samePosition(in: self),
          let to = to16.samePosition(in: self)
        else { return nil }
        return from ..< to
    }
}

let substring = str.substring(with: str.range(from: nsRange))

From the look of the code, it is so compiler-optimizable. Which means if this is taken care by the built in framework (like a real RegularExpression type), it can be optimized down to nothing. Because technically you can directly access the raw value of the index, instead of doing all the offset work. I know by the natural of the offset behavior, it’s not going to be computational intensive. But when you have massive of conversions between NSRange and Range<String.Index> type in your code, it really adds up.

Looking Forward

It is a hard problem to solve. Especially when Apple added native emoji support in the String type. Regex was not designed to handle this kind of issue. But I really looking forward to see how smart people in swift team crack this.

June 2017
Rude Developers

Some Developers are rude. This happened on my work machine (A windows machine). It prompts a form for you to fill in regarding when should it update your system. When I choose outside of business hours, it prompts “there is a conflict about the time, some of the update needs to happen during business hours”, then the whole interface just went away. What the F? I found this kind of rudeness happens more regularly on a windows than a Mac.

April 2017
swift method dispatch

A very good explanation can be found here. Here is a summary.

Direct Table Message
NSObject @nonobjc or final Initial Declaration Extensions, dynamic
Class Extensions, final Initial Declaration dynamic
Protocol Extensions Initial Declaration @objc Declarations
Value Type All Methods n/a n/a
April 2017
cross-platform swift

After reading this, I am deeply worried. It involves lots off fiddly to make it work. However, after swift 3.x (I don’t know which one was it), we have a better swift package generate-xcodeproj command which does lots of the fiddly for you. It worked, almost, until I try to run unit tests for a different platform, say iPhone. The problem is here.

I’m no stranger to these sorts of problems, fortunately, (sadly); the problem manifests because iOS and macOS have different test-bundle layouts, so your framework will be placed in different places in each, thus you have to tell your tests to look in different places for each. The easiest way is just to set the test rpaths to:

From Multiplatform Single Scheme Xcode Projects·PromiseKit.

So here is what you do.

  • Go to Build Settings of your test target. Search for run. You will see Runpath Search Paths.
  • Add, under both Debug and Release, Any OS X SDK. Set the value to @loader_path/../Frameworks. (it was @loader_path/Frameworks originally.)
  • You are ready to go.
September 2016
Don't be an Agile Fanatic

Agile is not for everyone.

Take our team for example. We have 5 people, one of them is a team lead. We basically are 5 DRIs (Direct Responsible Individual) of some projects. But it doesn’t mean we don’t need to talk to each other. We talk to each other constantly because we sit next to each other. We just don’t need to collaborate very closely like normal development teams. By the way, a well designed architecture do a whole lot of more communication for itself. I found that it is efficient enough for us to understand the situation over all. Of cause we still need a meeting where we all sit together to clear some confusions occasionally, especially when there is a big change upstream that will alter our original roadmap. But that doesn’t happen every week, or month, and it shouldn’t. (If it does, we have a more serious issue to deal with.) We do iterate, but we iterate in our own pace. There isn’t a weekly/fortnightly meeting sitting in our calendars. IMO, we are agile in our way. It all comes down to communication. My point is: Agile methodology is nothing more than a collection of Good Ideas, you can pick some of them that apply to your team, just don’t build a religion around it.

August 2016
Finally Dipping my Toes in Swift

Swift is getting there. The language is maturing by day. The community is incredible. With version 3 coming, I think it is time to dip my toes in it.

The Experience

The language feels extremely elegant in my opinion. I agree most of the decisions made by the designer of the language. Which makes it very easy for me to learn. Took me an hour to get the big picture through Getting Started with Swift - WWDC 2016, which by the way, is a good starting point for anyone with background of other programming languages. And roughly another hour to play with it in Xcode. Then I started to write real code.

Dependency Manager

A good dependency management system is essential for any project. After digging around, I found that cocoapods is the established solution for that. However, I found Carthage/Carthage is the new black now. Also personally I like Carthage’s design philosophy better. I followed Creating your first iOS Framework from thoughtbot to setup my own framework project.

May 2016
About Notes

It took me sometime to realise that I am not a writer, yet. I am struggling with converting my thoughts into a cohesive article. I believe the reasons behind this are:

  • I don’t have large block of time to process the thoughts, polish them, and write a long form article around it.
  • Different ideas come and goes, but very small, and very different.
  • English is not my native language.

So I decided not to wait for them to be crafted into articles before publishing them. Instead, I will just share the short notes directly, like this one. When time comes, I can still connecting the dots and make it complete.

April 2016
Detail Matters

Whenever some friends who just converted to Mac from Windows ask me questions like: Where is the start menu? How to I open “my computer”? Hey, I downloaded the app, I double clicked it, and it just open, how do I install it? I feel sorry for them. It is just so sad.

Recently I got into debate with one of my colleague, about a minor design difference between Windows and OS X. On Windows, you can right click anywhere, desktop, or a folder within file explorer, and select “new file” in the menu to create a text file, then you can give it a name, then you got an empty file with 0 bytes. Then you can double click to open it with a text editor start working on it. You can’t do it on OS X. It sounds like a good feature, or at least a convenient shortcut to quickly create a file. But really think about it, what do you need an empty file for? Your next action after you created the file is always going to be opening it with a text editor. So let’s count: find the place -> right click -> left click on new file -> type in file name -> hit enter to confirm -> double click the file (assuming you have the right file type association, otherwise you have to: right click the file -> open with -> sublime text). So at least 6 steps to create and open an empty file in a particular place. Yeah! What about: open the text editor -> start typing -> save with name and location. So 3 steps to create a file and write the content into it and save it in a particular place. So I would take that menu option out if I was making the decision.