I converted the Objective-C app Dropit from the Stanford CS193P course to Swift. The original code is located at:
http://web.stanford.edu/class/cs193p/cgi-bin/drupal/
There are multiple uses of lazy instantiation that perform additional initialization or cause some other side effect. My conversion to Swift is working, but I'm not sure that it is the proper solution or whether this kind of Objective-C coding should be avoided entirely? The first examples just set some properties as part of the lazy instantiation.
- (UIGravityBehavior *)gravity
{
if (!_gravity) {
_gravity = [[UIGravityBehavior alloc] init];
_gravity.magnitude = 0.9;
}
return _gravity;
}
- (UICollisionBehavior *)collider
{
if (!_collider) {
_collider = [[UICollisionBehavior alloc] init];
_collider.translatesReferenceBoundsIntoBoundary = YES;
}
return _collider;
}
My Swift version:
@lazy var gravity: UIGravityBehavior = {
var tempGravity = UIGravityBehavior()
tempGravity.magnitude = 0.9
return tempGravity
}()
@lazy var collider: UICollisionBehavior = {
var tempCollider = UICollisionBehavior()
tempCollider.translatesReferenceBoundsIntoBoundary = true
return tempCollider
}()
Is there a cleaner way of doing that initialization in Swift?
The ViewController of the Objective-C for the second example is a bit more involved:
@interface DropitViewController () <UIDynamicAnimatorDelegate>
@property (weak, nonatomic) IBOutlet UIView *gameView;
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) DropitBehavior *dropitBehavior;
@end
@implementation DropitViewController
static const CGSize DROP_SIZE = { 40, 40 };
- (DropitBehavior *)dropitBehavior
{
if (!_dropitBehavior) {
_dropitBehavior = [[DropitBehavior alloc] init];
[self.animator addBehavior:_dropitBehavior];
}
return _dropitBehavior;
}
- (UIDynamicAnimator *)animator
{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.gameView];
_animator.delegate = self;
}
return _animator;
}
In Swift:
class ViewController: UIViewController, UIDynamicAnimatorDelegate {
@IBOutlet var gameView: UIView
let DROP_SIZE = CGSize(width: 40.0, height: 40.0)
// lazy instantiation with closures
@lazy var animator: UIDynamicAnimator = {
var tempAnimator = UIDynamicAnimator(referenceView: self.gameView)
tempAnimator.delegate = self
return tempAnimator
}()
@lazy var dropitBehavior: DropitBehavior = {
var tempBehavior = DropitBehavior()
// side effect...
self.animator.addBehavior(tempBehavior)
return tempBehavior
}()
The way the program was designed, during the lazy initialization of dropitBehavior is when animator is created for the first time and then dropitBehavior is added to the animator. This can be verified with println, in the debugger, etc.
Note that there is a related question: Lazy instantiating a UIDynamicAnimator with referenceView - Swift
However, the suggested solution appears to be avoid @lazy, use optionals and move the work into viewDidLoad which doesn't seem that great either.
There is a lot of Objective-C code out there like this to translate, so I would like to learn the proper way to do this in Swift.