Description
Previous ID | SR-7576 |
Radar | None |
Original Reporter | stonehouse (JIRA User) |
Type | Bug |
Environment
Developer machine:
Xcode 9.3 9E145
Swift 4.1 'swiftlang-902.0.48'
Test device:
iPod Touch
iOS 9.3.5 (13G36)
Additional Detail from JIRA
Votes | 0 |
Component/s | Foundation |
Labels | Bug, 4.1Regression |
Assignee | None |
Priority | Medium |
md5: 3fb56679807fff4408f2adb44e525772
Issue Description:
Casting Float to NSNumber results in nil in cases where it cannot be cleanly represented.
This seems to be related to SR-5179, and it seems from looking at SR-5228 that this may be an intentional change to avoid lossy conversion. However, I wanted to point out that this behaviour leads to crashes on iOS 9 when using CABasicAnimation and animating CGFloat properties.
I recently discovered that our app started crashing on iOS 9 after the upgrade to Xcode 9.3/Swift 4.1. I've determined the cause is this issue happening somewhere in CABasicAnimation. If I don't specify `fromValue` then it works fine. This crash does not happen in iOS 10 or later.
The problem with this crash is that it results inside CoreAnimation code, so the developer needs to either disable the animation on iOS 9 or sanitise Float values before passing them in.
If I should file a radar let me know, but it seems like the problem is with Swift, because the issue doesn't exist in iOS 10 or later.
The following exception is thrown on an iOS 9 device when attempting to run the animation
Fatal error: Unable to bridge NSNumber to CGFloat: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-902.0.48/src/swift/stdlib/public/SDK/Foundation/NSNumber.swift, line 606
Here is a simplified example of the kind of animation that might crash on iOS 9
import UIKit
class SampleLayer: CALayer {
var brightness: CGFloat = 0
@objc dynamic var locations: [CGFloat] = [0.0, 1.0] {
didSet {
setNeedsDisplay()
}
}
func setBrightnessAnimated(_ brightness: CGFloat, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)) {
let oldBrightness = self.brightness
let newBrightness = brightness
// On iOS 9 with Swift 4.1 this is crashing due to a bug where CGFloat cannot be cast to NSNumber in some conditions
if duration > 0 {
let oldLocations = presentation()?.locations ?? [0, oldBrightness]
let animation = CABasicAnimation(keyPath: "locations")
animation.duration = duration
animation.fromValue = oldLocations
animation.timingFunction = timingFunction
removeAnimation(forKey: "brightness")
add(animation, forKey: "brightness")
}
self.brightness = newBrightness
}
}
let testLayer = SampleLayer()
testLayer.setBrightnessAnimated(1, duration: 0.1)