Just how expensive is creating an NSDateFormatter?

One of the many mantras that is drilled into iOS devs is that you shouldn’t recreate expensive, complex objects like formatters every time you need them. Instead, keep them around in a static or instance variable and use them as needed. To test this, I created a playground and put this file in the Sources folder:

import Foundation
import QuartzCore
public func testWithMultipleInstantiation() -> CFTimeInterval {
var dateStrings: [String] = []
dateStrings.reserveCapacity(100000)
let start = CACurrentMediaTime()
for _ in 0..<100000 {
let df = NSDateFormatter()
df.dateStyle = .MediumStyle
df.timeStyle = .MediumStyle
dateStrings.append(df.stringFromDate(NSDate()))
}
let end = CACurrentMediaTime()
return end - start
}
public func testWithSingleInstantiation() -> CFTimeInterval {
var dateStrings: [String] = []
dateStrings.reserveCapacity(100000)
let start = CACurrentMediaTime()
let df = NSDateFormatter()
df.dateStyle = .MediumStyle
df.timeStyle = .MediumStyle
for _ in 0..<100000 {
dateStrings.append(df.stringFromDate(NSDate()))
}
let end = CACurrentMediaTime()
return end - start
}
public func testWithStaticMethod() -> CFTimeInterval {
var dateStrings: [String] = []
dateStrings.reserveCapacity(100000)
let start = CACurrentMediaTime()
for _ in 0..<100000 {
dateStrings.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .MediumStyle, timeStyle: .MediumStyle))
}
let end = CACurrentMediaTime()
return end - start
}
view raw FormatterTest.swift hosted with ❤ by GitHub

I didn’t put the code directly into the playground because doing ANYTHING in a playground 100k times is going to take a very long time and not be very representative of real-world conditions.

Here’s the playground code, which simply invokes the tests and records the results:

import Foundation
let timeForMultiple = testWithMultipleInstantiation()
let timeForSingle = testWithSingleInstantiation()
let timeForStatic = testWithStaticMethod()
print("multiple:", timeForMultiple) //multiple: 7.96959602891002
print("single: ", timeForSingle) //single: 0.888885863008909
print("static: ", timeForStatic) //static: 7.89041009696666
view raw Playground.swift hosted with ❤ by GitHub

The results were unequivocal: creating an NSDateFormatter each time you need to use it is roughly 9x slower than creating it once and using it repeatedly. That said, this is only likely to be an issue for tasks where there is a lot of date formatting going on, such as in a table view with dates in every cell. If you have a situation where you only need to format a single date infrequently (today’s date in a page header, for example), then you shouldn’t worry about hanging on to the date formatter; even though it’s heavy, it only takes a tiny fraction of a second to instantiate one.

CONVENTIONAL WISDOM: CONFIRMED

Addendum: At the request of a coworker (@rexeisen), I added in a test of the static NSDateFormatter.
localizedStringFromDate(_:dateStyle:timeStyle:)
test. As you can see, the results are no better than creating an instance each time. That said, it would be more convenient to use for 1-off formatting tasks, where keeping the formatter around is unnecessary.

Addendum II (2016-01-15): Further testing has revealed that changing the timeStyle and dateStyle of an NSDateFormatter is tremendously expensive. Even more so than just creating a new formatter for each use! Across several trials, performance using a single NSDateFormatter that is re-parameterized on each use was 10% slower than creating a new formatter each time and a full 10x slower than using a single, pre-configured formatter. The take away here is, create a formatter for each repeating case, don’t try to make a single shared formatter do all the work.

Using didSet to configure IBOutlet views…

Swift’s didSet property observer is a great way to configure views linked via IBOutlet. It allows you to set properties that need to be dynamic at runtime or that can’t be configured via Interface Builder:

