iOS 8.3+
Swift 1.2
Parse 1.7.2
2015/04/14 — Updated to cover Parse 1.7.2.
iOS, Swift 1.2, Parse.com 1.7.2
There is NO official documentation that I could find on how to achieve this — it’s assumed that you understand how to mix in other iOS components to make this all work. Hitting up Duck Duck Go and then Google I realised there is very little no sharing going on to help either. Hmmm.
Our starting point is the XCode project we created in the previous tutorial — “How to display parse.com images in a table view”. We’ll take that project and extend it to include the search capability. You might notice I have added some more countries to the data set so that we have more variations for testing.
Finally, don’t forget that I am all about Swift. My knowledge of ObjectiveC would fit in the tiny tiny space that presents when a miser opens his wallet.
Like all my posts, there is a shopping list of links at the end of the article. No leaving this page until you’ve read it all!
Goal
Strategy
We’re going to achieve the new search feature via 6 simple steps.
- Download the starter project from GitHub
- Decide on our search strategy and method
- Extend our Parse data class
- Revise how data is saved back to Parse
- Add some the search component to our story board
- Write some code to perform the search
Like other tutorials we’ll be stopping along the way to run our project and check on progress as we go.
Step 1 — Download the starter project
Over on GitHub you’ll find the starter project. Download this and make sure you can build and run the app.
The app should look this this….
The video does not demonstrate the DELETE ROW capability that was implemented in the previous tutorial, but is included in the starter project.
Step 2 — Search Strategy
I’ve included this section because search is one of those features that people (managers) just think happens. You just tick the “enable search” check box in your “developer coding thingy desktop”……Hmmm. If you’re not interested in why I chose the method I did — skip this section.
First lets check out the Parse search documentation, which appears to be pretty detailed. Excellent! There are 2 types of search method, “Where Clause” and “NSPredicates”.
If you are hoping that Parse will allow you to complete complex searches like within the contents of office type files, or search for objects within images you are going to be disappointed. While the documentation does hint that Parse are able to search against large text/numeric data sets quickly enough to make it viable I have a suspicion that these “large” data sets would be tall and narrow (content per record is not great, but there are lots of them). As apposed to searching tall and wide data sets, where I would guess performance is impacted. What I’m saying is that for many (?most?) app projects Parse search will be great — just make sure you validate this before you start coding.
Predicates
I’m a fan of predicates. They make defining search criteria a sinch, they are easy to read and easy to validate in your head. Unfortunately Parse predicates are limited in capability, missing key matching methods — LIKE, MATCHES, CONTAINS and ENDSWITH. So predicates are not going to help us. I really hope that in time (soon) Parse invest in this area.
Where Clause
With predicates not able to provide the matching methods we need we must look to the more traditional “Where Clause” for our search feature. Unfortunately Parse Where Clauses are also a little limited, eg: you can only search against 1 column and you can not specify case sensitivity. Fortunately with our small data set we can work around these limitations by concatenating the text we need to be searchable into it’s own column. (Parse have a blog post that discusses this tactic — see the links section for details)
Solution
We now know our search strategy. We’ll concatenate the text we want to be searchable into a new Parse column, ensuring the text in this column is all lower case.
Step 3 — Extending our parse data class
Let’s log into Parse and add a new column to our Countries Parse data class. We’ll call the new column searchText and it will be of type String.
Update Parse class definition — add searchText column
That’s it. Step 3 = TICK!
Step 4 — Revise how data is saved back to Parse
The next step is to update the Save and New functions within the app so that our new Parse column gets some data. For the purposes of this tutorial and keeping it simple we are going to update our new column in the app.
There are many good reasons why you should update this column using the Parse “Cloud Code” features — reduces data sent to Cloud, improves app performance, allows for more complex algorithms, allows updating the logic without having to release a new version of your app — I’m sure there are more. But all of that is beyond the scope of this tutorial.
So. What are we going to put into this new column?
There are many strategies we could take in updating our new column. We could create an array of unique words, which would reduce the amount of data sent/stored and needing searching. We could remove common words like “and” or “the”, again reducing data sent/stored and needing searching. All these can be great but ultimately they can lead to a crappy user experience.
In my time I have come to realise that users don’t want to have to think about how they search. I’m convinced users just want to enter a few words or a simple sentence. If more than one word is entered for search then the order of words will likely be important. I’m all for a simple life so we’ll keep our searchText column simple.
We’ll just concatenate a string of all our data columns — minus the flag column and convert our new string to lowercase — remembering that searches are case sensitive, and store this whenever we update data back to Parse.
We need to update some code in DetailViewController.swift. Specifically the saveButton() IBAction method.
Replace the existing method with this new code and then we’ll walk through and highlight the important changes.
First let me just say that this code is not optimal, and could/should be refactored — but not today.
This line concatenates the text together and converts is to all lower case. Notice that we’re using the 2 names, the capital and the currency code.
var searchText = (capital.text + “ “ + nameEnglish.text + “ “ + nameLocal.text + “ “ + currencyCode.text).lowercaseString
This line pushes our new string into the searchText column. Very simple.
updateObject[“searchText”] = searchText
Notice how I am duplicating these lines, once for updating an existing record and again when creating a new record. Truth is that this method needs refactoring for efficiency of code, but I leave it as is for this tutorial because I think it is easier to understand what I am doing.
Test #1
Right there are rows in our data set where the searchText column is blank. Lets build and run the app, edit/saving the existing country data so that push data into this column — and test that our new code works as we hoped.
Step 5 — Add some components to our story board
Step 5 is all about adding the search feature to the app interface. We’ll add the search bar to the table view, configure the search bar delegate methods and then update the Parse query method. Lots to do!
Open up the story board and drag on a UISearchBar — position it at the top of your table view.
There are several settings we need to make to the search bar.
First let’s make sure the “Cancel” button displays — this will allow users to clear the search and re-present all rows or data.
Second let’s make sure the keyboard doesn’t make life difficult for users. Make sure capitaisation, correction and spelling are set to none/no. And, set the return key to “Search”.
Now lets add the outlet code to our Table View class.
Add the following code to TableViewController.swift
Okay. Now switch to assistant editor mode and drag the outlet handle to the UISearchBar in the storyboard.
Excellent now we are ready to implement the delegate methods which allow us to control the cancel button and keyboard behavior. For the Cancel button our goal is to ensure the button clears any search and resets the data results. For the keyboard the goal is to ensure that when the user clicks outside the keyboard or clicks the “Search” button — the keyboard dismisses and the search is performed.
First step is to add the UISearchBar delegate to our table view class definition. Update the class definition to…
Update the viewDidLoad() method so that we set the delegate on the search bar.
Now we need to add the following delegate methods to the table view class — these are the methods that help the keyboard play nicely.
These methods dismiss the keyboard when the user clicks outside of the search bar, when the user clicks Search and when the user clicks Cancel. The cancel method also clears any search criteria the user may have entered.
Phew! Nearly there. Now we need to update the parse query to include any search criteria.
We already have a query method within our table view controller which returns all the data from parse. Update this method to include a where clause if we have a search criteria.
The key lines are.
// Add a where clause if there is a search criteria if searchBar.text != “” { query.whereKey(“searchText”, containsString: searchBar.text.lowercaseString) }
This code looks to see if there is any text in the search bar. If there is then it converts this to lowercase and adds this as a parameter to a whereKey where clause.
We’re done! Let’s test our hard work. Build and run the app. We’ll try searching for “eur” — this should return all the countries using the Euro currency.
Final Test
Congratulations!
We’ve created a searchable table view with data hosted in the Parse cloud platform.
Links
Originally published at bizzi-body.com/blog on March 8, 2015.