Swift Arrays – Basic forEach, map & filter — July 10, 2016

Swift Arrays – Basic forEach, map & filter

 

I have been using Swift for quite a long while now but while trying to explain parts of it I was trying to make simple little demos of how our lives can be made easier by using the functional aspects of the language. Starting simple lets start by making an array for the ages of some people etc

var ages = [12, 54, 13, 76, 86, 34]

 

What if we wanted the ages in months? We could iterate over ages and multiply each element by 12

var agesMonths: [Int] = []

for age in ages{
	agesMonths.append(age * 12)
}

 

Alternatively we could use the forEach function on the people array. This function essentially shortens the syntax for a for in loop by taking a function as an argument and then applying it to each element in the array in index order. This does not replace the for in loop, as that has its place, for example when using continue as that is not permitted for this function.

var agesMonths1: [Int] = []
ages.forEach({agesMonths1.append($0 * 12)})

 

Incase you didn’t already know $0 is shorthand for the passed in parameter to the function. In this case it will be the Int from whichever index of the array is being iterated over. A great example of how a function can be reduced down to this level is in the Apple Swift docs here.

If we didn’t want to make a new array and just transform the elements of the original array we could use map. Map allows us to pass a function as an argument which will again be performed on each element of the array, allowing for each element to be transformed by our passed in function. In this case each element will be multiplied by 12. The difference being though that the result of the map function returns a new array consisting of the newly transformed elements.

ages = ages.map({$0 * 12})

 

Perhaps a slightly more complex example. We will make a struct Person that will have four properties.

struct Person{
	var firstName: String
	var lastName: String
	var age: Int
	var weight: Float
}

let person1 = Person.init(firstName: "Harry", lastName: "Goodwin", age: 28, weight: 10.2)
let person2 = Person.init(firstName: "Jessica", lastName: "Joop", age: 35, weight: 9.3)
let person3 = Person.init(firstName: "Mark", lastName: "Master", age: 22, weight: 10.2)
let person4 = Person.init(firstName: "Xavier", lastName: "Brand", age: 44, weight: 13.3)

var people: [Person] = [person1, person2, person3, person4]

So lets try and transform some array elements! Perhaps we wanted all of names to be uppercase? Why not make a function to do it for us that we can pass as an argument to the map function.

func personNamesUppercase(person: Person) -> Person{
	var newPerson = person
	newPerson.firstName = person.firstName.uppercaseString
	newPerson.lastName = person.lastName.uppercaseString
	
	return newPerson
}

let peopleCapitalised = people.map(personNamesUppercase)

 

Now lets print out all of the first and last names

peopleCapitalised.forEach{
	print($0.firstName)
}

 

The weights are currently in imperial measurement (stone), so lets use map again to convert them to metric (kg).

func convertWeight(person: Person) -> Person{
	var newPerson = person
	newPerson.weight = person.weight * 6.35029
	
	return newPerson
}

let peopleMetricWeight = people.map(convertWeight)

peopleMetricWeight.forEach{
	print($0.weight)
}

 

What is really cool though is that we can chain map function so we can make the names uppercase and convert the weights all in one statement

let peopleUpperCaseMetric = people.map(personNamesUppercase).map(convertWeight)

Now lets take a look at some other useful functions we can use. What if we wanted to have an array of people under 30?

let underThirties = people.filter{$0.age < 30}
print(underThirties.count)

Or perhaps people 30 or over and weighing more than 60kg. For this we will need to use the function we created earlier to convert the weights to metric and then apply a filter on age and weight.

let overThirtyover10kg = people.map(convertWeight).filter{$0.age >= 30 && $0.weight > 60}
print(overThirtyover10kg.count)

There is a playground with all of the examples on Github.

I really like the syntax for passing in functions in Swift, although it took me quite a long time to really get it never having come across it in any functional languages etc. But seeing how clean code can be made above I think it is definitely worth taking a look at.

Bullet Point List – iOS Swift — April 10, 2016

Bullet Point List – iOS Swift

I recently wanted to make a bullet point list in an app so thought I would share how I did it.

Firstly just set up a new Swift single view project with just a UILabel at the top of the view. Use Autolayout to pin the label view to the left, top and right sides of the view. Also set your label to have lines = 0 so we can have a multiline label that grows with content. Then just create an IBOutlet for the label in your ViewController file. Below is my setup.

Screen Shot 2016-04-10 at 09.21.21

 

When I first tried this I thought it would be easy, create an empty string, iterate through an array of strings prepend a bullet point, appending a new line and then append the strings to the empty string. Pass it to the label’s text property and done. So something like this:

import UIKit

class ViewController: UIViewController
{
	var strings:[String] = []
	@IBOutlet weak var bulletLabel: UILabel!