class MyClass {
@IBOutlet weak var outputLabel: UILabel! {
didSet {
// Ensure that the label wasn't just set to nil.
guard let outputLabel = self.outputLabel else { return }
// Set the text color based on the user's style choices.
outputLabel.textColor = StyleManager.sharedManager().outputLabelColor
// Set the label to use fixed-width numbers.
let oldDescriptor = outputLabel.font.fontDescriptor()
let fontAttributes = [
UIFontDescriptorFeatureSettingsAttribute : [
UIFontFeatureTypeIdentifierKey : kNumberSpacingType,
UIFontFeatureSelectorIdentifierKey : kMonospacedNumbersSelector
]
]
let newDescriptor = oldDescriptor.fontDescriptorByAddingAttributes(fontAttributes)
outputLabel.font = UIFont(descriptor: newDescriptor, size: 0)
}
}
}
view raw DidSetExample.swift hosted with ❤ by GitHub

However, it’s worth keeping a simple rule in mind when you go about configuring your views with didSet:

Don’t reference other IBOutlet views or implicitly unwrapped properties in your didSet block.

The reason for this is simple: you have no way of knowing which order your IBOutlets will be set in, so the other view or property you’re trying to access may not be there. At best, you’re going to be nil-checking a lot and end up only partially configuring your views, requiring follow-up elsewhere in code. At worst, you’re going to accidentally force-unwrap a nil and crash your app.

It’s okay to reference external objects, as seen in the call to the StyleManager above, as long as they’re non-optional and not subject to race conditions. The proper place to establish things like view layout relationships or properties from one view that copy the properties from another is still in viewDidLoad.

Avoid [unowned self] whenever possible!

One of the confusing aspects of Swift is how capture semantics work with closures. When used improperly, they can result in retain cycles or crash the app with the dreaded EXC_BAD_ACCESS. It is worth keeping in mind that capture semantics only apply to reference-based objects (classes); value objects can be used freely without worrying about this. The situation I’ll be looking at here is the capture of self, which is far-and-away the most common situation.

Many asynchronous processes such as API calls include a completion closure, and the most common way to provide it is as an inline closure. In this example, we’re storing the API request object that is created by the call to our API client so that we can allow the user to cancel the request if it’s taking too long. Let’s assume this code appears in our LoginViewController class.

func attemptLogin(user: String, password: String) {
self.loginRequest = APIClient.sharedClient().createLoginRequest(user:user, password:password) {
result in
switch result {
case .Success(let data):
parseLoginData(data) // Compiler error: implicit reference to self
case .Failure(let error):
errorHandlingMethod(error) // Compiler error: implicit reference to self
}
}
}

Whoops! The compiler is mad at us because we have calls to other methods in the class which have an implicit self in front of them. Swift requires that you be explicit about capturing references to objects to avoid unexpected behavior. Fine, let’s add self:

func attemptLogin(user: String, password: String) {
self.loginRequest = APIClient.sharedClient().createLoginRequest(user:user, password:password) {
result in
switch result {
case .Success(let data):
self.parseLoginData(data)
case .Failure(let error):
self.errorHandlingMethod(error)
}
}
}

Better! However, the closure is now holding a strong reference to self and the LoginViewController is holding a strong reference to the closure. This creates a retain cycle and will cause the LoginViewController to be kept alive, even if the user navigates away from this screen. Even if the login was successful and the completion closure was invoked, it still exists and maintains its capture of self unless you explicitly nil the reference as part of the completion. We don’t want to go leaking view controllers all willy-nilly, so let’s try that [unowned self] thing we saw in some WWDC video:

func attemptLogin(user: String, password: String) {
self.loginRequest = APIClient.sharedClient().createLoginRequest(user:user, password:password) {
[unowned self] result in
switch result {
case .Success(let data):
self.parseLoginData(data)
case .Failure(let error):
self.errorHandlingMethod(error)
}
}
}

Right, now we definitely don’t have a strong reference to self! However, users are reporting the app is crashing when login is taking too long and they leave the screen before it completes. Looking at the crash logs, you see a rash of EXC_BAD_ACCESS events occurring. Because you declared self was unowned, it was deallocated when the users left the screen, but when the API call completed, it attempted to call the methods referenced in the closure to disastrous effect.

