Friday, July 31, 2015

How To Write a Swift Generic Function Based Only On Return Type

TL;DR Annotate the return variable with a type, e.x. let foo:String? = Utility.nullableValueFromKey(“identifier", dictionary: jsonDictionary)
Versions: OS X 10.10.4  Xcode 6.4 iOS SDK 8.4 

When I started working with Objective-C coming from .NET, one of language features that I missed the most was Generics. They solve a whole class of problems that are tedious and/or require way more code than without generics. When Swift was announced with Generics...

Finally...It's Done

But Generics can be hard, thinking in T for any giving problem can make you a little crazy, especially when the compiler keeps yelling at you.

In Swift 1.0, I used generic functions to help parsing JSON server responses, the functions worked, but they were less than ideal.

The typical problem I wanted to solve was getting a primitive type out of the response dictionary that could be null.

I ended up with this function to do the trick:

class func originalNullableValue<T>(valueType: T, key: String, dictionary: NSDictionary) -> T? {
    var value:T? = nil
    var valueTemp = dictionary[key] as AnyObject! as? T
    if valueTemp != nil {
        value = valueTemp!
    }
    return value
}

Ugly! Why did I end up with this? Either I wasn’t smart enough to figure this out or the 1.0 compiler wasn’t.

Smart, but not Smart Enough

Getting either me or the compiler to figure out what type T was without passing an argument of that type into the method, was, well let’s just say it was the solution I found.

What I wanted was this:

class func nullableValueFromKey<T>(key: String, dictionary: NSDictionary) -> T? {
var value:T? = nil
    var valueTemp = dictionary[key] as AnyObject! as? T
    if valueTemp != nil {
        value = valueTemp!
    }
 
    return value
}

So I dusted off the original method and tried making it what I wanted with Xcode 6.4 & Swift 1.2.

Defining it works fine, but If you attempt to call it:

let foo = JSONUtility.nullableArrayFromKey("fooBar", json: Dictionary<String, AnyObject>())

The compiler returns this error:

Argument for generic parameter 'T' could not be inferred

I have no way to know/test if this is the same error that caused me trouble in Swift 1.0, but this time, either I or the compiler were smart enough to figure it out!

All you have to do is add the type to the variable declaration:

let bar:String? = JSONUtility.nullableValueFromKey("name", dictionary: Dictionary<String, AnyObject>())

Flawless Victory

Thursday, July 30, 2015

No Dan Gillmor, Government Should Do Nothing About Android Security

I nearly burst from laughter after reading this tweet and then article by Dan Gillmor:

What’s so funny? The free market is working in this case exactly as intended. A company in the market, e.x Apple, provides mobile devices that are usually secure and updated. Some consumers have voted with their dollars that isn’t as important to them as other criteria, so they bought an Android phone. There is no surprise here that if you buy an Android device, you highly likely will not get updates of any kind, security or otherwise. 

What criteria stops people from buying an Apple device? Let’s return to Mr. Gillmor:

Apple's iOS devices, of course, are part of a tightly controlled ecosystem, and while Apple is far from perfect on security, it does update iPhones. But we shouldn't be required to turn over our computing and communications to control-freak companies in order to get necessary security updates.

So let me get this straight? Mr. Gillmor doesn’t want Apple devices because Apple is a “control-freak” company, so he invites the control-freak government to use laws &  regulations & force Android implementors to be more control freaks about updates…like Apple. LOL. Sure, the government is always the lightest touch!

If Android users thought updates and security where higher priorities than cheap phones or “open source” software, then they wouldn’t have bought an Android device.

Returning this as not a bug, working as intended!

Epilogue

Google made this mess, they can still fix it. They already offer an Android Bug Bounty. They have a generic Patch Reward Program.

Instead of the heavy hand of laws and regulations, Google should start an Update Rewards program.

Every carrier or vendor that releases Android updates in a timely fashion (say within 1 month) gets a payment from Google.

Security Updates pay more than Feature Updates. To really sweeten the pot, Google can pay per user upgraded, get some vendor/carrier incentive to update as many users as possible.

Vendors/carriers have such thin margins, seems like they don’t have the money to cover testing and deploying Android updates without taking a loss.

Use some of that ad revenue to cover the costs. I mean, advertisers should be clamoring for this. After all, how can they trust the ad profiles Google vends with compromised devices?

Monday, July 13, 2015

How to Completely Eliminate UITableView Content and Separator Indent on UITableVIewCell

TL;DR

overridefunc viewDidLoad() {

    super.viewDidLoad()

         

    self.tableView.separatorInset = UIEdgeInsetsZero

    self.tableView.layoutMargins = UIEdgeInsetsZero

}

 

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    cell.layoutMargins = UIEdgeInsetsZero

}

