Syncano — iOS — Swift — Tableview — data & images

Welcome to the second in a series of posts regarding Syncano. This post is all about getting data and images out of Syncano into a tableview, we’ll show how to send data to the Syncano platform in the next post.

I’ll be linking to various resources as we go, and you’ll also find a list of all the links at the end of the post. You will find a starter project and a completed project over on GitHub — definitely start with the starter project as it will save you time.

If you have questions or feedback please use the comments feature. Your first post will be moderated, but from then on you’ll be fine to have a conversation.

As I’ve previously mentioned, there is a growing Syncano Slack forum. It’s worth signing up to this Slack as the Syncano team are quick to respond to questions and requests for advice. You can sign up here…. http://syncano-community.github.io/slack-invite/

Goal

Ok. So what are we attempting to achieve? We’re going to build an app where a] users must sign in, b] country data is displayed in a table and detail view. Here — take a look.

Strategy

So how are we going to achieve our goal?

  • Download the starter project
  • Add the Syncano SDK to our project via a CocoaPod
  • Configure our App Delegate
  • Implement User Sign In
  • Create a Syncano data model
  • Write a fetch data method
  • Set up a tableview

Happy? Lets get started.

Download Starter Project

I already mentioned there is a started project over on Github.

Download the project from HERE. Extract/Expand the project to your local computer.

Lets take a look at the storyboard. As you can see the storyboard has all the scenes we require in place and the segues are connected. Also notice the controllers have already been created.

If you attempt to build or run the project now it is very likely you’ll get build errors, just wait a couple more steps.

If you want to skip right to the end — you can download the fully worked project HERE.

Install Syncano CocoaPod

The starter project already has a completed podfile so you don’t need to create that. However you will need to “install” the pods.

This post is not a post on how Cocoapods work or how you install pods in detail — please see my previous post on Cocoapods if you need more help.

Close the Xcode project

Because Cocoapods will update our project it is important that we close the project in Xcode.

Launch Terminal

Launch Terminal and navigate to the project directory.

Install the Syncano cocoapod

pod install

Cocoapods will output details of the frameworks being added to your project. Notice that although we only specified “syncano-ios” in our podfile, the Cocoapods application has added all the dependent frameworks that Syncano requires.

Also notice that Cocoapods tells us that going forward we must open the project using the “syncanoSeriesTwo.xcworkspace” file.

Open the project in Xcode using Terminal

open syncanoSeriesTwo.xcworkspace

Xcode will launch if it was fully closed and the project will open.

Close / Quit Terminal

Configure App Delegate

Open appDelegate.swift

Notice the line…

let syncano = Syncano.sharedInstanceWithApiKey("APIKEY", instanceName: "INSTANCE")

You need to replace “APIKEY” with the Syncano API key you created in the “Syncano — Setting Up” tutorial. Likewise “INSTANCE”, replace this with the name of your Syncano instance.

It should look something like this…

let syncano = Syncano.sharedInstanceWithApiKey("e8b1b2a61bf3ca6d7d7db307eb396e07b3ef411f", instanceName: "fragrant-lover-9387")

If you were to build/run the app now, although you would be able to enter some user credentials, you would not be able to login. The login functionality is missing. Let’s add that now.

Implement User Sign In

Open login.swift

Notice that we have an empty IBAction — the button exists, but there is no code behind the button. Yet.

Update the IBAction method to this.

To make it easy to understand I have stripped the code back to bare minimum. In the real world you will need to add validation and cleaning to your values before you attempt to call the login method.

Lets step through the code.

