November 10, 2015
Functionally Dreaming with Swift
Making iOS or OS X apps using a functional programming approach means dealing with a lot of application pieces designed using an Object Oriented (OO) approach if you use the default templates and libraries. The user interface (UI) and the data transmission and storage behaviors for both OS’s are heavily object oriented. This dramatically restricts the space where functional programming designs and techniques can be applied.
Having experienced this restriction I decided to explore what iOS development would be like if the UI behaviors were functional rather than OO in their design. Swift, much more than Objective-C, enables this kind of exploration so I created a Swift example of what could be done. I call it SwiftlyFunctional and an example Xcode project is in my gitHub repository.
Swift uses pass-by-reference for instances of classes and pass by value for structs. To have a ‘truly’ functional approach the UI Elements of iOS and OS X would have to be changed to be structs. That isn’t going to happen and I have no desire to write struct wrappers for the many different types of UI elements in iOS and OS X. It wouldn’t be impossible, but is outside of the scope of what I wanted to explore. I’m chose to ignore this problem as part of my dreaming.
When laying out what I hoped to accomplish, I decided I still wanted to be able to use Interface Builder (IB) to design and layout the UI, but not be restricted to using the Object Oriented (OO) design enforced by the current Xcode templates. Because of this, I decided to dump working with the ApplicationDelegate and ViewController classes generated in a new iOS and OS X projects. Instead of using the ViewController class to add IBAction Selectors to UI elements and gesture recognizers as is traditionally done, I wanted to use Swift functions and closures to handle UI events.
With these goals in mind, I began my dreaming. What I show here is what I came up with but it is only one of many ways this could be done. I’ll show you how to use the SwiftlyFunctional code in this posting. In subsequent postings I’ll describe how some of the more interesting portions of the code work.
Let’s start with the structure of a functional iOS app. Image 1 shows that the ApplicationDelegate.swift and ViewController.swift files are gone. Instead you can see the SwiftlyFunc.swift and Main.swift files. You can call Main.swift anything you would like except for main.swift. The capitalization is important. If you try to use main.swift you will get a compilation error. This is likely due to the hidden files in all Swift apps that allow for Objective-C interactions. The file main.h is part of the default Objective-C projects.
Main.swift is where you will begin writing the code for your application. It contains the main(application:userView:launchReasons) function. This function acts somewhat similar to the C main function and is the where the app will start running your code.
the main function, as you can see in the example code, is where you can attach Swift functions or closures to application events such as ‘did become active’ and UI elements like buttons, images and labels.
In the example source code, and in Image 2, you can see an example of adding a closure for the ‘did become active’ application event.
Image 2: Attaching a closure to an Application level event.
The addApplicationEventHandler(application:theEventType) function is part of the SwiftlyFunctional API. It can be used to add a closure to any app event except application(didFinishLaunchingWithOptions:launchOptions). That one happens much earlier than will be captured and made available for closures. The addApplicationEventHandler function has three parameters, the application running (this is the first parameter of the main function), an enum for the type of application event to map to, and a closure with no parameters and a void return. In image two the closure only prints a string to the console, but you would be able to perform any computation normally associated with a ‘did become active’ event in iOS.
image 3 contains the complete list of Application events to which SwifltyFunctional can add closures or function. If other events are added by Apple they can be easily added to the enum.
Assigning closures to events for UIControl elements such as Buttons is nearly the same. One major difference is the closure must be associated with an event triggered by a specific UI element. In the case of the example, this is a UIButton. To make this possible, a reference to the button is needed. The SwiftlyFunctional API has a method, getViewByUniqueID(containingView:anID).
The first parameter passed to getViewByUniqueID in Image 4 is ‘userView.’ This UIView is the same as the second parameter of the main function and represents the topmost view of the view controller in the view hierarchy created in IB.
Image 4: Attaching closures to UIButton events.
The second parameter is the type of event, TouchDown and TouchUpInside for this code snippet. These event types are part of of the standard iOS library UIControlEvents struct. Like the addApplicationEventHandler function, the last parameter is the closure to activate when the event of touchEventType occurs.
Closures can also be attached to gestureRecognizers. In the SwiftlyFunctional example project users Interface Builder to add a TapGestureRecognizer to chalk.png’s UIImageView. The TapGestureRecognizer and the UIImageView both have a uinqueID added to them using KeyPaths. You can see how this is done by examining the right-hand side of Image 5.
Image 5: UITapGestureRecognizer for a UIImageView.
Set up this way, the SwiftlyFunctional’s getViewByUniqueID, getGestureRecognizerByUniqueID, and addTouchEventHandler functions can be used to find the recognizer and attach a closure to it (See Image 6).
Image 6: Attaching a closure to a UIGestureRecognizer event.
Unlike closures for UIControls such as UIButtons, no UIEvent object is passed to the closure by SwiftlyFunctional. UIGestureRecognizers contain the information obtainable from UIEvents. Notice that the location of the tap in the UIImageView is directly available without getting or using a Set of UITouches like you saw in the UIButton code snippet.
So there it is. Now closures can be attached to Application, UIControl, and UIGestureRecognizer events using a functional rather than an OO approach. Take a look at the example project, specifically the SwiftlyFunc.swift file, to see how this was done. I’ll follow up this posting with a couple of explanations of the SwiftlyFunctional code.