Monday, August 31, 2015

Showing Charts

For my existing app, Pain Logger, my new app, and pretty much any other app idea I have right now, I need to display graphs.  In Pain Logger I originally used CorePlot and found it worked well for the time.
But with move to all Swift and particularly my current effort to rewrite Pain Logger in Swift I decided to take a look at how things have changed in the chart generation side of the world and particularly with a bent towards Swift.
I found ios-charts (https://github.com/danielgindi/ios-charts) and decided to take a look at it for my needs.
To do this I decided I would try to use it in an Xcode Playground so I could iterate on the different things I wanted to try rapidly.
I first started by downloading the project from github with the link provided above.  Since I am targeting the latest version of Swift, I checked it out from the “swift-2.0” branch.
Next I created my playground in Xcode and added the package’s files.  This was a little tricky as when you get the iso-charts package you will essentially get an XCode project with a structure that looks like this:
Screen Shot 2015 08 30 at 11 52 01 AM
In a normal project where you were using iOS-charts you would either use CocoaPods or manually add the project as a dependent project.  But with a playground this really isn’t possible.  So what I ended up doing was coping all the files from the “Classes” folder (shown above) into the Sources folder of my playground.
To test this out I created a simple view and added a BarChartView to it like so:
import UIKit
import XCPlayground

let chartView = BarChartView()
chartView.bounds = CGRect(x: 0.0, y: 0.0, width: 375.0, height: 200.0)
chartView.center = CGPoint(x: 375.0/2.0, y: 200.0/2.0)
chartView.noDataText ="You need to provide data"

let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 200.0))
containerView.backgroundColor = UIColor.grayColor()
containerView.addSubview(chartView)
XCPShowView("Container View", view: containerView)
At this point the playground could still not find the ios-charts code so, I ended up moving the class files out of their individual folders and placed them all at the root of the Sources folder for the playground.  The first part of this looks as follows:
Screen Shot 2015 08 30 at 11 59 53 AM
This change got it working.
Screen Shot 2015 08 30 at 2 03 57 PM
Now I was ready to try some things out.
To meet the requirements for Pain Logger I need a line graph where I can plot multiple data.  The y -axis needs to run from 0 to 10 and the x-axis needs to be a date range.  Additionally, for a new feature I am adding in the next version, I need to be able to call out specific data points.  All of these needs to be presented in an aesthetically pleasing way.
So I embarked on an effort to see how close I could get.
My first effort was just to get a line chart showing. To do this I changed the chartView from a BarChartView to a LineChartView and added the following code.
func getEntryArray(dataPoints:[String], values:[Double]) -> [ChartDataEntry] {
    var entries = [ChartDataEntry]()
    for i in 0..<dataPoints.count {
        let entry = ChartDataEntry(value: values[i], xIndex: i)
        entries.append(entry)
    }
    return entries
}

let values1:[Double] = [0, 3, 2, 6, 5, 7, 2]
let values2:[Double] = [2, 4, 1, 1, 0, 2, 2, 4, 4, 4]
let range1 = ["Aug 1, 2015", "Aug 2, 2015", "Aug 4, 2015", "Aug 5, 2015", "Aug 6, 2015", "Aug 8, 2015", "Aug 15, 2015"]
let range2 = ["Aug 1, 2015", "Aug 3, 2015", "Aug 4, 2015", "Aug 8, 2015", "Aug 9, 2015", "Aug 10, 2015", "Aug 11, 2015", "Aug 13, 2015", "Aug 14, 2015", "Aug 18, 2015"]

var dataEntries1 = getEntryArray(range1, values: values1)
var dataEntries2 = getEntryArray(range2, values: values2)

// Get and configure the line chart data set
let lineChartDataSet = LineChartDataSet(yVals: dataEntries1, label: "Y")
lineChartDataSet.fillColor = UIColor.redColor()
lineChartDataSet.drawCirclesEnabled = false
lineChartDataSet.drawFilledEnabled = false
lineChartDataSet.drawCubicEnabled = false
lineChartDataSet.drawVerticalHighlightIndicatorEnabled = true
lineChartDataSet.lineWidth = 2.0
lineChartDataSet.valueTextColor = UIColor.whiteColor()

