Implicit Unwrapping and Initializers - swift

I have a subclass of UIView, which instantiates several sublayers. For convenience, I want a direct reference to one in particular, and because it has the same lifetime as the view, it seems reasonable to make it a let constant. The problem is that I want the sublayer to have a similarly immutable reference back to its owner/parent view, so I try to pass this to the layer’s initializer:
class MyView: UIView{
let myLayer: MyLayer!
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.myLayer = MyLayer(owner: self) // <- The problem line.
self.layer.addSublayer(self.myLayer)
}
}
class MyLayer: CAShapeLayer {
unowned let owner: MyView
init(owner: MyView) {
self.owner = owner
super.init ()
}
}
If I put the call to the layer initializer before the call to MyView’s super.init, I get the error “'self' used before 'super.init' call.” I take this to mean it’s permissible (preferred I always thought?) to use self in a property initialization, but it doesn’t yet have a value that can be passed as a parameter?
But if I place the line after super.init as shown, I get two errors: “Property 'self.myLayer' not initialized at super.init call” and “Immutable value 'self.myLayer' may only be initialized once.” This puzzles me, as I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits. (It also sounds a trifle contradictory: something wasn’t initialized, but after an attempted initialization, it wound up initialized twice, nevertheless?)
I know I can get around the problem by dropping my obsession with immutables, but is there a way to do this properly? I’m all in favor of Swift’s safety checking, but is there an actual danger here? I’d think the implicit unwrap would signal the compiler that the programmer is aware of a possible issue and is looking out for consequences.

I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits.
Unfortunately, it counts. This behavior is fixed into current style after this update:
Xcode Release Notes > Xcode 6.3 > Swift Language Changes
You may have found some workarounds, but I think this would be very near to what you want:
class MyView: UIView {
private(set) var myLayer: MyLayer!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.myLayer = MyLayer(owner: self)
self.layer.addSublayer(self.myLayer) }
}

You could use a lazy var for this. It’s basically a let, and only gets initialized once, at the first use site.
class MyView: UIView{
lazy var myLayer: MyLayer = {
return MyLayer(owner: self)
}()
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.layer.addSublayer(self.myLayer)
}
}

Related

Any way to opt out of autoresizing permanently?

I'm writing nib-less views in which I use autolayout for all my layout logic. I find myself having to turn off autoresizing with every view I instantiate. My code is littered with a lot of these:
view.translatesAutoresizingMaskIntoConstraints
Ideally I'd like to just
extension UIView/NSView {
override var translatesAutoresizingMaskIntoConstraints: Bool = false
}
and get it over with once and for all, but extensions can't override stored properties.
Is there some other simple way to switch off autoresizing for good?
Well just a suggestion since its annoying to always set that to false, just setup a function with all the shared setups for the UIView and call it every time,
its saves time and its kinda less annoying than trying and setting the values each time,
extension UIView {
func notTranslated() {
self.translatesAutoresizingMaskIntoConstraints = false
//Add any additional code.
}
}
//Usage
let view = UIView()
view.notTranslated()
You can't override this constraints properties because the UIView maybe declared in the IB
translatesAutoresizingMaskIntoConstraints according to apple.
By default, the property is set to true for any view you programmatically create. If you add views in Interface Builder, the system automatically sets this property to false.
imagine if you could override that from an extension that would lead to some conflicts if there was other UIView's that's have the opposite value True || false, so in my opinion:
Apple did this to prevent any conflicts with the views constrains, therefore if you don't like to write it every time just wrap it up in a function.
Please if anyone have additional information, don't hesitate to contribute.
UPDATE: I found this cool answer that could also work, check out the code below.
class MyNibless: UIView {
//-----------------------------------------------------------------------------------------------------
//Constructors, Initializers, and UIView lifecycle
//-----------------------------------------------------------------------------------------------------
override init(frame: CGRect) {
super.init(frame: frame)
didLoad()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
didLoad()
}
convenience init() {
self.init(frame: CGRect.zero)
}
func didLoad() {
//Place your initialization code here
//I actually create & place constraints in here, instead of in
//updateConstraints
}
override func layoutSubviews() {
super.layoutSubviews()
//Custom manually positioning layout goes here (auto-layout pass has already run first pass)
}
override func updateConstraints() {
super.updateConstraints()
//Disable this if you are adding constraints manually
//or you're going to have a 'bad time'
//self.translatesAutoresizingMaskIntoConstraints = false
translatesAutoresizingMaskIntoConstraints = false
//Add custom constraint code here
}
}
var nibless: UIView = MyNibless()
//Usage
nibless.updateConstraints()
print(nibless.translatesAutoresizingMaskIntoConstraints) //false
So simply just create MyNibless instance as UIView and this also open big door to customizations too

Random KVO block crashes when allocating mapView, happens only when open/close screen several times