	override func viewDidLoad()
	{
		super.viewDidLoad()
		
		let bullet1 = "This is a small string"
		let bullet2 = "This is more of medium string with a few more words etc."
		let bullet3 = "Well this is certainly a longer string, with many more words than either of the previuos two strings"
		
		strings = [bullet1, bullet2, bullet3]
		
		var fullString = ""
		
		for string: String in strings
		{
			let bulletPoint: String = "\u{2022}"
			let formattedString: String = "\(bulletPoint) \(string)\n"

			fullString = fullString + formattedString
		}
		
		bulletLabel.text = fullString
	}
}

Unfortunately this gave me the following output:
Screen Shot 2016-04-10 at 09.24.34

We have the bullet points and new lines for each new bullet entry but I think bullet points should have any subsequent lines inline with the text not the bullets. At this point I did a bit of googling and found NSParagraphStyle, which lets you define how a paragraph should be formatted and then you can pass it in as an attribute to an NSAttributedString. So lets try that again, notice that I now use attributed strings and the label’s AttributedString property rather than the text property.

class ViewController: UIViewController
{
	var strings:[String] = []
	@IBOutlet weak var bulletLabel: UILabel!

	override func viewDidLoad()
	{
		super.viewDidLoad()
		
		let bullet1 = "This is a small string"
		let bullet2 = "This is more of medium string with a few more words etc."
		let bullet3 = "Well this is certainly a longer string, with many more words than either of the previuos two strings"
		
		strings = [bullet1, bullet2, bullet3]
		
		let attributesDictionary = [NSFontAttributeName : bulletLabel.font]
		let fullAttributedString = NSMutableAttributedString(string: "", attributes: attributesDictionary)
		
		for string: String in strings
		{
			let bulletPoint: String = "\u{2022}"
			let formattedString: String = "\(bulletPoint) \(string)\n"
			let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: formattedString)
			
			let paragraphStyle = createParagraphAttribute()
			attributedString.addAttributes([NSParagraphStyleAttributeName: paragraphStyle], range: NSMakeRange(0, attributedString.length))
			
			fullAttributedString.appendAttributedString(attributedString)
		}
		
		bulletLabel.attributedText = fullAttributedString
	}
	
	func createParagraphAttribute() ->NSParagraphStyle
	{
		var paragraphStyle: NSMutableParagraphStyle
		paragraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle
		paragraphStyle.tabStops = [NSTextTab(textAlignment: .Left, location: 15, options: NSDictionary() as! [String : AnyObject])]
		paragraphStyle.defaultTabInterval = 15
		paragraphStyle.firstLineHeadIndent = 0
		paragraphStyle.headIndent = 15

		return paragraphStyle
	}
}

So how did that work out?

Screen Shot 2016-04-10 at 10.50.01

Excellent, everything is formatted correctly. Example Gist here!

note on why NSTextTab needs an empty dictionary for options param.

 

Get List Of BLE Devices Using CoreBluetooth On iOS – Swift — January 21, 2016

Get List Of BLE Devices Using CoreBluetooth On iOS – Swift

This post will show you just the minimum effort needed to get a list of BLE (Bluetooth 4.0) devices that are advertising on your iOS device. Make sure you have an iOS device with BLE and another device that is capable of advertising its BLE services ( I am using an RFDuino, with it’s device name set to Display).

Let’s get started.

  • Create a new single view Swift project.
  • Add a UITableView to the ViewController in the pre-made Storyboard.
  • Attach the datasource and delegate from the UITableView to the ViewController in the Storyboard.
  • Add an IBOutlet for the UITableView in your ViewController class.
  • Make sure the class for the ViewController is set to the class ViewController.

Add the CoreBluetooth framework to your project.

Screen Shot 2016-01-21 at 16.51.32

 

Now make your ViewController Swift file look like this:


import CoreBluetooth
import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CBCentralManagerDelegate
{
    var centralManager: CBCentralManager?
    var peripherals: Array<CBPeripheral> = Array<CBPeripheral>()
	
	@IBOutlet weak var tableView: UITableView!
	
	override func viewDidLoad()
    {
        super.viewDidLoad()
		
	//Initialise CoreBluetooth Central Manager
        centralManager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
    }
	

    //CoreBluetooth methods
    func centralManagerDidUpdateState(central: CBCentralManager)
    {
        if (central.state == CBCentralManagerState.PoweredOn)
        {
            self.centralManager?.scanForPeripheralsWithServices(nil, options: nil)
        }
        else
        {
            // do something like alert the user that ble is not on
        }
    }

    func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
    {
        peripherals.append(peripheral)
		tableView.reloadData()
    }
	

	//UITableView methods
	func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
		
		let peripheral = peripherals[indexPath.row]
        cell.textLabel?.text = peripheral.name

        return cell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return peripherals.count
    }
}

centralManagerDidUpdateState will be called whenever the CoreBluetooth BLE service state changes. For this example I am only checking if the state == CBCentralManagerState.PoweredOn but there are other states you can check for such as off, unknown and resetting. When the state is on we initiate a device scan using scanForPeripheralsWithServices.

If the service finds a device the delegate method didDiscoverPeripheral is called. In this method we just get the newly discovered peripheral and add it to our array of peripherals. We then call reloadData on the tableView so that the new object in the array is used in our tableView. I have only used the name property from the peripheral but there is loads more stuff to play with.