Using unowned as a capture semantic is the equivalent of force-unwrapping an optional. It’s never a great idea and should only be done when you are 100% sure there’s no chance the captured object will be deallocated before it is invoked. When you’re dealing with long-running asynchronous tasks like API requests, this is a bad bet unless the originating class is a singleton or some other pattern which will guarantee the object exists for the lifetime of the app.

For all other cases, consider this pattern using weak capture semantics:

func attemptLogin(user: String, password: String) {
self.loginRequest = APIClient.sharedClient().createLoginRequest(user:user, password:password) {
[weak self] result in
guard let strongSelf = self else { return }
switch result {
case .Success(let data):
strongSelf.parseLoginData(data)
case .Failure(let error):
strongSelf.errorHandlingMethod(error)
}
}
}

The secret sauce is line 4, where we guard against the possibility that the LoginViewController was deallocated prior to the API request completing. If self no longer exists, it doesn’t care about the API result, and we can bail out of the closure right away. We simply use the strongSelf reference for the remainder of the closure to avoid unwrapping self at every step and we’re good to go!

Why you shouldn’t mourn the removal of –, ++ and C-style for loops from Swift

One of the neatest things about Swift going open-source earlier this year is that the deliberation process for the future of the language, including breaking changes to the syntax, is out in the open. Case in point are the two accepted proposals to remove the unary -- and ++ operators and to remove C-style for loops.

The case against — and ++

View the proposal.

Chris Lattner, the principle architect of Swift, has stated in the past that the ++ and -- operators were added very early in Swift’s inception simply because Objective-C had them. Now that Swift has had a chance to mature, there are a few factors which indicate they are a poor fit for the language. First and foremost is that they are confusing as hell to programmers who haven’t already spent time banging their heads against them in one of the C-derived programming languages. Consider this case:

let n = 4
print(--n) // Prints '3', value of n is now 3.
print(++n) // Prints '4', value of n is now 4.
print(n--) // Prints '4', value of n is now 3. Confusing!
print(n++) // Prints '3', value of n is now 4. Also confusing!

The trailing versions of these operators are particularly confusing, where the value is being returned prior to being changed. This difference in pre- and post-incrimenting of the variable is a particularly fruitful source of errors in code, with things like index values going out of range or holding unexpected values because the wrong operator was used.

The strongest case for keeping them is their brevity, but Swift does not favor brevity over security and, as Chris Lattner points out, the more expressive n += 1 is hardly an onerous amount of typing. The main use for the operators seems to be in C-style for loops (based on a survey of Swift-based GitHub projects). Thus, with the imminent removal of those for loops from the language, the main use-case for the operators will die with them.

The case against C-style for loops

View the proposal.

The C-style for loop, like the unary increment and decrement operators, were added early in Swift’s development simply because Objective-C had them. As Erica Sadun so eloquently points out in her proposal, they’re a hold-over from an earlier era of programming and have a complex and error-prone syntax. They accomplish nothing which can’t be accomplished in a more succinct and expressive fashion using the Swift for in loop. Consider these two examples, both of which combine strings from an array to a base string and prints them out:

C-style for loop

let baseString = "/Documents/"
let words = ["Alpha", "Beta", "Gamma", "Delta"]
var paths : [String] = []
for (var i = 0; i < words.count; ++i) {
let word = words[i]
paths.append("\(baseString)\(word)")
}
print(paths)

Note that the for loop syntax is completely non-expressive. There’s no indication of what each of the “;”-separated fields aims to accomplish…you have to already be familiar with it.

Swift map() function

let baseString = "/Documents/"
let words = ["Alpha", "Beta", "Gamma", "Delta"]
let paths = words.map({"\(baseString)\($0)"})
print(paths)
view raw MapExample.swift hosted with ❤ by GitHub

“But wait,” you might say, “I need the index value as well!” There are a couple of ways to do it in Swift without relying on C-style for loops:

Method 1: enumerate()
The handy enumerate() method is present on all collections conforming to SequenceType is one way:

for index, word in words.enumerate() {
funcThatRequiresWordAndIndex(word, index: index)
}

