C. Keith Ray

C. Keith Ray writes about and develops software in multiple platforms and languages, including iOS® and Macintosh®.
Keith's Résumé (pdf)

Wednesday, September 20, 2017

Is True Greater Than False in Swift 4?

This is a sample from my book in progress at LeanPub: What Every Programmer Needs to Know About Swift.

Is true > false?

Relational operators ("<", ">", "<=", ">=") are not defined for Bool values. So "true > false" is a syntax error.
However, if you want those operators to work with Bool values (so you can sort "false" before "true", for example), you canmake Boolean conform to the "Comparable" protocol. I'll illustrate:
extension Bool : Comparable {
    // "Is the left-hand-side value 
    // less than the right-hand-side value?"
    public static func <(_ lhs: Bool, _ rhs: Bool) 
            -> Bool {
        switch (lhs, rhs) {
            case (false, true):
                return true
            default: 
                return false
        }
    }
}

assert( (true  >  false) == true )
assert( (true  >= false) == true )
assert( (true  <  false) == false )
assert( (true  <= false) == false )
assert( (false >  true)  == false )
assert( (false >= true)  == false )
assert( (false <  true)  == true )
assert( (false <= true)  == true )
The Comparable protocol requires two operators be implemented: "<" and "==". It defines the other relational operators in terms of those two. I only need to implement "<", since Bool already implements "==".


Tuesday, September 19, 2017

What Every Programmers Needs to Know About Swift



I have a book in progress at LeanPub titled What Every Programmer Needs to Know About Swift.

I've set the minimum price to $0.00 for a while, so buying the book now is at no risk for you. (Plus, LeanPub has a money-back guarantee. See also their Terms of Service.)

This book is intended for people who already know a programming language and are getting started using Swift. Or, you may already know Swift, but have questions. Or, like me, you keep forgetting the syntax and you’re tired of searching the web and having to carefully read the pages you find to figure out which version of the Swift language some example code was written in. Plus, the page you find on the web might be wrong or long-winded.

My book is written for Swift 4, and every line of code has been tested in a Swift playground in Xcode. You should be able to copy and paste my code confidently.

It's in a question-and-answer format inspired by C++ Faqs by Cline and Lomow. (There are more recent C++ Faqs books on Amazon, which, if they are as good as the original edition, you should read if you’re programming in C++.

I'm also going to publish snippets from the book on my blog.

If you buy the book, please join and participate on the mailing list! (See the Introduction in the book.)


Saturday, April 29, 2017

Implementing Control Structures in Swift

In Smalltalk, control structures like "for" or "while" were not part of the language, but built using the language itself. The Smalltalk compiler would special-case these for efficiency, but you could make your own, if you wanted to.

In Swift, you can almost do the same, though the syntax makes it look less like native syntax.

I just wrote this in a Swift playground in Xcode 8.1.

NOTE: just because this is possible, doesn't mean you should do this. Readability and testability matters. This is just exploring the language.

import Foundation

extension Int {
    func through(_ end: Int, `do`: (_ i: Int)->() ) {
        var i = self
        while i <= end {
            `do`(i)
            i += 1
        }
    }
    // originally, I thought of naming this 'to' but that's ambiguous
    // whether the loop executes using the end value or not.
}

print("through loop 12-15")

12.through(15, do: {i in print("\(i)")})

/* prints
 through loop 12-15
 12
 13
 14
 15
*/

print("through loop 1-5")

1.through(5) { index in print("\(index)") }

/* prints:
 through loop 1-5
 1
 2
 3
 4
 5
*/

extension Int {
    func upTo(_ end: Int, `do`: (_ i: Int)->() ) {
        var i = self
        while i < end {
            `do`(i)
            i += 1
        }
    }
}

print("upTo 12-15")

12.upTo(15, do: {i in print("\(i)")})

/* prints
 upTo 12-15
 12
 13
 14
 */

print("upTo 0-5")

0.upTo(5) { index in print("\(index)") }

/* prints
 upTo 0-5
 0
 1
 2
 3
 4
*/

/*

 It's much more readable if you use the built-in syntax:

*/

print("for 12 through 15")

for i in 12...15 {
    print("\(i)")
}

/* prints
 for 12 through 15
 12
 13
 14
 15
*/

print("for 0 up to 5")