When you turn on your BLE device you should see something like the following:

Screen Shot 2016-01-21 at 23.14.49

Blurring a UIView (Animated) — December 18, 2015

Blurring a UIView (Animated)

I had to do this the other day so I thought I would share how simple the effect can be achieved. You will need to be building with iOS 8 or above.

Create a new single view Swift project. Add a UIImageView with an image(more interesting images look better) to the supplied Storyboard Viewcontroller, with an IBOutlet in the viewController file. Then create a button with an IBAction in the same viewController.

To get an easy blue effect all we have to do is get an instance of UIBlurEffect and assign it to an instance of UIVisualEffectView as the effect. Then we just add the instance of UIVisualEffectView as a subview to our UIImageView.

To animate the blur effect the simplest way is animate the UIVisualEffectView’s alpha from 0.0 to 1.0.

//iOS8 example
import UIKit

class ViewController: UIViewController
{
    @IBOutlet weak var imageView: UIImageView!

    @IBAction func blurStuff(sender: AnyObject)
    {
	let blurEffect = UIBlurEffect(style: .Light)
	let effectView = UIVisualEffectView(effect: blurEffect)
	effectView.frame = imageView.frame
	imageView.addSubview(effectView)
	effectView.alpha = 0
		
        UIView.animateWithDuration(0.8) {
            effectView.alpha = 1.0
        }
    }
}
giphy
Blurring alpha

 

If you are lucky enough to be only targeting iOS9 upwards you can achieve the same result with less effort. In iOS9 we can leave the effect for UIVisualEffectView as nil and then animate the setting of the effect property. There is a slight visual difference though (looks like a slight pop in contrast). To be honest I prefer the alpha animation.

//iOS9 example
import UIKit

class ViewController: UIViewController
{
    @IBOutlet weak var imageView: UIImageView!

    @IBAction func blurStuff(sender: AnyObject)
    {
	    let effectView = UIVisualEffectView()
	    effectView.frame = imageView.frame
	    imageView.addSubview(effectView)
		
        UIView.animateWithDuration(0.8) {
	        effectView.effect = UIBlurEffect(style: .Light)
        }
    }
}
giphy-2
Animating effect property
My Favourite Dev Books (so far…) — December 7, 2015

My Favourite Dev Books (so far…)

I try to read a lot of books and get as much exposure to new ideas as possible. Below I have listed some of my favourites books in no particular order. Also please leave a comment on books you think I may want to read or just share your thoughts etc.

 

Objective-C Programming – Aaron Hillegass & Mikey Ward

Although Swift is now chosen for a lot of new projects if you are going to maintain older iOS or OSX projects an understanding of Objective-C is a must. This book gave me a great start to understanding not jut the how but more importantly why the language works like it does. For example the first few chapters feature a refresher/intro to C. The book also works through examples for each topic so you can practice what you learn.

On a similar note the following books from the Big Nerd Ranch also helped me greatly.

iOS Programming

Android Programming 

 

Code Complete – Steve McConnell

An oldy but one of my absolute favourites. When I got my first job as a software developer the lead developer for the team recommended I tried Code Complete and although it was a big read it changed my thoughts on software development immediately. The main reason I like this book is that rather than narrow down on a certain technology it gives a sweeping overview of the craft as a whole, giving helpful advice on a huge range of topics. Pretty much the first book to show me that there is so much more to software development than writing code.

 

iOS Animations by Tutorials – Marin Todorov

If you want to learn more about animations in iOS this book is great. It covers a lot of ground, from simple UIView animations up to 3d looking animations with Core Animation. There are also some interesting sections on animating Autolayout constraints in there too which I found particularly useful.

The book does require a knowledge of Swift and iOS to get the most out of it but nothing overly complex outside the realm of UI. Every chapter works on an already partially complete app with the source code available to download so you can work through the examples. Challenges at the end of each chapter also allow you to think a bit for yourself and test what you have learnt.

 

Conceptual Blockbusting – James L. Adams

I read this book in a book club and it was brilliant; bringing quite a lot of excitement to everyone. With anecdotes, exercises and a deep understanding of the theory the author tries to help the reader identify and overcome the blocks that can occur in creative thought. I would recommend reading this with another person though, as when you try to solve an exercise it makes the point that the author is trying to make clearer when you discuss your thought process and outcome with other people.

 

The Pragmatic Programmer: From Journeyman to Master – Andrew Hunt  & David Thomas

I thought this book was excellent butI won’t dwell on it as it is already well established as a must read. I consider it (rightly or wrongly) to be a more compact and friendly version of Code Complete. If someone new to working in software development asked what book to read I would say this one every time.

 

The Design of Everyday Things – Don Norman

I almost didn’t put this one in as some parts of the book were quite drawn out ( I have heard the first edition is much shorter) but it did make me rethink how I worked on UI and user experience. Plus when doing design work it can be useful to know some terms such as affordances and signifiers. The big downside to this book is that you may never look at doors the same way again.

