Nothing like vacation to recharge batteries

After almost two years of ’stay at home’ vacation this year my wife and I went to Egypt and Jordan. 

I forgot how great is to travel, meeting new people, learning new cultures, visiting new places… It was an extremely enjoyable trip, but more importantly, it helped recharge batteries and come up with new ideas.

IMG 9973

Now that I’m back, in the next couple of weeks I’ll be accelerating work on the new app -idea is to ship shortly after macOS 12 ships- and continuing to work on ML on the side. 

Will keep you posted as the launch nears. 

Until next time, 

Marc

 

PHP maintains an enormous lead in server-side programming

When I choose the backend code for NewsWave several years ago I went with PHP after listening Marco’s recommending it due to its reliability and stability. After several years I can confirm. 

Apparently a lot of other fellow developers think the same looking at the latest data from Ars Technica.W3techs chart 800x444

If you’re planning a new project, I can’t recommend PHP enough for server-side. It may not be sexy, it may not be cool but it consistently delivers without a hitch. 

Marc

Using NSSortDescriptor to sort Dates and nil values

Today I’ve been working on sorting tasks, as I’m using swiftUI and CoreData I use a FetchRequest with the following SortDescriptor:

NSSortDescriptor(keyPath: \Task.targetDate, ascending: true)

The idea being it will sort based on the task due date from smallest to the largest, meaning you’ll see the ones you should act on first at the top.  

Great right?

Well… Although the sorting works as intended, if you have tasks that have a nil due date they will appear at the top. And, because we’re using the SwiftUI fetch request there’s no easy way to subclass it.

Untitled Artwork