Method 2: for-in over a Range

for i in 0..<words.count {
funcThatRequiresWordAndIndex(words[i], index: i)
}

The 2nd method is stylistically closest to the C-style for loop, but it is still much easier to understand what values i will hold and isn’t subject the problem of a statement inside the for loop modifying the index and causing it to go out of bounds (i is constant).

Conclusion

It can feel a little jarring to lose language features, but with some thought it is clear to see that the removal of these 2 features will result in a language that is more expressive and less error prone.

Don’t use Swift enums to contain magic strings!

At first glance, using a Swift enum with a raw type of String seems to be a great way to package (or, if you like, enumerate) magic strings used by things like Notifications:

enum NotificationNames: String {
case UserDataChanged: "UserDataChangedNotificationName"
case ReceivedAlert: "ReceivedAlertNotificationName"
case PeanutButterJellyTime: "ItsPeanutButterJellyTimeNotificationName"
}
view raw EnumExample.swift hosted with ❤ by GitHub

However, this is a poor application of the Swift enum for the following reason: you are not interested in the enum case, only its raw value. Any place you want to use the magic string in your code you’re forced into fully qualifying the enum case (because the argument type is a string, not the type of your enum) and then accessing the rawValue property.

A better approach is to use a Swift struct with static constant string members defining the magic strings:

struct NotificationNames {
static let userDataChanged = "UserDataChangedNotificationName"
static let receivedAlert = "ReceivedAlertNotificationName"
static let peanutButterJellyTime = "ItsPeanutButterJellyTimeNotificationName"
}
view raw StructExample.swift hosted with ❤ by GitHub

The look is very similar to an enum, but in practice it ends up being shorter and cleaner to use:

// Using the enum:
NSNotificationCenter.defaultCenter().postNotificationName(NotificationNames.UserDataChanged.rawValue, object:nil)
// Using the struct
NSNotificationCenter.defaultCenter().postNotificationName(NotificationNames.userDataChanged, object:nil)
// If you need switching, remember that Swift can switch on just about any comparison type:
func handleNotification(note: NSNotification) {
switch note.name {
case NotificationNames.userDataChanged: print("User data changed!")
case NotificationNames.receivedAlert: print("WHAT!? An alert!")
case NotificationNames.peanutButterJellyTime: print("Where ya at? Where ya at? Where ya at?")
default: print("I have no idea what's going on.")
}
}
view raw Usage.swift hosted with ❤ by GitHub

Now, this argument is moot if you have a situation where methods in your classes take your enum type as an argument and use the rawValue at some point internally, but for things like userInfo dictionary keys, user defaults keys, notification names, segue names, etc. you are better off with the struct approach, since the string is all you’re interested in.

2015-12-16 Addendum:
As with any advice on using Swift, this should not be viewed as an Immutable Truth of the Universe™. There are still situations when an enum would be a perfectly reasonable container for your strings: namely, when you have a model built around the use of the enums and not just the strings they contain.

iOS 9.1 Appearance Proxy

I made a quick Gist outlining the properties and methods made available for styling using the UIAppearance proxy.

View on GitHub

UIActivityIndicatorView

@property (nullable, readwrite, nonatomic, strong) UIColor *color NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UIBarButtonItem

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)backgroundImageForState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state style:(UIBarButtonItemStyle)style barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)backgroundImageForState:(UIControlState)state style:(UIBarButtonItemStyle)style barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (void)setBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

- (CGFloat)backgroundVerticalPositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

- (UIOffset)titlePositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setBackButtonBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (nullable UIImage *)backButtonBackgroundImageForState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (void)setBackButtonTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (UIOffset)backButtonTitlePositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (void)setBackButtonBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (CGFloat)backButtonBackgroundVerticalPositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

UIBarItem

- (void)setTitleTextAttributes:(nullable NSDictionary<NSString *,id> *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable NSDictionary<NSString *,id> *)titleTextAttributesForState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UIButton

@property(nonatomic) UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero

- (void)setTitleColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default if nil. use opaque white