Versions: OS X 10.10.4 Xcode 6.4 iOS SDK 8.4

Almost every iOS app has at least one and usually more UITableViews and associated UIViewController or UITableViewControllers to manage them.

The view’s ubiquity has to make implementing it very tough and I don’t envy the team that has to account for all the use cases.

Over time, UITableView, UITableViewDelegate, UITableViewDataSource, and UITableViewCell have become very large. Throw in their subclasses, UIScrollView & UIView, and developers have to remember a lot of stuff to get all the behavior they want.

Or in this case, don’t want. UITableView enforces default layout margins and separator margins that are not obvious…once you use AutoLayout.

This is what you see in your storyboard:

UITableView Storyboard

 

This is what you see in the app:

UITableView Insets

Notice the left hand margin? There is no option you can change in the Storyboard to fix it, you just get an indent/margin/inset you didn’t ask for. The storyboard doesn’t show this, so I’m not sure if this is a bug in the Storyboard editor, or in runtime in UITableView.

What’s happened is that I added a few AutoLayout constraints to the left red bar (margins t: -8, l: -8, b: -9).

When I do this, the tableView.layoutMargins appear to be their default value of 8. It looks like without AutoLayout, UITableView resets layoutMargins to UIEdgeInsetsZero, but with AutoLayout, it either wants the default margins or there’s a bug.

You have to add the following code to fix the pre-cell tableView display:

override func viewDidLoad() {

    super.viewDidLoad()

         

    self.tableView.separatorInset = UIEdgeInsetsZero

    self.tableView.layoutMargins = UIEdgeInsetsZero

But that’s not enough to get the cells looking right. Based in part on this StackOverflow post, but with my own testing, you have to implement the delegate callback willDisplayCell like this:

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    cell.layoutMargins = UIEdgeInsetsZero

}

Why do we have to set layoutMargins again in willDisplayCell? Apparently the cell’s layoutMargins get reset again right UITableView decides where to draw the separator line, so you have to make them whatever you want. Curiously, willDisplayCell has existed since iOS 2, but I don’t remember needing to use it before iOS 8.

Now we get what we want.

UITableView Insets fixed

Another Workaround That’s Incomplete...

This other StackOverflow post mentions the UIView property preservesSuperviewLayoutMargins. It defaults to NO/false, but for a UITableViewCell is set to YES/true.

You could have mostly solved the problem by explicitly setting that property to NO/false, but it would not have fixed the separator issue.

iOS 9 Beta 

I haven’t run through this on iOS 9 beta, so I don’t know what the the default indent situation is there.

Friday, July 10, 2015

Change the Global Tint in an Xcode Storyboard

TL;DR Look at the File Inspector tab (the one with the blank document) of your storyboard

Whenever I go looking for the Global Tint defined in the main storyboard in Xcode, there’s always a moment of cognitive dissonance.

My problem is that I go looking for some kind of Window object, which of course is how you’d do it programmatically (in Swift):

UIApplication.sharedApplication().delegate?.window!!.tintColor = UIColor.redColor()

The double !! after window is not a typo according to Xcode, though it sure looks weird.

Anyway, I guess nobody wants to make UIWindows things in Storyboards, so this property is stashed under the File Inspector tab!