let lineChartData = LineChartData(xVals: values1, dataSet: lineChartDataSet)
chartView.data = lineChartData
chartView.backgroundColor = UIColor.blackColor()
chartView.drawGridBackgroundEnabled = false
chartView.drawBordersEnabled = false
chartView.legend.enabled = false
chartView.rightAxis.enabled = false
chartView.leftAxis.labelTextColor = UIColor.whiteColor()
chartView.leftAxis.startAtZeroEnabled = false

The first part of this sets up a helper function to collect data into the required arrays for ios-charts.  Next is my sample data.  My intent was to have two lines show up (hence the values1 and values2 arrays).
Finally I configured the line chart data set and the line chart view itself.
This resulted in the following view.

Screen Shot 2015 08 30 at 3 23 49 PM
At this point I have a single line graph showing, but it’s not close to the result I am looking for.
After looking at the documentation, it appears that I can only have a single line on a line chart.  This is a problem.  I need to explore the possibilities more but it appears right now I need to go look at other graph solutions.  Rather than continue to iterate on this I think I will end this post here.
What I have shown is how to setup a playground to use iso-charts and I have shown how to use a few settings.  Overall the package presents well and there are several other types of charts it can render, but right now it doesn’t look like it will support my basic requirements, so I am going to move on to another package.

Till next time.

Monday, August 24, 2015

Rethinking CloudKit

Ok, when I'm wrong, I'm wrong and I will admit it.  My previous post on using CloudKit in my new app, was a bit naive so I am rethinking how I intend to use it.

I actually had it working with my top level object. But things went a bit awry when I started extending the design.

As described in my previous post the design supported a local store managed by a LocalDataManager class.  All view controllers used CRUD operations through the LocalDataManager.  The LocalDataManager, upon receiving a CRUD request, would pass the object to a RemoteDataManager class which would transform and update it in CloudKit.

The RemoteDataManager would then notify the LocalDataManager with the updated record after successfully making the change to the CloudKit store.

The LocalDataManager would then update it's local store appropriately and notify any views about the changes that just occurred.

Where this design has difficulty and needs some more thought is when there are related objects.

For example say we have a top level object called Category.  A Category can have many notes. Additionally when viewing a list of categories you want to know how many notes each one has.

Since CloudKit isn't a truly relational database you don't have access to the notes of a Category when you first load it.  In fact, using the recommended CloudKit design pattern, Category objects don't even know they have notes.  Instead, a Note object has a "back reference" to it's owning parent.

What this means is if you wanted to know how many notes each Category had in a list of categories you would need some way of knowing the note count without out actually loading the Note objects themselves.

For this example I will assume the desired solution is to store a count on the Category itself.  Note: I am ignoring other more traditional SQL counting solutions which would also work with the CloudKit store, but might require a separate network call.  