Being In A Software Developer Book Club — November 19, 2015

Being In A Software Developer Book Club

First, a little background…

The book club I am currently in is made up of 8 people, 3 iOS developers and 5 .Net developers. Typically we read around 20-30 pages a week in our own time and then meet for 20 minutes or so after lunch to discuss what we read that week.

But why do it in the first place? Here are a select few of my reasons (I have a lot more!)

article-1294306779303-0ca714e3000005dc-110102_304x183
Lots of people have book clubs

We all want to get better at what we do but for one reason or another some struggle to read a book past the first few chapters. Reading with other people is a great way to make yourself read a book, as you will at least want to keep up with the group but you may also find solidarity with your group through the tough chapters.

Naturally the group will be made up of different skill levels so you may find yourself soon reading a book that you may never have picked up because you considered it out of your depth. This can also help to broaden team knowledge and cohesion by finding out who is knowledgeable on certain subjects and learning from other’s anecdotes on the topic of discussion.

Looking up concepts born in conversation is also regular occurrence. For example while reading The Art Of Unit Testing, which is all in C#, a discussion erupted on the difference of unit testing with Swift and Objective C. This then evolved into questions regarding what Objective C and Swift compile down to and the Objective C runtime. Needless to say much research was carried out afterwards.
Most importantly, keep the book selection fun. We tend to read tech book then a more abstract book so that everyone gets to change gear a little. When I mean abstract I mean books that can relate to software development but cover a much wider scope of thought, for example Conceptual Blockbusting.

The benefit of more abstract books is to make everyone think on another level about their thought process and how they tackle problems, as well as broadening people’s horizons into areas they may not normally have ventured. Personally I also find these books stimulate a lot of conversation in the group, perhaps because it transcends skill level and equals the playing field for conversation?

Unknown
You never know how popular your book club may become

Tips

  • Someone should really lead the group, prompting talking points and scheduling meet ups.
  • Ideally at least one person will have a small grasp of the topic if a tech book is being read to help lead conversation.
  • Some books are easier to read than others, change pages/chapters per week to suit.
  • Meet up after lunch, as people tend to be less busy.
  • It should be fun, not a monologue or lecture.
Easy Expanding/Collapsing TableViewCells — November 6, 2015

Easy Expanding/Collapsing TableViewCells

I noticed when using an app the other day that its UITableViewCells were increasing in size when touched, with the height increase also animated. I thought this would be quite difficult to achieve but after digging around it turns out that its quite simple. By the end of this quick post we will have something like the below.

giphy

Start by creating a new single view project in Swift. Navigate to the storyboard view and change the default UIViewController for a UITableViewController. Then create a new prototype cell that is 140 points in height. Place the label you want visible when the cell is small in height (44 points) and then place some labels and buttons below. The latter views will be visible when the cell has been touched.

Screen Shot 2015-11-06 at 12.39.55

Create a new class called TableViewController and paste the code beneath inside it.

import UIKit

class TableVIewController: UITableViewController
{
    var selectedCellIndex = -1
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        return tableView.dequeueReusableCellWithIdentifier("Cell")!
    }
    
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        return (indexPath.row == selectedCellIndex) ? 140 : 44
    }
    
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
    {
	selectedCellIndex =  (selectedCellIndex == indexPath.row) ? -1 : indexPath.row

        self.tableView.beginUpdates()
        self.tableView.endUpdates()
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return 4
    }
}

Add the new TableViewController to the UITableViewController in the Storyboard using the Identity Inspector and hit run. Select a row and its size should increase with a nice animation. So how does this work?

BeginUpdates and endUpdates allows you to make changes to the tableView, such as inserting and deleting rows without needing to call for a reload. To make this process look nicer to the user it is animated. Although we don’t perform any insertions/deletions between beginUpdates and endUpdates the animation is still triggered as well as heightForRow, where we set the new height of the selected row.

If you don’t want the animation to happen just do this

UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.endUpdates()
UIView.setAnimationsEnabled(true)
Sorting Arrays – Objective C — August 24, 2015

Sorting Arrays – Objective C

Using arrays is a staple of iOS development and there many tools at our disposal to do all manor of things with them. Below are some that I use often and find quite useful.

Sorting simple arrays of strings and numbers

NSArray *names = @[@"Harry", @"George", @"Xavier", @"Adam", @"Michael", @"Tim"];
NSArray *sortedNameArray = [names sortedArrayUsingSelector:@selector(compare:)];

for (NSString *name in sortedNameArray)
{
    NSLog(@"%@", name);
}

Which will print out:
2015-08-16 17:39:08.137 ArrayTEST[24935:755443] Adam
2015-08-16 17:39:08.138 ArrayTEST[24935:755443] George
2015-08-16 17:39:08.138 ArrayTEST[24935:755443] Harry
2015-08-16 17:39:08.138 ArrayTEST[24935:755443] Michael
2015-08-16 17:39:08.138 ArrayTEST[24935:755443] Tim
2015-08-16 17:39:08.139 ArrayTEST[24935:755443] Xavier

