Functional Swift: Using Array filter and forEach functions

Recently, I started learning a couple of functional languages through online MOOCs:

Update: I created a Github page with other functional examples.

Swift isn’t classified as a functional language. However, that doesn’t mean we can’t use lessons learned from functional programming to improve our code. In this article, I illustrate a few ways my programming has changed after a couple of weeks of functional programming.

[].filter

There are many places in my code that looked something like this snippet:

self.retestQuestions = []

for q in questionList {
    if !q.isAnsweredCorrectly {
        self.retestQuestions.append(q)
    }
}

This translates to one line of Swift code:

let retestQuestions = questionList.filter({!$0.isAnsweredCorrectly})

In the past, I’ve used filter() to replace NSPredicate statements, which I used extensively in Objective C. In my first conversion to Swift, I directly translated my Objective C to Swift then cast my Swift array back to an NSArray to get the filter to work. Roughly my code looked like this:

let predicate = NSPredicate(format: "numWordsInGroup >= \(minWordCount) and  numPicturesInGroup >= \(minPictureCount)")

let answer = (a1 as NSArray).filtered(using: predicate)

Embracing the Array filter() function makes a lot more sense:

let answer = a1.filter({$0.numWordsInGroup >= minWordCount && $0.numPicturesInGroup >= minPictureCount})

After only a couple of weeks of functional programming, I’ve noticed many places in my code where this example, and other

[].forEach

The forEach() function isn’t a functional concept. However, without going into detail, my functional programming has led me to prefer using the Array forEach function more often. Here are several snippets from my code:

Add subviews to parent

[pronounView, answerWellView, answerConjugationView].forEach {self.mainContainerView.addSubview($0)}

Update: Someone pointed out on Reddit that this can be simplified:

// Simply pass func to apply to each element
[pronounView, answerWellView, answerConjugationView].forEach (mainContainerView.addSubview)

Active constraints

[c1, c2].forEach {NSLayoutConstraint.activate($0)}

Applying the same concept of passing the function as an argument to forEach(), activating constraints is even simpler.

[c1, c2].forEach(NSLayoutConstraint.activate) 

translatesAutoresizingMaskIntoConstraints and addSubviews

[answer1Btn, answer2Btn, answer3Btn, answer4Btn].forEach {
    $0.translatesAutoresizingMaskIntoConstraints = false
    self.cardFrontSubView.addSubview($0)
}

Disable and hide buttons

[answer1Btn,answer1Btn,answer3Btn,answer4Btn].forEach {
    $0.isEnabled = false
    $0.isHidden = false
}

Conclusion

I’ve just finished the second week, of a six week, Haskell MOOC. Hopefully, soon I’ll be using more functional concepts and my code will be “cleaner”, and more importantly contain fewer bugs.

Disabling Activity Tracing in Xcode 8

In Xcode 8, every time you run your app, there is additional logging information in the debugging window that looks similar to this:

2016-09-29 12:37:04.826400 LosVerbos[24208:17542891] subsystem: com.apple.UIKit, category: HIDEventFiltered, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-29 12:37:04.833232 LosVerbos[24208:17542891] subsystem: com.apple.UIKit, category: HIDEventIncoming, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-29 12:37:04.844944 LosVerbos[24208:17542886] subsystem: com.apple.BaseBoard, category: MachPort, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0

xcode

While this may be useful at some point, I find that it makes it harder to see my own debugging messages. These messages can be disabled by setting an environment variable in your schema: OS_ACTIVITY_MODE disable

 

schema
More on Apple’s new logging: WWDC 2016 – Session 721 – iOS, macOS – Unified Logging and Activity Tracing

My iOS 10 and Swift 3 Refactor List

I used an early beta of Xcode 8 to begin my conversion to Swift 3, which led to some issues so I had to do a lot of manual changes. Actually the conversion tool still works well a line at a time. Next Error followed by Return (CMD-‘, Return) is quite handy.

I thought it would be great to track them and keep a list in one place.

layerClass() method is now a variable:

override public class func layerClass() -> AnyClass {
    return CAEmitterLayer.self
}

override public class var layerClass: AnyClass {
    return CAEmitterLayer.self
}

Method signature in AppDelegate changed to:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

Still unresolved: ??

CGAffineTransform.scale(transform, scale, scale)
CGAffineTransform.scaledBy(transform, scale, scale) ???
transform = CGAffineTransform(scaleX: scale, y:scale)

CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
context.setStrokeColor(red: 1, green: 0, blue: 1, alpha: 1)
[color getRed:&red0 green:&green0 blue:&blue0 alpha:&alpha0];
       var red0:CGFloat = 0
        var green0:CGFloat = 0
        var blue0:CGFloat = 0
        var alpha0:CGFloat = 0