- (void)setTitleShadowColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil. use 50% black

- (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil

UINavigationBar

@property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent

@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

@property(nullable, nonatomic,strong) UIImage *shadowImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nullable,nonatomic,copy) NSDictionary<NSString *,id> *titleTextAttributes NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable,nonatomic,strong) UIImage *backIndicatorImage NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

@property(nullable,nonatomic,strong) UIImage *backIndicatorTransitionMaskImage NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)backgroundImageForBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)backgroundImageForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (CGFloat)titleVerticalPositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UIPageControl

@property(nullable, nonatomic,strong) UIColor *pageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

UIProgressView

@property(nonatomic, strong, nullable) UIColor* progressTintColor  NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic, strong, nullable) UIColor* trackTintColor     NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic, strong, nullable) UIImage* progressImage      NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic, strong, nullable) UIImage* trackImage         NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UIRefreshControl

@property (nullable, nonatomic, strong) NSAttributedString *attributedTitle UI_APPEARANCE_SELECTOR;

UISearchBar

@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

@property(nullable, nonatomic,strong) UIImage *backgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic,strong) UIImage *scopeBarBackgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic) UIOffset searchFieldBackgroundPositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic) UIOffset searchTextPositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // Use UIBarMetricsDefaultPrompt to set a separate backgroundImage for a search bar with a prompt

- (nullable UIImage *)backgroundImageForBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;

- (void)setSearchFieldBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)searchFieldBackgroundImageForState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setImage:(nullable UIImage *)iconImage forSearchBarIcon:(UISearchBarIcon)icon state:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)imageForSearchBarIcon:(UISearchBarIcon)icon state:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setScopeBarButtonBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

- (nullable UIImage *)scopeBarButtonBackgroundImageForState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setScopeBarButtonDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)scopeBarButtonDividerImageForLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setScopeBarButtonTitleTextAttributes:(nullable NSDictionary<NSString *, id> *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable NSDictionary<NSString *, id> *)scopeBarButtonTitleTextAttributesForState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setPositionAdjustment:(UIOffset)adjustment forSearchBarIcon:(UISearchBarIcon)icon NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (UIOffset)positionAdjustmentForSearchBarIcon:(UISearchBarIcon)icon NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UISegmentedControl

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

- (nullable UIImage *)backgroundImageForState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)dividerImageForLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState barMetrics:(UIBarMetrics)barMetrics  NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setTitleTextAttributes:(nullable NSDictionary *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable NSDictionary *)titleTextAttributesForState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setContentPositionAdjustment:(UIOffset)adjustment forSegmentType:(UISegmentedControlSegment)leftCenterRightOrAlone barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

- (UIOffset)contentPositionAdjustmentForSegmentType:(UISegmentedControlSegment)leftCenterRightOrAlone barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UISlider

@property(nullable, nonatomic,strong) UIColor *minimumTrackTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic,strong) UIColor *maximumTrackTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic,strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UIStepper

- (void)setBackgroundImage:(nullable UIImage*)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage*)backgroundImageForState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (void)setDividerImage:(nullable UIImage*)image forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage*)dividerImageForLeftSegmentState:(UIControlState)state rightSegmentState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (void)setIncrementImage:(nullable UIImage *)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)incrementImageForState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (void)setDecrementImage:(nullable UIImage *)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)decrementImageForState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

UISwitch

@property(nullable, nonatomic, strong) UIColor *onTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic, strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic, strong) UIImage *onImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic, strong) UIImage *offImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

UITabBar

@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

@property(nullable,nonatomic,strong) UIColor *selectedImageTintColor NS_DEPRECATED_IOS(5_0,8_0,"Use tintColor") UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

@property(nullable, nonatomic,strong) UIImage *backgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

@property(nullable, nonatomic,strong) UIImage *selectionIndicatorImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

@property(nullable, nonatomic,strong) UIImage *shadowImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic) UITabBarItemPositioning itemPositioning NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

@property(nonatomic) CGFloat itemWidth NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic) CGFloat itemSpacing NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;