Luckily this will also work with NSNumbers. Also notice that in this example I have reversed the sort order by adding reverseObjectEnumerator.

NSArray *ages = @[@34.3, @45, @67.2, @12, @23, @74];
		NSArray *sortedAgesArray = [[[ages sortedArrayUsingSelector:@selector(compare:)] reverseObjectEnumerator] allObjects];
		
for (NSNumber *age in sortedAgesArray)
{
    NSLog(@"%@", name);
}

2015-08-16 17:39:08.139 ArrayTEST[24935:755443] 74
2015-08-16 17:39:08.139 ArrayTEST[24935:755443] 67.2
2015-08-16 17:39:08.139 ArrayTEST[24935:755443] 45
2015-08-16 17:39:08.139 ArrayTEST[24935:755443] 34.3
2015-08-16 17:39:08.140 ArrayTEST[24935:755443] 23
2015-08-16 17:39:08.140 ArrayTEST[24935:755443] 12

What would happen though if our array of strings had a mix of cases?

NSArray *names = @[@"Harry", @"George", @"Xavier", @"adam", @"michael", @"tim"];
NSArray *sortedNameArray = [names sortedArrayUsingSelector:@selector(compare:)];
	
for (NSString *name in sortedNameArray)
{
    NSLog(@"%@", name);
}

Here is the output
2015-08-16 17:53:49.929 ArrayTEST[25007:767608] George
2015-08-16 17:53:49.931 ArrayTEST[25007:767608] Harry
2015-08-16 17:53:49.931 ArrayTEST[25007:767608] Xavier
2015-08-16 17:53:49.931 ArrayTEST[25007:767608] adam
2015-08-16 17:53:49.931 ArrayTEST[25007:767608] michael
2015-08-16 17:53:49.931 ArrayTEST[25007:767608] tim

Well they are ordered but not how I wanted. Notice the order of precedence here, uppercase letters are ordered then lowercase. If we wanted to order the array disregarding case we can use localizedCaseInsensitiveCompare.

NSArray *names = @[@"Harry", @"George", @"Xavier", @"adam", @"michael", @"tim"];
NSArray *sortedNameArray = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
	
for (NSString *name in sortedNameArray)
{
    NSLog(@"%@", name);
}

Giving us the output:
2015-08-16 18:02:43.434 ArrayTEST[25022:774253] adam
2015-08-16 18:02:43.435 ArrayTEST[25022:774253] George
2015-08-16 18:02:43.435 ArrayTEST[25022:774253] Harry
2015-08-16 18:02:43.435 ArrayTEST[25022:774253] michael
2015-08-16 18:02:43.435 ArrayTEST[25022:774253] tim
2015-08-16 18:02:43.435 ArrayTEST[25022:774253] Xavier


Sorting Arrays by Object Properties

For an example I am making a class called Developer that will have three properties; name (string), age (NSNumber), platform (string). The code below will generate 6 developer objects, add them to a NSMutableArray named Developers and print them out in the original order of the array.

#import <Foundation/Foundation.h>

@interface Developer: NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *platform;
@property (nonatomic, strong) NSNumber *age;

@end

@implementation Developer
@end

int main(int argc, const char * argv[])
{
	@autoreleasepool
	{
		NSArray *names = @[@"Harry", @"Adam", @"Xavier", @"Steve", @"Adam", @"Michael", @"Tim"];
		NSArray *lastNames = @[@"Smith", @"Jackson", @"Cameron", @"Atlee", @"Bevin", @"Potter", @"Cortez"];
		NSArray *platforms = @[@"iOS", @"iOS", @"Android", @".Net", @"Android", @"Tizen", @"iOS"];
		NSArray *ages = @[@34.3, @45, @67.2, @12, @23, @74, @32];
		
		NSMutableArray *developers = [[NSMutableArray alloc] init];
		for (NSInteger i = 0; i < [names count]; i++)
		{
			Developer *developer = [[Developer alloc] init];
			developer.name = names[i];
			developer.lastName = lastNames[i];
			developer.age = ages[i];
			developer.platform = platforms[i];
			
			[developers addObject:developer];
		}
		
		for (Developer *developer in developers)
		{
			NSLog(@"%@ %@, %@, %@",developer.name, developer.lastName, developer.platform, developer.age);
		}
	}
	
    return 0;
}

Giving us
2015-08-16 19:23:46.898 ArrayTEST[25222:819170] Harry Smith, iOS, 34.3
2015-08-16 19:23:46.899 ArrayTEST[25222:819170] Adam Jackson, iOS, 45
2015-08-16 19:23:46.899 ArrayTEST[25222:819170] Xavier Cameron, Android, 67.2
2015-08-16 19:23:46.900 ArrayTEST[25222:819170] Steve Atlee, .Net, 12
2015-08-16 19:23:46.900 ArrayTEST[25222:819170] Adam Bevin, Android, 23
2015-08-16 19:23:46.900 ArrayTEST[25222:819170] Michael Potter, Tizen, 74
2015-08-16 19:23:46.900 ArrayTEST[25222:819170] Tim Cortez, iOS, 32