The easiest way around it, combine it with a sorted by within the List, in the View body. Here’s  the implementation:

    var body: some View {

        List(tasks.sorted(by: { ($0 as Task).targetDate ?? Date(timeIntervalSinceNow: 9999999999999) < ($1 as Task).targetDate ?? Date(timeIntervalSinceNow: 9999999999999) }), selection: $selectedTask){ task in

And voila, now appears as intended:

Screen Shot 2021 09 11 at 1 56 48 PM

Hope it helps, if you find a better solution by all means let me know at @MarcMasVi

 

Marc

Multiple Word, Random Order Search – CoreData & SwiftUI

Today I worked on the search functionality for the upcoming Product Management app. Given that this app will contain lots of long notes, having good search is essential.

Screen Shot 2021 08 27 at 5 06 07 PM

In most cases & for most apps, a simple one to one comparison will do the job great -like in the above image-. Here’s how a predicate implementation one to one could look like. Lets say you have a list of names in Core Data and you want to search for John in them:

let pred = NSPredicate(format: “name CONTAINS[cd] %@”, searchTerm)

Great! Well… great for the use case mentioned, but what if instead of names you have long notes and the search words could be out of order? Not. So. Great…

Lets imagine the following two notes:

1. “Today John works at the factory, he delivered 10 new bottles”

2. “We’re not sure what we’ll do with John this weekend, maybe we’ll work on the paper due next week”

The NSPredicate we typed above would match the former but totally miss the latter. That’s because in the first line text contains the combination “John works” one to one (in that precise order) while in the second line the text does not. 

In the second example words are out of order so our predicate misses them. To solve this, let me introduce you to NSCompoundPredicate

NSCompoundPredicate allows you to combine multiple predicates in one array that is searched in one CoreData fetch, making it hugely powerful. 

Here’s a swiftUI implementation that would handle the out of order use-case:

    .onChange(of: searchableText) { searchableText in

        var predicateList: Array<NSPredicate> = []

        if !searchableText.isEmpty {

            for iterator in searchableText.components(separatedBy: ” “){

                if iterator == “”{

                    continue

                }

                let pred = NSPredicate(format: “text CONTAINS[cd] %@”, iterator)

                predicateList.append(pred)

            }

            someElement.nsPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateList)

        }

    }

In this example I take the search query, split it in words and create a predicate for each. As long as the note contains the words, does not matter the order, it’ll find the right match. 

NOTE: If you don’t know what’s [c] and [d], that’s a way to tell NSPredicate it should not care about capitalization nor diacritics for the given keyword, in our case ‘CONTAINS’. 

That’s it, nice and easy. 

Happy Friday, 

Marc

Counting CoreData entries based on relationship entities

As I continue working on the new product management app, I was trying to get a quick CoreData count. To be precise,  I needed to get the count for how many of its relationships met a given criteria. There’s quite a few approaches but none as clean as “the filter”

Screen Shot 2021 08 22 at 4 49 59 PM

Using it for boolean arguments:

      entity.childOfEntity?.filter({ ($0 as! entityType).booleanAgument != true}).count

Using the image example above:

      noteObject.tasksOfNote?.filter({ ($0 as! Task).completed != true}).count

Using it for string arguments:

      entity.childOfEntity?.filter({ ($0 as! entityType).stringArgument != ”Something}).count

Definitely recommended it for most CoreData relationship work, simplifies things quite a bit. 

 

Marc

Developing on the cutting edge

If you’re working on a new app using the latest Xcode & macOS Monterey betas be aware of a bug that leads to SceneStorage failures appearing on the console:

Failed to restore SceneStorage with key selectedTaskId. at SwiftUI/SceneStorage.swift:105
[…]
Failed to restore SceneStorage with key sidebarSelection. at SwiftUI/SceneStorage.swift:105
Binary[2571:23261] Checked for defaults IMKUseDistributedObjects. result is 0
Binary[2571:23261] IMK will use XPC for App = com.Binary.Binary, euid=501
Binary[2571:23261] IMKInputSession [0x6000004e4a20 activate] Invoking activateServerWithReply:, from Main thread = 1, bundleID=com.apple.PressAndHold

It’s not you, it’s the Beta. Same if after updating to 12B3 you start getting EXC_BAD_ACCESS crashes, I fixed that by updating to XcodeB3.  

Screen Shot 2021 07 18 at 10 30 54 PM

Working with the latest betas is a double edged sword 🙂 

Happy Sunday, 

Marc

What Ever Happened to IBM’s Watson

Very interesting article from the NY Times on the ‘downfall’ of IBM’s Watson. 

Iu

Maps quite well with what Steve Jobs said many years ago:

“I have my own theory about why the decline happens at companies like IBM or Microsoft. The company does a great job, innovates and becomes a monopoly or close to it in some field, and then the quality of the product becomes less important. The product starts valuing the great salesmen, because they’re the ones who can move the needle on revenues, not the product engineers and designers. So the salespeople end up running the company.”

Read it here

Rich Barton: Expedia & Zillow

“How I built this” is a podcast where successful entrepreneurs are interviewed in a very candid way. The ups and downs, the complexities, the long nights… 

Today I listened to Rich Barton, the creator of Expedia, Zillow and GlassDoor. Both the show, and this specific episode are great: https://overcast.fm/+YsraNZ6eo

Iu

On a related note, here’s some of the improvement Zillow has done lately when it comes to AI: https://www.wired.com/story/zillow-taps-ai-improve-home-value-estimates/

 

Marc

Identifying combinations of dates in text using regex

For the upcoming app I’m working on I need a way to easily detect dates the user may have typed at the end of a line. It was time to re-visit old faithful regex

Ewrsit7pbw671

After lots of readings and tests I ended up settling on this beauty: 

     “.*[0-9]{2}/[0-9]{2}”

Here’s what it does:

The first section means take any character from the beginning of a line:

     .  Any character except new line

     * Zero or more charecters

provided it ends in the second part…

     [0-9]{2} Two numbers between 0 to 9

     /  This specific seperator

     [0-9]{2} Two numbers between 0 to 9

So in essence, it will take any line that ends in xx/xx where xx is any combination of two digits. That’s it, clear and simple!

***

Now, all we have to do is expand the number of cases to account for the user typing only one digit for the month or day.  

Here’s a swift array containing each case: [“.*[0-9]{2}/[0-9]{2}”,”.*[0-9]{1}/[0-9]{2}”,”.*[0-9]{2}/[0-9]{1}”,”.*[0-9]{1}/[0-9]{1}”]

One thing I’d point out is that the order of the regex pattern matters, it should go from most complex to most simple as regex will stop once it finds a suitable match. 

Here’s an example of all of it working together (in this case I’m also adding a year pattern):

func dateMatches(text: String) -> [String] {

    let regexPatternMonth = [“.*[0-9]{2}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{2}”,“.*[0-9]{2}/[0-9]{1}”,“.*[0-9]{1}/[0-9]{1}”]

    let regexPatternYear = [“.*[0-9]{2}/[0-9]{2}/[0-9]{4}”,“.*[0-9]{1}/[0-9]{2}/[0-9]{4}”,“.*[0-9]{2}/[0-9]{1}/[0-9]{4}”,“.*[0-9]{1}/[0-9]{1}/[0-9]{4}”,“.*[0-9]{2}/[0-9]{2}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{2}/[0-9]{2}”,“.*[0-9]{2}/[0-9]{1}/[0-9]{2}”,“.*[0-9]{1}/[0-9]{1}/[0-9]{2}”]

    

    

    let regexPatternsToMatchArray = regexPatternMonth + regexPatternYear

    do {

        let regex = try NSRegularExpression(pattern: “(\(regexPatternsToMatchArray.joined(separator:“|”)))”)

        let results = regex.matches(in: text,

                                range: NSRange(text.startIndex…, in: text))

        let finalResult = results.map {

            String(text[Range($0.range, in: text)!])

        }

        return finalResult

    } catch let error {

        print(“invalid regex: \(error.localizedDescription))

        return []

       }

}

That’s it, you can call this function and it will return the array of matching sub-strings.

From there you can process convert them into dates (make sure to account for localization as some countries use day/month and others month/day). 

Hope this helps, if you’re new to regex here are some useful references: regex cheatsheet, validatormatching valid dates and ios regex.

Comments, questions I’m at @MarcMasVi on Twitter

Marc