@property(nonatomic) UIBarStyle barStyle NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED;

UITabBarItem

@property (nonatomic, readwrite, assign) UIOffset titlePositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

UITableView

@property (nonatomic) UIEdgeInsets separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // allows customization of the frame of cell separators

@property (nonatomic, strong, nullable) UIColor *sectionIndexColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;          // color used for text of the section index

@property (nonatomic, strong, nullable) UIColor *sectionIndexBackgroundColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;         // the background color of the section index while not being touched

@property (nonatomic, strong, nullable) UIColor *sectionIndexTrackingBackgroundColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; // the background color of the section index while it is being touched

@property (nonatomic, strong, nullable) UIColor *separatorColor UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // default is the standard separator gray

@property (nonatomic, copy, nullable) UIVisualEffect *separatorEffect NS_AVAILABLE_IOS(8_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // effect to apply to table separators

UITableViewCell

@property (nonatomic) UIEdgeInsets  separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // allows customization of the separator frame

@property (nonatomic) UITableViewCellFocusStyle       focusStyle NS_AVAILABLE_IOS(9_0) UI_APPEARANCE_SELECTOR;

UIToolbar

@property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent

@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forToolbarPosition:(UIBarPosition)topOrBottom barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)backgroundImageForToolbarPosition:(UIBarPosition)topOrBottom barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

- (void)setShadowImage:(nullable UIImage *)shadowImage forToolbarPosition:(UIBarPosition)topOrBottom NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

- (nullable UIImage *)shadowImageForToolbarPosition:(UIBarPosition)topOrBottom NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;

UIView

@property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is nil. Can be useful with the appearance proxy on custom UIView subclasses.

 

Better mogenerator Swift Templates

It finally happened that Core Data was required for one of my Swift 2 projects, so naturally I turned to mogenerator to produce my human and machine NSManagedObject subclasses. However, I was unsatisfied with the results of the machine file…particularly, the tendency of the template to clutter up the global namespace with enums for the attributes and relationships. Here’s the default template’s output for an entity named Category with a pair of attributes and one relationship.

// DO NOT EDIT. This file is machine-generated and constantly overwritten.
// Make changes to Category.swift instead.
import CoreData
public enum CategoryAttributes: String {
case displayName = "displayName"
case id = "id"
}
public enum CategoryRelationships: String {
case filters = "filters"
}
@objc public
class _Category: NSManagedObject {
// MARK: - Class methods
public class func entityName () -> String {
return "Category"
}
...

As you can see, it creates a pair of public enums to enumerate the names of the attributes and relationships. This isn’t too bad for one or two entities, but gets annoying when you have dozens of entities, each adding 2-4 new global-level types.

Moreover, I have a problem with the use of the enum type. Yes, we are enumerating the possible attributes for this entity, but absolutely nothing in the Core Data framework will accept them; it’s purely string-based. As a result, anywhere you use the enum cases, you’ll have to tack on the .rawValue accessor to get the underlying string. I figured we could do better:

// DO NOT EDIT. This file is machine-generated and constantly overwritten.
// Make changes to Category.swift instead.
import CoreData
@objc public
class _Category: NSManagedObject {
public struct Attributes {
static let displayName = "displayName"
static let id = "id"
}
public struct Relationships {
static let filters = "filters"
}
// MARK: - Class methods
public class func entityName () -> String {
return "Category"
}
...

The way I chose to address the first issue is to move the declarations inside the class declaration for the entity. Since Swift supports name-spacing, this means that instead of typing “CategoryAttributes.id”, you’d type “Category.Attributes.id”. This is only 1 more character and it results in only “Category” being added to the global namespace.

The second change I made was to convert the enums to structs with each of the entries being a static let string. The structs are never meant to be instantiated, they simply provide a coherent namespace for our attribute and relationship names.

You can get the updated template files here: https://gist.github.com/JoshuaSullivan/2057f09cc18243a3f2df

More information about using custom mogenerator templates can be found in this Stack Overflow post.