So if we wanted to sort our array by name how would we do it? How about NSSortDescriptor? To do this we simply get an object of NSSortDescriptor and tell it what property we are sorting by in string form and if we want our data sorted in ascending order or not. Simple

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"name" ascending: YES];
		NSArray *sortedDevelopers = [developers sortedArrayUsingDescriptors: [NSArray arrayWithObject:sortDescriptor]];
		
		for (Developer *developer in sortedDevelopers)
		{
			NSLog(@"%@ %@, %@, %@",developer.name, developer.lastName, developer.platform, developer.age);
		}

Which will give us the following
2015-08-16 19:24:23.626 ArrayTEST[25230:819624] Adam Jackson, iOS, 45
2015-08-16 19:24:23.627 ArrayTEST[25230:819624] Adam Bevin, Android, 23
2015-08-16 19:24:23.627 ArrayTEST[25230:819624] Harry Smith, iOS, 34.3
2015-08-16 19:24:23.627 ArrayTEST[25230:819624] Michael Potter, Tizen, 74
2015-08-16 19:24:23.628 ArrayTEST[25230:819624] Steve Atlee, .Net, 12
2015-08-16 19:24:23.628 ArrayTEST[25230:819624] Tim Cortez, iOS, 32
2015-08-16 19:24:23.628 ArrayTEST[25230:819624] Xavier Cameron, Android, 67.2

Well its a start but look at the two Adams, their last names are not in alphabetical order. That is because we are only sorting by name and not lastName. Luckily the NSArray method sortedArrayUsingDescriptors can take an array of NSSortDescriptor to sort our data. Lets try again.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"name" ascending: YES];
		NSSortDescriptor *secondSortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"lastName" ascending: YES];
		NSArray *sortedDevelopers = [developers sortedArrayUsingDescriptors: [NSArray arrayWithObjects:sortDescriptor, secondSortDescriptor, nil]];
		
		for (Developer *developer in sortedDevelopers)
		{
			NSLog(@"%@ %@, %@, %@",developer.name, developer.lastName, developer.platform, developer.age);
		}

2015-08-16 19:29:52.785 ArrayTEST[25243:822769] Adam Bevin, Android, 23
2015-08-16 19:29:52.786 ArrayTEST[25243:822769] Adam Jackson, iOS, 45
2015-08-16 19:29:52.786 ArrayTEST[25243:822769] Harry Smith, iOS, 34.3
2015-08-16 19:29:52.786 ArrayTEST[25243:822769] Michael Potter, Tizen, 74
2015-08-16 19:29:52.787 ArrayTEST[25243:822769] Steve Atlee, .Net, 12
2015-08-16 19:29:52.787 ArrayTEST[25243:822769] Tim Cortez, iOS, 32
2015-08-16 19:29:52.787 ArrayTEST[25243:822769] Xavier Cameron, Android, 67.2

Lets try one more. Why not order our developers by what platform they work on and then by age?

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"platform" ascending: YES];
		NSSortDescriptor *secondSortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"age" ascending: YES];
		NSArray *sortedDevelopers = [developers sortedArrayUsingDescriptors: [NSArray arrayWithObjects:sortDescriptor, secondSortDescriptor, nil]];
		
		for (Developer *developer in sortedDevelopers)
		{
			NSLog(@"%@ %@, %@, %@",developer.name, developer.lastName, developer.platform, developer.age);
		}

2015-08-16 19:35:25.610 ArrayTEST[25256:827191] Steve Atlee, .Net, 12
2015-08-16 19:35:25.611 ArrayTEST[25256:827191] Adam Bevin, Android, 23
2015-08-16 19:35:25.612 ArrayTEST[25256:827191] Xavier Cameron, Android, 67.2
2015-08-16 19:35:25.612 ArrayTEST[25256:827191] Michael Potter, Tizen, 74
2015-08-16 19:35:25.612 ArrayTEST[25256:827191] Tim Cortez, iOS, 32
2015-08-16 19:35:25.612 ArrayTEST[25256:827191] Harry Smith, iOS, 34.3
2015-08-16 19:35:25.612 ArrayTEST[25256:827191] Adam Jackson, iOS, 45

A third argument can be used when initiaising NSSortDescriptor, which allows a selector to be used to perform the comparison, for example caseInsensitiveCompare and localizedStandardCompare. We used the former earlier on to sort our names disregarding letter case, the latter should be used when dealing with strings that a user will see, as it takes into account localisation language features.

For example to add the caseInsensitiveCompare you would use:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];

Pretty useful!

 

IBInspectable and IBDesignable – Swift — June 24, 2015

IBInspectable and IBDesignable – Swift