App is crashing on a map screen when it open and close several times. (Mostly on 6th attempt)
Class that inherits from GMSMapView
class AirportMapView: GMSMapView , AirportMapViewProtocol{
weak var airportMapViewModuleDelegate: AirportMapViewModuleProtocol?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(from frame: CGRect, with cameraPosition: GMSCameraPosition) {
self.init(frame: frame)
self.camera = cameraPosition
}
func setCluster() {
let algorithm = CustomGoogleMapsClusteringAlgorithm.init();
mapIconGenerator = VJAirportIconGrayClusterGenerator.init()
let renderer = VJGoogleMapsClusterRenderer(mapView: self,
clusterIconGenerator: mapIconGenerator!)
clusterManager = GMUClusterManager.init(map: self, algorithm: algorithm, renderer: renderer)
clusterManager?.setDelegate(self, mapDelegate: self)
}
}
In ViewController viewDidLoad I am calling init method of mapView
self.mapView = [[AirportMapView alloc] initFrom:frame with:camera];
self.mapView.myLocationEnabled = YES;
self.mapView.settings.compassButton = YES;
self.mapView.settings.zoomGestures = YES;
self.mapView.airportMapViewModuleDelegate = self;
Backtrace of the crash and console logs attached
Observation:
GMUClusterManager initWithMap method if I remove addObserver code app is not crashing
After inspecting the Google-Maps-iOS-Utils source code, it turns out that the GMSClusterManager class did not maintain a strong reference to the GMSMapView that it observed via KVO. This could potentially cause crashes if the GMSMapView object ever deallocated before the removeObserver:forKeyPath: method could be called from dealloc.
Per Apple documentation, strong references should be maintained for objects observed via KVO:
Note: The key-value observing addObserver:forKeyPath:options:context:
method does not maintain strong references to the observing object,
the observed objects, or the context. You should ensure that you
maintain strong references to the observing, and observed, objects,
and the context as necessary.
See this pull request (which is now merged) for more details.
I had a similar problem then GMSMapView inside my custom UIView subclass
var mapView: GMSMapView!
var clusterManager: GMUClusterManager!
is deallocated before dealloc in GMUClusterManager and this causes a crash because mapView is going to nil before removeObserver is called
So I added
deinit {
clusterManager = nil
}
to my UIView subclass
Check this thread
https://github.com/googlemaps/google-maps-ios-utils/issues/181#issuecomment-385531638
Almost the same crash here ... it disappears when i changed clusterManager from strong property to stack object

Changing the initial value of a var in another module

I am using this module to display an image:
https://github.com/huynguyencong/ImageScrollView/blob/master/Sources/ImageScrollView.swift
I have added it to my project using CocoaPods and I want to change the value of the var "maxScaleFromMinScale". I just cant figure out how.
First I tried to just override the var which is not possible.
I then tried to override the intializers by just copying and overriding the same ones that are overrided by the creator of the module.
override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
That does not work, it gives me an error on the top initializer saying that I am not overriding the designated initializer. It feels like I cannot override it since it is not public like the second one. Correct?
Last thing I tried was to make an extension:
extension ImageScrollView {
public func setMaxScale(scale: CGFloat) {
maxScaleFromMinScale = scale
}
}
That does not work either, maxScaleFromMinScale is an unresolved identifier. This also seems to be because it is an internal var and I do not have access to it (another module). Does this mean that my ONLY option is to copy the whole file and modify the source. Was hoping for a minimal and elegant solution.
If this is the case, why can you override the built in UIKit elements but not this one that I downloaded using CocoaPods.
Yes, you cannot override the variable/function because it's internal. That's the point of access modifiers.
Certain UIKit functions are public so you can override them.
Go to the pod file and unlock the file (It prompts to do that on editing), and change
var maxScaleFromMinScale: CGFloat = 3.0
to
public var maxScaleFromMinScale: CGFloat = 3.0

Swift View and Controller shared variable initialization troubles