for i in 0..<5 { print("\(i)") }

/* prints
 for 0 up to 5
 0
 1
 2
 3
 4
*/

Saturday, April 15, 2017

Template Design Pattern in Swift

In the Template Design pattern, you write an abstract class where an algorithm is implemented, but at least one part of the algorithm is not implemented -- it calls an abstract/unimplemented method for some part of the algorithm. The subclass inherits from the abstract class, implementing the abstract method(s) but not changing the overall algorithm. 

I will show two ways to implement this design pattern.

First, using classes:

import Foundation

class TemplateClass { // "Abstract" class
    func doit() {
        before()
        during()
        after()
    }
    func before() {
        print("before")
    }
    
    func during() { // in C++ should be "pure virtual".
        fatalError("subclass must override")
    } // in Java, should be "abstract"
    
    func after() {
        print("after")
    }
}

class TemplateClassExample: TemplateClass {
    
    override func during() {
        print("during")
    }
}

var tx = TemplateClassExample()

tx.doit() // prints "before", "during", "after"

As you can see, Swift doesn't allow us to declare a method "abstract". It's similar to Objective-C and Smalltalk in this way. We can put in an assertion or throw an exception, but we don't have the compiler forcing us to override/implement the abstract method.

Second, using Protocols:

protocol TemplateProtocol {
    func during()
}

extension TemplateProtocol {
    func doit() {
        before()
        during()
        after()
    }
    func before() {
        print("before")
    }
    func after() {
        print("after")
    }
}

class TemplateProtocolExample: TemplateProtocol {

    internal func during() {
        print("during")
    }
}

var tp = TemplateProtocolExample()

tp.doit() // prints "before", "during", "after"

Here the protocol forces us to implement the abstract method, but we can't put the algorithm's concrete methods in the protocol declaration, they have to go in the protocol extension. This seems to be a rather arbitrary restriction, but it's probably reflecting the way Swift evolved. I don't think the earliest versions of Swift allowed extending protocols with extensions containing concrete methods, though obviously Swift 3.x does allow this.


Friday, August 12, 2016

CGRect Extensions in Swift

While I was implementing drawing code, I found I wanted several CGRect functions that did not exist in Swift 2 and Swift 3. This is the Swift 2 code and tests that I implemented.  The following explanation of that code can be copied into a playground if you desire.

/*
  CGRectExtensions.playground

  Created by C. Keith Ray on 2016.08.06.
  Copyright © 2016 by C. Keith Ray. All rights reserved.
  Permission is granted for anyone to use and modify this
  code for any purpose, as long as this copyright message
  is retained.
*/

import Cocoa
import CoreGraphics

/* These are the constructors available for CGRect:

 CGRect()
 CGRect(origin: CGPoint, size: CGSize)
 CGRect(x: CGFloat, y: CGFloat, width: CGFloat, 
     height: CGFloat)
 CGRect(x: Double, y: Double, width: Double, height: Double)
 CGRect(x: Int, y: Int, width: Int, height: Int)

 I'd like to be able to create CGRects given a height 
 and width, and a center-point. Creating a new constructor 
 isn't hard, we just add it to an extension.
*/

extension CGRect {
    public init(center: CGPoint, size: CGSize) {
        // This assumes width and height are positive numbers.
        let origin = CGPoint(x: center.x - size.width/2.0,
            y: center.y - size.height/2.0)
        self.init(origin: origin, size: size)
    }
}

let a = CGRect(center: CGPoint(x:25,y:15), size: CGSize(width:20,height:10))
print("a.origin.x = \(a.origin.x)")
print("a.origin.y = \(a.origin.y)")
print("a.size.width = \(a.size.width)")
print("a.size.height = \(a.size.height)")
/* prints:
a.origin.x = 15.0
a.origin.y = 10.0
a.size.width = 20.0
a.size.height = 10.0
*/

/*
 We'd also like to get the center of the rectangle easily.
*/

extension CGRect {
    public var center: CGPoint {
        get { return CGPoint(x: self.midX, y: self.midY) }
    }
}

print("a.center = \(a.center)")
// a.center = (25.0, 15.0)