SCUser.loginWithUsername(textUsername.text, password: textPassword.text) { error in

SCUser is a Syncano class that represents the user and provides methods for authentication and logout etc. SCUser.loginWithUsername runs in the background and has a code block that is run when a response from the Syncano platform is received. In the event of something going wrong and the login failing, the error object details the failure.

guard error == nil else {
// Sad path
return
}

We use a guard to test if the login was successful. If there was a failure, the code in this guard will fire. You should consider adding an alert to notify the user their login attempt has failed.

// Happy path
self.performSegueWithIdentifier("login_table", sender: self)

If login was successful then we can segue to the table view.

Okay. Lets test the app as it is so far. We should be able to launch the app, authenticate and be directed to an empty table view. We can also test that failed logins do not also direct us to an empty table view.

Create a Syncano data model

Open countries.swift

Right now it’s an empty class file.

We’ll update the class file in 2 steps. First lets detail the countries attributes. Remember the Syncano name for these elements. Class = Table, Data Object = Row.

Update the class file with the following.

Let’s step through the attributes so that we can all be clear — and I can explain the second flag attribute.

// Country attributes
var name = ""
var capital = ""
var currency = ""
var timezone = 0

Here are the key data object attributes that you’ll be familiar with.

// Country flag image file
var flag = SCFile()
var flag_f : UIImage?

For the flag we have two entries. The first is the Syncano binary representation of the flag file. SCFile is a Syncano class that is provided as part of the SDK, and like the SCUser class it provides some smarts that help when working with files stored, or needing to be stored, on the Syncano platform. Right now the Syncano documentation (here) on files is a little sparse, but it is still good to review.

The second attribute flag_f is the iOS/Swift UIImage version of the file. In playing around with Syncano I’ve found this to be the easiest method of working with small images. Maybe in time I’ll revise this, or maybe you the readers, can come up with a better method.

// DB ID key
var id = 0

The final attribute is the Syncano key for the data object.

These attributes we’ve just added are the bare minimum for working with Syncano data objects.

The second change to the countries class that we’ll make is to add a method. We’ll add a method that will help us download the image files and place them into our second flag_f attribute.

Update the countries class file, adding this method.

Lets step through the code.

// Quit early if the image has already been downloaded
guard self.flag_f == nil else {
// Sad path return
}

When the method is called we first check to see if we already have the image file. If we do then we can quit the method early. Although it’s the sad path, it’s actually a really good thing if it returns early as we’re saving time/connectivity.

// Download file
self.flag.fetchInBackgroundWithCompletion { data, error in
guard error == nil else {
// Sad path
return
}
// Happy path
self.flag_f = UIImage(data: data)!
}

Initiate a download of the file in the background — and when it completes return some data and any error, and run this code.

If the error is not empty then quit early, the download has failed. Ideally you would provide some message to your user to ensure this was gracefully handled in the interface.

Now that we have some data, create a UIImage from the data and store the UIImage in the flag_f attribute.

This is the complete countries.swift class. Unfortunately we can’t test it just yet.

Fetch data

Open table.swift

Much of table.swift has been completed for you. Whats missing is: fetching data from Syncano, and, displaying the table cell. The strategy we’ll use is to place data fetched from Syncano into an array, which will provide data for the table and the detail views.

At the top of the table.swift, notice the cs array declaration is all set.

// Country data
var cs = [countries]()

First we need to fetch data from Syncano and sill the array. As it stands the fetch button IBAction is empty.

Update the IBAction to the following.

Lets step through this code.

// Clear down the holding array
self.cs.removeAll(keepCapacity: true)

First we empty our countries array, specifying to keep the memory space available.

let sync = countries.please()

Create a SCPlease connection object and bind it to the countries class on our Syncano instance.

sync.enumaratePagesWithPredicate(nil, parameters: [SCPleaseParameterPageSize : 25]) {shouldStop, s_data, error in {

There is a lot happening in this line so let’s break it down. We call the enumaratePagesWithPredicate method on our connection object. Syncano will only return a maximum of 100 data objects per request, paging resolves this limitation. I know you’re probably thinking but we only have a few data objects in our test data — it’s always best to plan for the future.

We then feed enumaratePagesWithPredicate with the following parameters.

  • nil The name of our predicate. Because we do not wish to filter our data, just return all there is, we give a nil predicate.
  • 25 The number of data objects per page of data. My preference here is to use a large enough value to ensure we fetch the data quickly, but not too large that network issues might become a problem. parameters: [SCPleaseParameterPageSize : 25])

We then have the data returned by the enumaratePagesWithPredicate method.

  • shouldStop a boolean value. false = more data on the server. true = all data sent.
  • s_data the query response data — our countries data.
  • error any error information.

Finally

in {

The start of a call back block — that will be called with each page of data.

guard error == nil else {
// Sad path
return
}

If there is an error then return out of the completion block. Of course in a real world project you would likely need to handle the error appropriately.

// Loop through results adding country data items to the cs data set
if let s_data = s_data as? [countries] {

Check that we have received data from Syncano before continuing.

for s_item in s_data {
// Download flag
s_item.downloadFlag()
// Add the Syncano country to the countries array
self.cs.append(s_item)

At this point s_data contains a collection of countries. So we loop over this collection.

We call the downloadFlag() method — which fetches the flag image from Syncano, and stores this image in the flag_f attribute. The downloadFlag method returns immediately to this loop — downloading the image in the background.

Finally, add the country to the cs array.

// Refresh the table
self.tableView.reloadData()

Now that we have some country data we refresh the table view, by refreshing the table view as each page of data is received from Syncano we keep the app interface active and engaging for users — giving a greater impression of speed.

We loop back over this code until all pages countries of data has been received from the Syncano platform.

If you want to test the app right now you have 2 choices. Either put some break points in the code and step through the code, looking at the cs array. Or, add some print/debug statements to output values as they come in and are processed.

Set up a tableview

The final step required at a tableview level is to update the cellForRowAtIndexPath to output some country data. The table view and custom cell have all been provided in the starter project so you don’t need to worry about them.

The stub method provided with the starter project looks like this.

We need to update this so that it looks like this.

Now there is a lot happening here — but you should recognise the code from previous snippets in this post, so I will only highlight some key elements.

// country flag
cell.flag.image = UIImage(named: "question")

As the table displays we push a “?” image into the image location, this ensures that at the least there is “some” kind of image displayed. You will find the “?” image in the starter project image assets.

if let f = cs[indexPath.row].flag_f {

If the flag image has already been downloaded then we replace the “?” image with the “in array” image.

Else

cs[indexPath.row].flag.fetchInBackgroundWithCompletion { data, error in

We fetch the image and replace the “?” image in the table view and we push the newly downloaded image into the countries array. The download image code in this case is the same as in the countries.downloadFlag() method.

The starter project already contains a fully wired up detail view — so we don;t need to create one of those.

Let’s test the app again.

Final test

Here we are at the end of the tutorial. I hope you’ve enjoyed the tutorial and learned how easy it is to use the Syncano platform. Part 3 of this series will show how to update data back on to the Syncano platform.

If you have questions or feedback please use the comments feature. Your first post will be moderated, but from then on you’ll be fine to have a conversation.

Links


Originally published at bizzi-body.com on April 12, 2016.