When making views in Xcode Storyboards it can be frustrating not being able to see any work you have done using code or not being able to change run time attributes like corner radiuses. For the latter you could use User Defined Runtime Attributes but there is no auto-complete so errors are likely and personally I don’t find it that friendly.

Fortunately, Xcode has some added features to allow us to see what views we are working on in real time and to be able to change run time values very easily, all in Storyboards.

So to get started create a new Swift single view application and add a new UIView to the empty View Controller. Make it fill most of the View Controller.

Screen Shot 2015-06-23 at 21.11.13

 

Add some basic constraints (this way it doesn’t matter what your target device is).

Screen Shot 2015-06-23 at 21.12.21

 

Now make a new Swift class that subclasses UIView and add the below to it.

import UIKit

@IBDesignable class TestView: UIView
{
	@IBInspectable var viewBackgroundColor: UIColor?
	{
		didSet
		{
			layer.backgroundColor = viewBackgroundColor?.CGColor
		}
	}

	@IBInspectable var cornerRadius: CGFloat = 0
	{
		didSet
		{
			layer.cornerRadius = cornerRadius
		}
	}
}

There is some new terminology here so lets go through that first.

@IBDesignable – This tells Xcode that the view is to be rendered in the Storyboard editor, so there is no need to keep building and running the project to see simple view changes.

@IBInspectable – Allows us to access the property from the Attribute Inspector when the view is highlighted.

The rest of the code is simply creating two new properties,viewBackgroundColour and cornerRadius. We then use didset to change the view’s corresponding properties when either cornerRadius or viewBackgroundColour are changed.

Go back into your Storyboard and select the new UIView you created. Open the Identity Inspector and set the UIView’s class to be your new view class, in this case TestView.
Screen Shot 2015-06-23 at 21.11.39

 

Open the Attribute Inspector and you should be greeted by two new attributes at the top of the Attributes view window. Change either of the attributes and you should see the view change to match the changes almost immediately.

Screen Shot 2015-06-23 at 21.13.33

 

Good but it could do more. Make a new Swift class called ShapeView and copy the below code into it.

import UIKit

class ShapeView: UIView
{
	var shapeColor: UIColor?

	override init(frame: CGRect)
	{
		super.init(frame: frame)
		self.backgroundColor = UIColor.clearColor()
	}

	required init(coder aDecoder: NSCoder)
	{
		super.init(coder: aDecoder)
	}
}

 

This new class is going to be a base class for some shape objects. Each subclass will implement the drawRect function and will be initialised with a frame. The only property that we will set will be the shapeColor, the background color will remain clear so the background view isn’t obscured.

Now make two further swift files like the below, which are the subclasses of ShapeView.

import UIKit

class CircleView: ShapeView
{
    override func drawRect(rect: CGRect)
	{
		let context = UIGraphicsGetCurrentContext();

		CGContextSetLineWidth(context, 5.0);
		shapeColor?.set()

		CGContextAddArc(context, (frame.size.width)/2, frame.size.height/2, (frame.size.width - 10)/2, (frame.size.height - 10)/2, CGFloat(M_PI * 2.0), 1)

		CGContextStrokePath(context);
    }
}

 

import UIKit

class CrossView: ShapeView
{
	override func drawRect(rect: CGRect)
	{
		let context = UIGraphicsGetCurrentContext();

		CGContextMoveToPoint(context, 0, 0)
		CGContextAddLineToPoint(context, rect.width, rect.height)
		CGContextMoveToPoint(context, rect.width, 0)
		CGContextAddLineToPoint(context, 0, rect.height)

		shapeColor?.set()
		CGContextSetLineWidth(context, 5.0);
		CGContextStrokePath(context);
	}
}

 

Now all we need to do is change our TestView class to add the new features.

import UIKit

@IBDesignable class TestView: UIView
{
	@IBInspectable var viewBackgroundColor: UIColor?
	{
		didSet
		{
			layer.backgroundColor = viewBackgroundColor?.CGColor
		}
	}

	@IBInspectable var cornerRadius: CGFloat = 0
	{
		didSet
		{
			layer.cornerRadius = cornerRadius
		}
	}

	@IBInspectable var shapeType: Int
	{
		set(newValue)
		{
			internalShapeType = min(newValue, 1)
			internalShapeType = max(0, internalShapeType)
		}
		get{
			return internalShapeType
		}
	}

	var internalShapeType: Int = 0

	@IBInspectable var shapeSize: CGFloat = 100.0
	@IBInspectable var shapeColor: UIColor?

	override func layoutSubviews()
	{
		let horizontalPoint: CGFloat = self.frame.width/2 - shapeSize/2
		let verticalPoint: CGFloat = self.frame.height/2 - shapeSize/2
		let shapeFrame  = CGRectMake(horizontalPoint, verticalPoint, shapeSize,shapeSize)

		let crossView = CrossView(frame: shapeFrame)
		let circleView = CircleView(frame: shapeFrame)

		let shapes = [0:crossView, 1:circleView]

		let shapeView = shapes[internalShapeType]
		shapeView!.shapeColor = shapeColor
		self.addSubview(shapeView!)
	}
}

 