color.getRed(&red0, green: &green0, blue: &blue0, alpha: &alpha0)
NSMakeRange(col, 1) // Unresolved
substringWithRange()
let x = rowString.substring(with: <#T##Range#>)
UIFont *aFont = [UIFont fontWithName:"American Typewriter" size:fontSize]
let aFont = UIFont(name:"American Typewriter", size:fontSize)
NSDictionary *attributes = @{NSFontAttributeName:  aFont}
let attributes = [NSFontAttributeName:  aFont]
UIRectCornerAllCorners
UIRectCorner.allCorners
xmlText.componentsSeparatedByString("\n")
xmlText.components(separatedBy: "\n")
anArray.joinWithSeparator(",")
anArray.joined(separator: ",")
String(contentsOfURL: xmlUrl)
String(contentsOf: xmlUrl)
url.componentsSeparatedByString("/")
url.components(separatedBy: "/")
"  foobar  ".stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
"  foobar  ".trimmingCharacters(in: NSCharacterSet.whitespaces)
NSTextAlignment.Center
NSTextAlignment.center
forState: .Normal
forState: .normal
.TouchUpInside 
.touchUpInside
url.rangeOfString("github.com")
url.contains("github.com")
// https://www.hackingwithswift.com/swift3
stringByReplacingOccurrencesOfString(" ", withString:"_")
"Hello, world".replacingOccurrences(of: "Hello", with: "Goodbye")
// https://swift.org/migration-guide/
DispatchQueue.GlobalAttributes.qosDefault
        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {

}

UIApplication.sharedApplication().
UIApplication.shared.

UIDevice.currentDevice().
UIDevice.current.
UIBezierPath(ovalInRect
UIBezierPath(ovalIn

touchesBegan() signature changed?

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
            if let touch = touches.first {
                let point = touch.location(in: self.superview)
            }

    }

moveToPoint()
move(to:
addLineToPoint()        
addLine(to: 
closePath()
close()
CGGradientCreateWithColors
CGColorSpaceCreateDeviceRGB
drawInRect
.draw(in: 
CGRectGetMaxX
CGRectGetMaxY

self.maxX
self.maxY
CGRectGetMidX()
CGRectGetMidY()

self.midX
self.midY
CGContextStrokePath(context)
context.strokePath()
CGContextSetLineWidth(context, 20)
context.setLineWidth(20)
CGRectInset()
self.bounds.insetBy(dx: 3, dy: 3)
self.view.bringSubviewToFront(cardFront)
self.view.bringSubview(toFront: cardFront)

self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier:CellIdentifier)

self.tableView.register(UITableViewCell.self, forCellReuseIdentifier:CellIdentifier)

self.showViewController(viewController, sender: self)
self.show(viewController, sender: self)
lowercaseString
lowercased()
//http://stackoverflow.com/questions/38964264/openurl-in-ios10

UIApplication.shared.openURL(URL(string: appStoreUrl)!)
UIApplication.shared.open(URL(string:appStoreUrl)!, options: [:])

type(of:)

CGColorRef

UIGraphicsPushContext(context);
UIGraphicsPopContext();

context.saveGState()
context.restoreGState()
CalendarIdentifierGregorian
Calendar.Identifier.gregorian
Calendar.currentCalendar()
Calendar.current
CalendarOptions
Calendar.Options
.dateFromComponents()
.date(from:)
.dateByAddingComponents()
Calendar.current.date(byAdding: dateComponents, to: )

Autolayout issue Xcode 8 beta 6: _SwiftValue nsli_superitem

I’m converting my Objective C and Swift 2.x code to Swift 3 and I started getting this strange error:

[_SwiftValue nsli_superitem]: unrecognized selector sent to instance 0x610000056740

This occurred in my NSLayoutConstraint.constraints() methods:

[constraintString1, constraintString2].forEach {

    let constraint = NSLayoutConstraint.constraints(withVisualFormat: $0, options: [], metrics: metrics, views: viewDictionary)

   NSLayoutConstraint.activate(constraint)

}

Currently, I used inferred types on my viewDictionary and metrics variables:

let viewDictionary = [
"answerView1": self.answerView1,
"answerView2": self.answerView2,
"answerView3": self.answerView3,
"answerView4": self.answerView4,
]

let metrics = [:]

The workaround seems to be to provide explicit types on these two variables:

let viewDictionary:[String:UIView] = [
"answerView1": self.answerView1,
"answerView2": self.answerView2,
"answerView3": self.answerView3,
"answerView4": self.answerView4,
]

let metrics:[String:AnyObject] = [:]

There’s probably a deeper issue here but at this point, I’m simply glad to have my app running again. I’ll post more if I figure out any additional details.