/*
 I'd like to be able to get a rectangle that is close to
 an existing rectangle, but is a different size.
 For example, a taller and thinner rectangle at the same
 origin: ("inc" stands for "increment")

 let b = a.incHeightBy(5).incWidthBy(-5)

 Also, I don't want to modify the original rectangle. Using
 existing features we could do this instead:

 let b = a // copy a
 b.size.height += 5
 b.size.width -= 5

 but you can see that takes more lines of code.
*/

extension CGRect {
    public func incWidthBy(deltaX: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.width += deltaX
        return result
    }
    
    public func incHeightBy(deltaY: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.height += deltaY
        return result
    }
}

/* 
 We call self.standardized because rectangles can have 
 negative width and height (which puts the origin at the 
 opposite corner than usual) but we don't want to deal 
 with that in our math. The existing CGRect functions
 call self.standardized, too.
*/

let b = a.incHeightBy(5).incWidthBy(-5)
print("a = \(a)") // a = (15.0, 10.0, 20.0, 10.0)
print("b = \(b)") // b = (15.0, 10.0, 15.0, 15.0)

/*
 I want similar functions to change the origin. A 
 rectangle with the same width and height, but a 
 different origin can be gotten by:

 let c = a.incXBy(2).incYBy(3)

 Again, not changing the original rectangle. An 
 alternative would be:

 let c = a // copy a
 a.origin.x += 2
 a.origin.y += 3

 (Note: "incOriginBy(dx:dy:)" is essentially the 
 same as the already-existing function 
 "offsetBy(dx:dy:)")
*/

extension CGRect {
    public func incXBy(deltaX: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.x += deltaX
        return result
    }
    
    public func incYBy(deltaY: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.y += deltaY
        return result
    }
}

let c = a.incXBy(2).incYBy(3)
print("a = \(a)") // a = (15.0, 10.0, 20.0, 10.0)
print("c = \(c)") // c = (17.0, 13.0, 20.0, 10.0)

/*
 To be complete, we can also increment origin and size 
 using pairs of floats.
*/

extension CGRect {
    public func incOriginBy(dx deltaX: CGFloat
                            dy deltaY: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.x += deltaX
        result.origin.y += deltaY
        return result
    }
    public func incSizeBy(dx deltaX: CGFloat
                          dy deltaY: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.width += deltaX
        result.size.height += deltaY
        return result
    }
}

let d = CGRect(x: 10, y: 20, width: 30, height: 40)
let e = d.incOriginBy(dx: 5, dy: 10)
let f = d.incSizeBy(dx: 15, dy: 20)
print("d = \(d)")  // d = (10.0, 20.0, 30.0, 40.0)
print("e = \(e)")  // e = (15.0, 30.0, 30.0, 40.0)
print("f = \(f)")  // f = (10.0, 20.0, 45.0, 60.0)

/*
 We made center as a getter, but we can also make a 
 function that returns a new rectangle with a different 
 center. I'll call that newCenter.
*/

extension CGRect {
    public func newCenter(cxy: CGPoint) -> CGRect {
        let me = self.standardized
        let result = CGRect(center: cxy, size: me.size)
        return result
    }
}

let g = a.newCenter(CGPoint(x: 50, y: 30))
print("a.center = \(a.center)")
print("a = \(a)")
print("g = \(g)")
print("g.center = \(g.center)")
/* prints
a.center = (25.0, 15.0)
a = (15.0, 10.0, 20.0, 10.0)
g = (40.0, 25.0, 20.0, 10.0)
g.center = (50.0, 30.)
*/

/*
 and, last but not least, an overload of newCenter taking 
 x,y instead of CGPoint.
*/