Nothing here is that different from before except for how the shapeType variable is handled and how we change what shape is drawn.

To select which shape is used I used a dictionary with ascending integers as keys and the two shapeViews (circleView and CrossView) as objects. So depending on what the internalShapeType integer variable is set to the dictionary will return the corresponding object. But what if the integer is set above 1 or below 0?

The shapeType variable uses min and max operators in its set so that values below 0 and above 1 will not be passed to the internalShapeType. Unfortunately enums cannot be used for IBInspectable values so this is the best we can do for now.

Open the Storyboard and add select the TestView. You should see the new IBInspectable properties have been added. Change them and the view will update automatically.

Screen Shot 2015-06-23 at 21.28.34
Screen Shot 2015-06-23 at 22.07.23

 

Raspberry Pi Temperature Sensor Web Server – Part 4 (iOS Swift App) — May 28, 2015

Raspberry Pi Temperature Sensor Web Server – Part 4 (iOS Swift App)

So we have our Raspberry Pi web server which gives us a lovely set of json temperature data. What would be great though is an app that could display this data whenever we want. So lets do just that. Once again all of the source code can be found in my Github Repo.

Create a new single view project in Xcode, selecting Swift as the language. Open the storyboard and add some labels to the ViewController for the temperature output and date ( I used two for the date, one just as a title). Mine is below. I disabled size classes for this.

Screen Shot 2015-05-21 at 20.37.40

Using assistant editor, ctrl drag the temp and date labels into ViewController file to create IBOutlets for UIlabels. I called mine currentTempLabel and lastUpdateLabel.

Although we could put all the code we need for this project in the VIewController it would probably be better to abstract away all of the code we will use to get the json data from the Raspberry Pi. So make a new Swift file that subclasses NSObject calledTemperatureWebService. Copy the below code into that new file, remembering to swap the IP address for yours!

import Foundation
import UIKit

protocol TemperatureWebServiceDelegate
	
{
	func temperatureReceived(temperature: String, date: String)
}

class TemperatureWebService: NSObject, NSURLConnectionDelegate
{
	
	var delegate: TemperatureWebServiceDelegate?
	
	var data = NSMutableData()
	var jsonResult: NSArray = []

	func startConnection()
	{
		let urlPath: String = "http://192.168.0.11/temperaturejson.php"
		var url: NSURL = NSURL(string: urlPath)!
		var request: NSURLRequest = NSURLRequest(URL: url)
		
		var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
	}
	
	func connection(connection: NSURLConnection!, didReceiveData data: NSData!)
	{
		self.data.appendData(data)
	}
	
	func connectionDidFinishLoading(connection: NSURLConnection!)
	{
		var err: NSError
		jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSArray
		
		getLatestTempReading()
	}
	
	func getLatestTempReading()
	{
		var dictionary: NSDictionary = jsonResult.lastObject as! NSDictionary
		var tempValue = dictionary.objectForKey("Temp") as! String
		var dateValue = dictionary.objectForKey("Date") as! String
		
		
		if (delegate != nil)
		{
			delegate?.temperatureReceived(tempValue, date: dateValue)
		}
	}
}

This class is using NSURLConnection to get the data from our web service. We provide a function startConnection() that we can call from other classes that will do as it says. It will start the NSURLConnection for our IP address. We implement the NSURLConnectionDelegate and its delegate functions – using connection to append data to our NSMutableData object and connectionDidFinishLoading to know when we have all the data and to serialise our data into JSON.

When we have the data we call getLatestTempReading() to put the last temp reading from the JSON into a dictionary and call the corresponding objects for keys for temperature and date.

I have then opted to use a delegate method to give the data back to the object that owns the TemperatureWebService object as two String objects.

Now copy the below code into your ViewController class.

class ViewController: UIViewController, TemperatureWebServiceDelegate
{
	@IBOutlet weak var currentTempLabel: UILabel!
	@IBOutlet weak var lastUpdateLabel: UILabel!
	
	override func viewWillAppear(animated: Bool)
	{
		super.viewWillAppear(animated)
		
		var webService = TemperatureWebService()
		webService.delegate = self
		webService.startConnection()
}
	
	func temperatureReceived(temperature: String, date: String)
	{
		currentTempLabel.text = "\(temperature) °C"
		lastUpdateLabel.text = "\(date)"
	}
}

As all of the hard work is done in the web service class our ViewController is incredibly simple. We firstly have two IBOutlets for our labels and then we override viewWillAppear and create a TemperatureWebService object, which we call startConnection() on. Then we implement the TemperatureWebServiceDelegate and its associated method which will be called when the web service has received data, serialised it and found the last temperature reading. We then simply set our labels to show the two strings given to us by the web service.

NOTE: If you are using iOS9 changes in App Security stop you accessing http web addresses. To fix this do what is detailed here.

When working it should look like this.

Screen Shot 2015-05-21 at 22.31.53

Next time we will make a graph to show all of our data that has been collected.