I'm porting some code from Objc to Swift. And struggling with Swift's initialization life cycle. From a simplified point of view, there are 4 players I'm trying to bring into play here:
An object called a Program. This is my main top level model object at this point. The remaining 3 players all want access to an instance of him.
A ProgramEditController, painted in my main Main.storyboard. He's responsible for instantiating an initial Program, which cannot be done directly as a property initializer.
A top level custom UIView subclass, called ProgramTimelineView. Painted in the Main.storyboard, manages a variety of specialized subviews. Linked to a property of my ViewController. Has properties for the it's subviews as well. It wants access to the Program, so it can do layout and pass it on to subviews.
A particular subview of ProgramTimelineView called ProgramGridView. These are not painted in the XCode canvas tool, but directly instantiated by the containing ProgramTimelineView. It wants access to the Program. Uses it to do his custom drawRect.
Here's the relevant code for my Controller:
class ProgramEditController: UIViewController {
// MARK: - Variables
#IBOutlet var timelineView:ProgramTimelinesView!
var site = Site()
var program:Program!
// MARK: - Initialize
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// set up the new program
self.program = self.site.newProgram()
// get it into our top view before it starts drawing
self.timelineView.program = self.program
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) // Why does Swift make me have a redundant thing here?
}
}
And for ProgramTimelinesView:
class ProgramTimelinesView: UIView {
// MARK: - Variables
var gridView = ProgramGridView()
var program:Program! {
didSet {
self.gridView.program = self.program
}
}
// MARK: - Initialization
func addGridView() {
self.gridView.alpha = 0.0
self.gridView.opaque = false
self.addSubview(self.gridView)
}
func commonInit() {
self.addGridView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
}
And finally for the ProgramGridView:
class ProgramGridView: TimeAxisView {
// MARK: - Variables
var program:Program!
override func drawRect(rect: CGRect) {
// access self.program in here
}
}
What I thought would happen:
ProgramEditController.init(nibName...) would fire first.
The super call would cause my ProgramTimelineView.init(coder) to fire.
ProgramTimelineView instance would first call the gridView initializer setting it to a new ProgramGridView view
The remainder of ProgramTimelineView.init(coder) would run, which would add the gridView into the view tree.
Control would return to the ProgramEditController.init(nibName) initializers. The controller's program property would be populated.
The bound timelineView would have its program property set.
ProgramTimelineView would in turn set the program property of the gridView.
What seems to happen though, between steps 4 and 5, is that a drawRect() happens. That causes a seg fault, because the gridView's program has not been set yet! But why is it issuing drawRect()'s at this point? I thought that wouldn't happen before all of the initializers had fired. But clearly some side affect is occurring. What is the correct pattern/idiom to employ to avoid this? I really would rather not turn all of the program! into program? and then put let/guards every where.
There turned out to be a faulty assumption in my original premises (usually the case).
UIViewController.init(nib...) is NOT called when creating from interface builder assets. But local variables linked in interface builder (implicitly wrapped) are not set/realized at the point of init(coder) either. The correct approach required two adjustments:
Move the setup of the program var to the init(coder) initializers:
e.g.
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.program = self.site.newProgram()
}
Forward that to the view in a viewDidLoad() override.
e.g.
override func viewDidLoad() {
super.viewDidLoad()
self.timelineView.program = self.program
}

Why does the author use forced unwrapping in Example 2 but not in example one?

I know the concept of optionals and forced unwrapping but just to quote an example from iOS 8 Swift Programming Cookbook, I don't understand why var imageView: UIImageView is used
in example 1 but forced unwrapping var imageView: UIImageView! in example 2. Hopefully, someone tells me what I am missing here so I know what to read up on.
Example 1:
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView
required init(coder aDecoder: NSCoder) {
imageView = UIImageView(image: image)
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.center = view.center
view.addSubview(imageView)
}
}
Example 2:
import UIKit
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(frame: view.bounds)
imageView.image = image
imageView.center = view.center
view.addSubview(imageView)
}
}
As others have noted, the purpose of these two examples is to illustrate the difference between a non-optional variable that is set during the initialization process and an optional (in this case, an implicitly unwrapped one) which you may instantiate at a later point.
It's worth noting that while this example has pedagogic value, from a practical perspective, when dealing with view controllers, you would use the latter pattern with optional variable set in viewDidLoad (or for programmatically created views, you might use a third pattern, the loadView method), not the first pattern with the non-optional instantiated in init.
I just wanted to make sure you didn't walk away from this example concluding that in the case of view controllers that you should freely use either of these two patterns. There are reasons, unrelated to the Swift language but rather related to details of the life cycle of view controllers, views, etc., that you would favor the optional variable set in viewDidload when dealing with view controllers.
That notwithstanding, it is important to understand in what cases one would use an optional in Swift and in what cases one would not (the key point being that you must use optional if the variable may not be set during the initialization process of the object), so from that perspective, this may be an illuminating example. But just remember, that in the specific case of view controllers, one would generally adopt the viewDidLoad pattern.
In example 1, the variable value is set in init. In example 2, the variable is initialized later and after initialization (until viewDidLoad is called) its value is nil.
Since in example 1 the variable is never nil, it can be a non-optional. However, in example 2 the variable is nil for a long time, so it must be an optional variable.
In swift all non optional class and struct properties must be initialized in a constructor, and there's no way to avoid that (unless it's initialized inline, along with its declaration).
In some cases a non optional property cannot be initialized at instantiation time, because it depends from other properties of the same class, or just it must be initialized at a later stage in the instance lifetime. As such, these properties must be declared as optional.
A workaround consists of declaring all there properties as implicitly unwrapped - internally they are optionals, but they are accessed as non optionals (i.e. without the ? operator).
That explains the differences between example 1 and 2: in the former, the property is initialized in the initializer, so it is declared as non optional. In the latter case instead it is initialized in the viewDidLoad method, and so it's been declared as implicitly unwrapped.
This pattern is widely used in view and view controller outlets - you will notice that when you create an outlet in IB, the corresponding property is an implicitly unwrapped - that's because the variable is assigned after initialization.
Example 1's only initializer is the required init(coder aDecoder: NSCoder), which always assignes a value to imageView. Therefore imageView will always have a value after initilization has taken place.
Example 2 doesn't have or need an initilizer, since both stored properties are given an initial value in their declaration:
let image = UIImage(named: "Safari") // has an initial value of whatever UIImage(named: "Safari") returns
var imageView: UIImageView! // has an initial value of nil
You can therefore construct a ViewController, by calling the initializer which swift adds automatically: ViewController()
This will result in imageView having a value of nil.