The reason I am ignoring this is that in my app the value stored in the parent object is not a count but more of a metadata type value which describes some attribute of the collection of dependent objects (such as an average over some field's value, etc.)  I am using the count value in this example only to keep it simple.

Ok, getting back to the example. Now let's say you want to store a new note.  So in my design you would need to store the Note then update the Category so it's count of notes is correct.

This causes two store operations to CloudKit both of which may fail.  So you now have a few cases to handle.
  1. Note stores successfully, but Category does not
  2. Note and Category store successfully
  3. Neither the Note or Category store successfully.
If you look at this pragmatically, only case 1 is of concern.  If case 2 happens you are good as everything saved correctly.  If case 3 happens you are still good as your persistent stores both locally and remotely have not been polluted.

But what do you do about case 1.  This is where I am stuck and I think my design begins to have problems.  Additionally, I was doing some reading and there are some metadata fields of a CKRecord that need to be managed properly on the local device as well, so that CloudKit knows what to do with the object when an update comes in.

So my current thinking is I need to use the CloudKit database as bunch of bags. Into each bag I store the type of object that bag is for.  Nothing more.  I then use the LocalDataManager to figure out other information.

For the relationship issue, I'll have a LocalDataManager for each object type.  It will represent the entire list of those types of objects.  So any time I need to get counts or special values I'll ask the LocalDataManager for that record type.

Going back to the Category/Note parent child example I used before.  I would have two LocalDataManagers (i.e. CategoryLocalDataManager and NoteLocalDataManager).  Anytime I need to get the number of notes for a Category I would ask the NoteLocalDataManager for the count of notes related to the specific Category I was interested in.

Would I persist the count on the Category in this design?  I'm not sure.  It seems like by doing that the Category object itself is polluted.  But depending on how the local store is implemented asking the NoteLocalDataManager may be expensive.  So I'm not sure how I will tackle this issue yet, although I am leaning toward the second idea.

Actually, upon further reflection I think it will be important to store the count value on the local object at least.  That way table views will be performant.

For the second issue, the one about the metadata that needs to be stored, the obvious answer is to persist it with the local store representation of the object.  In fact Apple even provides a method (encodeSystemFieldsWithEncoder) to do exactly that.

For this one, I think my first thing to do is understand exactly what this metadata is and how it will be used by CloudKit.  From there, maybe I can get a better grasp on when it is needed.

One other concern I have is the code for this implementation is starting to get complicated.  Maybe this is a complicated problem that requires a complicated solution.  But something doesn't feel right.  When I am first working on a solution and I see it becoming complicated, my gut instinct is it will be impossible to maintain so I prefer simpler solutions.  I think that will be another area I will investigate over the next few iterations.

To sum this up, obviously this idea still is not "cooked" enough to use in production.  I'll post more information as I continue to explore this topic in practice.

Sunday, August 16, 2015

Installing/Uninstalling Views

One of the issues of converting my app (Pain Logger) over to Swift is that it uses two storyboards.  One for the iPad and one for the iPhone.  My goal, in this new version, is to only use one, but to do that I need to make the UI adapt properly to the different devices.

When I first developed Pain Logger, I found that it was easier to have two storyboards for the different devices as the UI's are very different.  But with the addition of the iPhone 6 plus and size classes in iOS 8, it seems converting to one storyboard will help reduce a lot of redundant code.

For example, for most view controllers I have a base class that extends UIViewController and a subclass for each of the two devices.  So in the appropriate storyboard for each view controller I set it to use the device specific class.  But if I could use only one storyboard and remove different views for different size classes then I would only need the base view controller.

So I started my investigation into how to add/remove a view for different size classes.  I quickly ran across this document from Apple which basically explains how to do it. 

So this blog entry is based on that document and what I learned about using different views with different size classes in practice.

I started by creating a Universal, Single View app. Opening the storyboard showed the initial view in the Any/Any size class configuration.  I set the view up so it contained three views that were laid out as follows:

My goal was to remove the cyan view and, to make it a little more real world, add a different view in a different place when running on an iPhone. 

The purple and red views are attached to the top and bottom of the containing view respectively and centered horizontally.

The cyan view is attached to the right side of the container and centered vertically.  

I also set constraints for the width and height of each of the views so it would be clearer where everything was moving to.

Next I switched to the Compact/Any size class.  Here is where I ran into my first point of confusion.  The size class picker shows these two options:


Notice the first one is for Compact/Any and the second one is for Compact/Regular.  Also notice that Compact/Any says it is for iPhones in portrait OR landscape.  This is important.

I pressed Command-Delete on the cyan view to uninstall it and added a green view in the center of the view controller.  This resulted in this layout:


It is important to note the settings on the Attribute Inspector for the green view:


I read this to mean that for any compact width and any height the green view will be installed (shown) otherwise it won't be shown.

Next I ran this on the iPhone 6 simulator, and got this result (as expected):



Rotating it left I got this:

Which based on the constraints I set, is correct. It is interesting to note that if I rotate it to the left one more time (essentially putting the iPhone upside down) I get the same view as if I had just rotated the phone in the previous picture.  I'm not sure I totally understand why.

Next I ran this on the iPhone 6 Plus simulator.  In portrait mode I got the same results as before:


but in landscape mode I didn't:


Why is that?  It is an iPhone and it is in landscape.  Hmm, I figured it must have something to do with what size classes the view was installed in.  So back to the storyboard.  This time I switched it to Any/Compact:
Sure enough I got the layout I had in the iPhone 6 plus simulator.  So I hit Command+Delete on the cyan view to uninstall it which resulted in in the following layout:

Notice that the description for this size class says that it is "for all compact height layouts (e.g. iPhones in landscape)"  So when I run this on the iPhone simulator I expected to see that layout when in landscape, but that is not the case.  Instead I got the original layout after rotating it:


I really don't understand why this is the case.  It seems the iPhone always is considered to have a compact width no matter what it's orientation.  But if I run this on the iPhone 6 Plus it does show the layout as shown above:


Next I selected the green view in the outline pane (since it wasn't showing in the storyboard directly) and went to the Attributes Inspector to install it.  Here is the pertinent part of the Attributes Inspector after installing it:

What was interesting was after doing this, the view was not installed with it's constraints:

I believe the reason for this is I had added it as a view into a specific size class so when I originally added constraints to it they were installed only into that particular size class.  At any rate I next added the same constraints as before to the view in this size class.  

Now in landscape mode the iPhone 6 plus shows the same as the iPhone 6 did:


Finally to prove I had not messed up the iPad version I ran this on the iPad:

Here is portrait:

Here is landscape:

Exactly as I had intended.

So, in summary, here is what I learned:  

  • The Attributes Inspector is where you install/uninstall views. - I have been used to using the Size Inspector for all of my auto layout stuff.  But when you are concerned with installed versus uninstalled views you have to go to the Attributes Inspector.
  • If you know you are going to have different views for different size classes it is best to change to that size class in Interface Builder BEFORE you add the view. That way you can add the correct constraints for that size class only.
  • An iPhone always seems to have a compact width size class regardless of it's orientation.  The iPhone 6 Plus on the other hand does not.  This one is confusing and probably bears more investigation.  
As always, let me know if you have any comments or suggestions.


Tuesday, August 11, 2015

Swift Conversion

I decided this week that it was time to prepare my paid app for iOS 9.  I have been collecting a long list of enhancements that I have been wanting to add and since this was the first app I ever released I also have a long list of internal enhancements I want to add.

The app was written in Objective-C and uses two storyboards, one for the iPhone and the other for the iPad.  It has about 70 classes and was written before auto-layout and flat UI design existed.  I have been doing my best to make incremental changes to the UI so it looks fresh, but I feel the code base has now become so tangled that it's time to rethink the whole design.  So in conjunction with upgrading it to iOS 9, I also decided to switch it to Swift 2.0.

I decided to start with converting the AppDelegate class.  But after working on that one class for four hours I realized this is not going to be a small task.

First of all, for this app, the AppDelegate has a lot of responsibilities.  It ensures the correct storyboard is loaded and configured properly, it stands up the app's CoreData stack, and it listens for local notifications when they occur.

With all these different concerns it's no wonder that I quickly ran into issues that were tough to fix.

For the most part I was able to resolve each issue with a quick Google search.  But I found switching contexts to do the search was slowing my progress.  So my intent with this post is to consolidate each of the issues along with their fixes so I will have one place to find the answer.  This will be a living document, so as I run across issues with this conversion I intend to update this post.

My plan is to keep each issue succinct by just listing the error, the code that the error was on and the fix.  I don't intend to give an explanation as to why it fixed it.

I'm hoping this will be a short post in the end, but given my experience so far I'm not holding my breath.

So here goes:

Downcast from X? to X only unwraps optionals did you mean to use !

original code:

if let vc = window?.rootViewController

Fix:

if let vc = window?.rootViewController as UIViewController!

How to see computed type of a variable?

let visibleVC = navVC.visibleViewController

Option-Click over visibleVC

Cannot subscript a value of type [NSObject:AnyObject]? with an index of type String (or NSObject or NSString)

  override func handleUserActivity(userInfo: [NSObject : AnyObject]?) {
    if let version = userInfo[kHandoffVersionKey] as! String {

Fix:

  override func handleUserActivity(userInfo: [NSObject : AnyObject]?) {
    if let version = userInfo?[kHandoffVersionKeyas? String {

Could not find member CalendarUnitMonth
Use of unresolved identifier 'NSCalendarUnitDay'

Original code:

     let myCalendar = NSCalendar.currentCalendar()  
     let components = myCalendar.components(NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond, fromDate:startDateTemplate)  
     let currentDateComponents = myCalendar.components(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay, fromDate:currentDate)  

Fix:
            
     let myCalendar = NSCalendar.currentCalendar()
     let units: NSCalendarUnit = [ NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day]
     let components = myCalendar.components(units, fromDate: NSDate())

Expected ',' separator

Original code (where objects is a NSArray):

for object:MyObject in objects {

Fix:

  
for object:AnyObject? in objects {
     if let object = object as? MyObject


Method 'shouldPerformSegueWithIdentifier(_:sender:') with Objective-C selector 'shouldPerformSegueWithIdentifier:sender:' conflicts with method 'shouldPerformSegueWithIdentifier(_:sender:)' from superclass UIViewController with the same Objective-C selector

Original code was this:


    func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject) -> Bool {

Fix:

    override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {

This problem is the error message is confusing.  Basically the fix is to make the "sender" type be "AnyObject?" instead of "AnyObject"

My hope is this format will help me as I go through this conversion and others who might be heading down the same path.

As always, let me know if you have questions or comments.

Monday, August 3, 2015

Guarding Errors

I was working on using the new Swift "guard" statement in my code and I ran across an oddity that felt strange to me. So I thought I would discuss it here.

Apple defines the "guard" statement as this:
guard statement is used to transfer program control out of a scope if one or more conditions aren’t met. 
So, as my thinking went, shouldn't I use this to bail out of a method when an error condition occurs?

I started trying to use it in my CloudKit code. But as I worked with it, it began to feel wrong.

For example, here is a method I use to save a record to a private CloudKit database prior to changing it to use the "guard" statement.

1.   func saveRecord(record:CKRecord, completionHandler:((record:CKRecord) -> Void)) {  
2.     privateDB.saveRecord(record) {  
3.       record, error in  
4.       if let error = error {  
5.         print("error loading: \(error)")  
6.         dispatch_async(dispatch_get_main_queue()) {  
7.           self.handleError(error)  
8.         }  
9.         return  
10.      }  
11.      if let record = record {  
12.        completionHandler(record: record)  
13.      }  
14.    }  
15.  }  

For my way of thinking, this method reads from top to bottom fairly well.  The callback to the privateDB.saveRecord method starting on line 2 returns two optionals, a CKRecord and a NSError.  

On line 4 I first check the error, and if one exists I log it, call an internal error handler method and bail out of the callback.  This to me sounded like the exact case the "guard" statement was meant for.

If no error was returned then the code continues on at line 11 by first checking to see if a CKRecord was returned in the record parameter and if so it calls the passed in completionHandler. 

Next I rewrote the method using the "guard" statement.  Here is how it looked after my first attempt:

1.   func saveRecord(record:CKRecord, completionHandler:((record:CKRecord) -> Void)) {  
2.     privateDB.saveRecord(record) {  
3.       record, error in  
4.       guard (error == nil) else {  
5.         print("error loading: \(error)")  
6.         dispatch_async(dispatch_get_main_queue()) {  
7.           self.handleError(error!)  
8.         }  
9.         return  
10.      }  
11.      guard (record != nil) else {  
12.        print("record not returned from save")  
13.        return  
14.      }  
15.      completionHandler(record: record)  
16.    }  
17.   }  

Hmm, the method was now 2 lines longer.  But more importantly on line 4, the first use of the "guard" statement, I was confused.  If the condition for the "guard" is true we want to skip the block and continue with the code after the "else" block, if it is false we want to go into the "guard's" "else" block and perform the code there.  

But in this code the existence of the error object is generally considered a bad thing.  Also in my brain (I come from a very Java heavy background) if something is equal to nil then I don't generally think of that as a good condition.

But, it seems to me, by using the "guard" statement my way of thinking about good and bad conditions in a code block is reversed.  Now "error == nil" is a "good thing", but it doesn't mean jump into the next block as that block is there for a "bad thing" or the false condition.

It seems reversed to me.

It gets worse on line 11 where the definition of a "good thing" and a "bad thing" is reversed.  Now if the "record" parameter is not nil we have a good condition and we should skip the "else" block.  Otherwise if the "record" parameter is nil we have a bad condition and we should execute the "else" block.  This is the exact reverse of the conditions in lines 4 through 10.

Yes, I'll admit, if you think about it, both are correct and the context is different in the two "guard" statements.

But I'm a lazy programmer and I tend to scan code quickly.  I like to have repeatable patterns that when I see them I know generally how it will work (without having to be overly concerned about the context at each line of code).

For example if I see a pattern like this:

   if (something == null) {
     // do something
   }
   else {
     // do something else
   }

I expect the true case to follow the condition and the false case to follow the "else".  But the "guard" statement reverses that.

I'm still thinking about this but for now I decided to go back to my original code.

I can see where the "guard" statement would work well in a method that took a few parameters and you could not execute the method unless each of the parameters met certain conditions.  In that use case I think bailing out of the method early is preferable and the "guard" statement facilitates that well.

But for this particular use case I'm not convinced it is a better construct. This could just be a case where I need to get used to using "guard" before it will feel natural.

As always, please don't hesitate to post a comment on your thoughts on using "guard" or where I might be missing the mark.