extension CGRect {
    public func newCenter(newX newX: CGFloat, newY: CGFloat
            -> CGRect {
        let me = self.standardized
        let result = CGRect(center: CGPoint(x: newX, y: newY), 
                              size: me.size)
        return result
    }
}

let h = a.newCenter(newX: 50, newY: 30)
print("a.center = \(a.center)")
print("a = \(a)")
print("h = \(h)")
print("h.center = \(h.center)")
/* prints
a.center = (25.0, 15.0)
a = (15.0, 10.0, 20.0, 10.0)
h = (40.0, 25.0, 20.0, 10.0)
h.center = (50.0, 30.0)
*/

/*
 One CGRect function I don't need to implement is "insetBy",
 which reduces the size of a rectangle, but keeps the same 
 center by altering the origin.
*/

let i = h.insetBy(dx: 3, dy: 3)
print("h = \(h)")
print("i = \(i)")
print("i.center = \(i.center)")
print("h.center = \(h.center)")
/* prints
h = (40.0, 25.0, 20.0, 10.0)
i = (43.0, 28.0, 14.0, 4.0)
i.center = (50.0, 30.0)
h.center = (50.0, 30.0)
*/

//
//  CGRectExtensions.swift
//
//  Created by C. Keith Ray on 2016.08.06.
//  Copyright © 2016 by C. Keith Ray. All rights reserved.
//  Permission is granted for anyone to use and modify this
//  code for any purpose, as long as this copyright message
//  is retained.
//

import Foundation
import CoreGraphics

extension CGRect {
    
    public init(center: CGPoint, size: CGSize) {
        let origin = CGPoint(x: center.x - size.width/2.0,
            y: center.y - size.height/2.0)
        self.init(origin: origin, size: size)
    }
    
    public var center: CGPoint {
        get { return CGPoint(x: self.midX, y: self.midY) }
    }
    
    public func newCenter(cxy: CGPoint) -> CGRect {
        let me = self.standardized
        let result = CGRect(center: cxy, size: me.size)
        return result
    }
    
    public func newCenter(newX newX: CGFloat, newY: CGFloat
            -> CGRect {
        let me = self.standardized
        let result = CGRect(center: CGPoint(x: newX, y: newY), 
                              size: me.size)
        return result
    }
    
    public func incXBy(deltaX: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.x += deltaX
        return result
    }
    
    public func incYBy(deltaY: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.y += deltaY
        return result
    }
    
    public func incOriginBy(dx deltaX: CGFloat
                            dy deltaY: CGFloat) -> CGRect {
        var result = self.standardized
        result.origin.x += deltaX
        result.origin.y += deltaY
        return result
    }
    
    public func incWidthBy(deltaX: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.width += deltaX
        return result
    }
    
    public func incHeightBy(deltaY: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.height += deltaY
        return result
    }
    
    public func incSizeBy(dx deltaX: CGFloat
                          dy deltaY: CGFloat) -> CGRect {
        var result = self.standardized;
        result.size.width += deltaX
        result.size.height += deltaY
        return result
    }
}

//
//  CGRectExtensionsTests.swift
//
//  Created by C. Keith Ray on 2016.08.06.
//  Permission is granted for anyone to use and modify this
//  code for any purpose, as long as this copyright message
//  is retained.

import XCTest

class CGRectExtensionsTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    func test_init_center() {
        let result = CGRect(center: CGPoint(
            x: 120.0y: 100.0), size: CGSize(width: 
            6.0, height: 4.0))
        let expected = CGRect(x: 120 - 3, y: 100 - 2
            width: 6.0height: 4.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_center() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.center
        let expected = CGPoint(x: 60.0, y: 120.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incXBy5() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incXBy(5.0)
        let expected = CGRect(x: 15.0, y: 20.0
            width: 100.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incXByMinus5() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incXBy(-5.0)
        let expected = CGRect(x: 5.0, y: 20.0
            width: 100.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incYBy5() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incYBy(5.0)
        let expected = CGRect(x: 10.0, y: 25.0
            width: 100.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incYByMinus5() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incYBy(-5.0)
        let expected = CGRect(x: 10.0, y: 15.0
            width: 100.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incOriginBy() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incOriginBy(dx: 5.0, dy: 6.0)
        let expected = CGRect(x: 15.0, y: 26.0
            width: 100.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incWidthBy() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incWidthBy(5.0)
        let expected = CGRect(x: 10.0, y: 20.0
            width: 105.0height: 200.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incHeightBy() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incHeightBy(5.0)
        let expected = CGRect(x: 10.0, y: 20.0
            width: 100.0height: 205.0)
        XCTAssertEqual(result, expected)
    }
    
    func test_incSizeBy() {
        let input = CGRect(x: 10.0, y: 20.0, width: 
            100.0height: 200.0)
        let result = input.incSizeBy(dx: 5.0, dy: 4.0)
        let expected = CGRect(x: 10.0, y: 20.0
            width: 105.0height: 204.0)
        XCTAssertEqual(result, expected)
    }
}