Is is ok to have weak self in viewDidLoad - swift

I am implementing this drop down menu from cocoaPod. It is pretty easy to implement and I got it to work.
https://github.com/PhamBaTho/BTNavigationDropdownMenu
However, as per instruction, I have implemented the following functions in viewDidLoad
self.navigationItem.titleView = menuView
menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in
print("Did select item at index: \(indexPath)")
if indexPath == 0 {
print("Closest")
self?.sortByDistance()
} else if indexPath == 1 {
print("Popular")
self?.sortByRatings()
} else if indexPath == 2 {
print("My Posts")
self?.myPosts()
} else {
}
I am abit concerned as Xcode is telling me to put a ? or a ! just after self which was never done in other places of my program. Could someone please advise if this is totally acceptable or is there a better way of doing it? It just seems odd force unwrapping or putting my VC as optional...?

The whole point of {[weak self] ... is that the controller may be released and you don't want this block to strongly capture it and keep it in memory if it has been released by whatever presented it. As such the reference to self may be nil.
So, you definitely don't want to use !, and you should either user ? or and if let check.

Related

Does this view controller leak in a "willSet/didSet" pair?

You have a vc (green) and it has a panel (yellow) "holder"
Say you have ten different view controllers...Prices, Sales, Stock, Trucks, Drivers, Palettes, which you are going to put in the yellow area, one at a time. It will dynamically load each VC from storyboard
instantiateViewController(withIdentifier: "PricesID") as! Prices
We will hold the current VC one in current. Here's code that will allow you to "swap between" them...
>>NOTE, THIS IS WRONG. DON'T USE THIS CODE<<
One has to do what Sulthan explains below.
var current: UIViewController? = nil {
willSet {
// recall that the property name ("current") means the "old" one in willSet
if (current != nil) {
current!.willMove(toParentViewController: nil)
current!.view.removeFromSuperview()
current!.removeFromParentViewController()
// "!! point X !!"
}
}
didSet {
// recall that the property name ("current") means the "new" one in didSet
if (current != nil) {
current!.willMove(toParentViewController: self)
holder.addSubview(current!.view)
current!.view.bindEdgesToSuperview()
current!.didMove(toParentViewController: self)
}
}
}
>>>>>>>>IMPORTANT!<<<<<<<<<
Also note, if you do something like this, it is ESSENTIAL to get rid of the yellow view controller when the green page is done. Otherwise current will retain it and the green page will never be released:
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
current = nil
super.dismiss(animated: flag, completion: completion)
}
Continuing, you'd use the current property like this:
func showPrices() {
current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices
}
func showSales() {
current = s.instantiateViewController(withIdentifier: "SalesID") as! Sales
}
But consider this, notice "point X". Normally there you'd be sure to set the view controller you are getting rid of to nil.
blah this, blah that
blah.removeFromParentViewController()
blah = nil
However I (don't think) you can really set current to nil inside the "willSet" code block. And I appreciate it's just about to be set to something (in didSet). But it seems a bit strange. What's missing? Can you even do this sort of thing in a computed property?
Final usable version.....
Using Sulthan's approach, this then works perfectly after considerable testing.
So calling like this
// change yellow area to "Prices"
current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices
// change yellow area to "Stock"
current = s.instantiateViewController(withIdentifier: "StickID") as! Stock
this works well...
var current: UIViewController? = nil { // ESSENTIAL to nil on dismiss
didSet {
guard current != oldValue else { return }
oldValue?.willMove(toParentViewController: nil)
if (current != nil) {
addChildViewController(current!)
holder.addSubview(current!.view)
current!.view.bindEdgesToSuperview()
}
oldValue?.view.removeFromSuperview()
oldValue?.removeFromParentViewController()
if (current != nil) {
current!.didMove(toParentViewController: self)
}
}
// courtesy http://stackoverflow.com/a/41900263/294884
}
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// ESSENTIAL to nil on dismiss
current = nil
super.dismiss(animated: flag, completion: completion)
}
Let's divide the question into two: (1) Is there a "leak"? and (2) Is this a good idea?
First the "leak". Short answer: no. Even if you don't set current to nil, the view controller it holds obviously doesn't "leak"; when the containing view controller goes out of existence, so does the view controller pointed to by current.
The current view controller does, however, live longer than it needs to. For that reason, this seems a silly thing to do. There is no need for a strong reference current to the child view controller, because it is, after all, your childViewControllers[0] (if you do the child view controller "dance" correctly). You are thus merely duplicating, with your property, what the childViewControllers property already does.
So that brings us to the second question: is what you are doing a good idea? No. I see where you're coming from — you'd like to encapsulate the "dance" for child view controllers. But you are doing the dance incorrectly in any case; you're thus subverting the view controller hierarchy. To encapsulate the "dance", I would say you are much better off doing the dance correctly and supplying functions that perform it, along with a computed read-only property that refers to childViewController[0] if it exists.
Here, I assume we will only ever have one child view controller at a time; I think this does much better the thing you are trying to do:
var current : UIViewController? {
if self.childViewControllers.count > 0 {
return self.childViewControllers[0]
}
return nil
}
func removeChild() {
if let vc = self.current {
vc.willMove(toParentViewController: nil)
vc.view.removeFromSuperview()
vc.removeFromParentViewController()
}
}
func createChild(_ vc:UIViewController) {
self.removeChild() // There Can Be Only One
self.addChildViewController(vc) // *
// ... get vc's view into the interface ...
vc.didMove(toParentViewController: self)
}
I don't think that using didSet is actually wrong. However, the biggest problem is that you are trying to split the code between willSet and didSet because that's not needed at all. You can always use oldValue in didSet:
var current: UIViewController? = nil {
didSet {
guard current != oldValue else {
return
}
oldValue?.willMove(toParentViewController: nil)
if let current = current {
self.addChildViewController(current)
}
//... add current.view to the view hierarchy here...
oldValue?.view.removeFromSuperview()
oldValue?.removeFromParentViewController()
current?.didMove(toParentViewController: self)
}
}
By the way, the order in which the functions are called is important. Therefore I don't advise to split the functionality into remove and add. Otherwise the order of viewDidDisappear and viewDidAppear for both controllers can be surprising.

Weak self in closures and consequences example

I have done abit of research on stackoverflow and apple's documentation about ARC and Weak/Unowned self (Shall we always use [unowned self] inside closure in Swift). I get the basic idea about strong reference cycle and how it is not good as they cause memory leaks. However, I am trying to get my head around when to use Weak/Unowned self in closures. Rather then going into the "theory", I think it would really really help if someone could kindly explain them in terms of the bottom three cases that I have. My questions is
Is it OK to put weak self in all of them (I think for case two there is no need because I saw somewhere that UIView is not associated with self?. However, what if I put weak self there, is there anything that can cause me headache?
Say if the answer is No, you cant put weak self everywhere in all three cases, what would happen if I do (example response would be much appreciated...For example, the program will crash when this VC ....
This is how I am planning to use weakSelf
Outside the closure, I put weak var weakSelf = self
Then replace all self in closure with weakSelf?
Is that OK to do?
Case 1:
FIRAuth.auth()?.signInWithCredential(credential, completion: { (user: FIRUser?, error: NSError?) in
self.activityIndicatorEnd()
self.performSegueWithIdentifier(SEGUE_DISCOVER_VC, sender: self)
})
Case 2:
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.1, animations: {
self.messageLbl.alpha = 0.5
})
Case 3:
//checkUserLoggedIn sends a request to firebase and waits for a response to see if the user is still authorised
checkUserLoggedIn { (success) in
if success == false {
// We should go back to login VC automatically
} else {
self.discoverTableView.delegate = self
self.discoverTableView.dataSource = self
// Create dropdown menu
let menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, title: self.dropDownItems.first!, items: self.dropDownItems)
menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in
if indexPath == 0 {
self?.mode = .Closest
self?.sortByDistance()
} else if indexPath == 1 {
self?.mode = .Popular
self?.sortByPopularity()
} else if indexPath == 2 {
self?.mode = .MyPosts
self?.loadMyPosts()
} else {
print("Shouldnt get here saoihasiof")
}
}
// Xib
let nib = UINib(nibName: "TableSectionHeader", bundle: nil)
self.xibRef = nib.instantiateWithOwner(self, options: nil)[0] as? TableSectionHeader
self.discoverTableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "TableSectionHeader")
// Set location Manager data
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
// Check location service status
if self.locationAuthStatus == CLAuthorizationStatus.AuthorizedWhenInUse {
// Already authorised
self.displayMessage.hidden = false
} else if self.locationAuthStatus == CLAuthorizationStatus.NotDetermined {
// Have not asked for location service before
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("LocationVC") as! LocationVC
vc.locationVCDelegate = self
self.presentViewController(vc, animated: true, completion: nil)
} else {
let alertController = UIAlertController(title: "Enable Location", message: "location is required to load nearby posts", preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: nil)
let settingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (action: UIAlertAction) in
let settingsUrl = NSURL(string: UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
})
alertController.addAction(settingsAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
self.displayMessage.hidden = false
self.displayMessage.text = "Could not determine your location to find nearby posts. Please enable location Service from settings"
}
// Styling
self.refreshBtn.tintColor = COLOR_NAVIGATION_BUTTONS
self.discoverTableView.backgroundColor = COLOR_DISCOVERVC_TABLEVIEW_BACKGROUND
// Allow navigation bar to hide when scrolling down
self.hidingNavBarManager = HidingNavigationBarManager(viewController: self, scrollView: self.discoverTableView)
// Allow location to start updating as soon as we have permission
self.locationManager.startUpdatingLocation()
}
}
--Update--
Most of my code looks like case 3 where everything is wrapped inside a closure that either check if there is internet connectivity before any of the action is taken place. So I might have weak self everywhere??
--Update 2--
Case 4:
// The haveInternetConnectivity function checks to see if we can reach google within 20 seconds and return true if we can
haveInternetConnectivity { (success) in
if success == false {
self.dismissViewControllerAnimated()
} else {
self.label.text = "You are logged in"
self.performSegueWithIdentifier("GoToNextVC")
}
}
Question about case 4.
Am I correct to say that even though this closure does not have weak/unowned self, it will never create strong reference (and memory leak) because even if the VC is dismissed before the completion block is executed, Xcode will try to run the code inside the completion block when we have confirmed internet status and will just do nothing (No crash) because self doesn't exist anymore. And once the code reached the last line inside the closure, the strong reference to self would be destroyed hence deallocate the VC?
So putting [weak Self] in that case would just mean that xcode would ignore those lines (as oppose to try and run it and nothing happens) which would mean a better practice but no issues on my hand either way
The question should not be "can I use weak reference," but rather "should I use weak reference." You use weak references to avoid strong reference cycles or to keep a closure from hanging on to something after it may have been disposed. But don't just add weak references because you can.
In case 1, you probably do want to use [weak self]. Why? Because if the view controller was dismissed while the authorization was underway, do you really want to keep a reference to a view controller that was dismissed? Probably not in this case.
In case 2, you theoretically could use [weak self] in the animation block, but why would you? There's no reason to. The weak reference is something you do with completion handlers and/or closure variables, but for an animation block it offers no utility, so I wouldn't do it there. To use weak here suggests a misunderstanding of the memory semantics involved.
In case 3, you have two separate issues.
In the didSelectItemAtIndexHandler, that probably should use [unowned self] because the object's own closure is referring to itself.
It may be a moot issue, as I don't see you actually using that BTNavigationDropdownMenu (perhaps that initializer is adding itself to the navigation controller, but that's not a well designed initializer if so, IMHO).
But as a general concept, when an object has a handler closure that can only be called when the object is still around, but shouldn't, itself, cause the object to be retained, you'd use [unowned self].
In the broader checkUserLoggedIn closure, the question is whether that's a completion handler. If so, you probably should use [weak self], because this could be initiated and be running by the time self is dismissed, and you don't want to have checkUserLoggedIn keep a reference to a view controller that was dismissed. But you wouldn't want to use [unowned self] because that would leave you with dangling pointers if it had been released by the time the closure runs.
As an aside, you contemplate:
weak var weakSelf = self
That is a bit unswifty. You would use the [weak self] pattern at the start of the checkUserLoggedIn closure. If you have an example where you're tempted to use weak var weakSelf = ..., you should edit your question, including an example of where you want to use that pattern. But this is not one of those cases.

I find myself using "if self != nil" a lot because of [weak self] ...is this normal?

Example (in my view controller):
RESTApi.fetchUser() { [weak self] Void in
if self != nil { //the view controller is still here.
self!.items.append(stuff)
self!.whatever
}
}
I notice that I'm using if self != nil immediately, followed by self! everywhere. Is there a better way? I feel like this is against the Swift way.
Note: I use [weak self] because my ViewController could be nil (if the user goes back before the REST Api downloads the data).
Why don't you use if let?
if let unwrappedSelf = self {
unwrappedSelf.items.append(stuff)
...
}
You could also use guard let:
guard let unwrappedSelf = self else { return }
unwrappedSelf.items.append(stuff)
...
Idle you have to use guard which is more idle then the if
guard let strongSelf = self else { return }
strongSelf.items.append(stuff)
If you are just calling methods on self when the closure is invoked, then you don't even need to use if let, guard let, or if self != nil. The below code also works just fine to call the methods if self is not nil, and to do nothing if it is:
RESTApi.fetchUser() { [weak self] in
self?.items.append(stuff)
self?.whatever()
}
This conditional unwrapping is usable in all cases except those where you need to pass self or a property of self as an argument to a non-optional function parameter, or assign self or a property of self to a variable with a non-optional type.
As a broader note, there isn't necessarily a need to make self weak in this scenario anyway. That's usually only needed if self is retaining the closure that references self inside as an ivar (circular reference). In your case, it's RestApi that's holding a reference to the closure, and the view controller that self is referring to would be retained by the closure until the closure returned. At that point, if the view controller had already been dismissed by the user and the closure's reference was the last reference to it, it would then be deallocated after the closure executed (no circular reference).
So making self weak in this example only applies if you specifically want the scenario of dismissing the view controller to deallocate the instance and the closure to then fail to run anything at all.
Simply do this:
guard let `self` = self else {
return
}
self.items.append(stuff)

Swift Optional Binding with a negative result

How can I do an optional binding in Swift and check for a negative result? Say for example I have an optional view controller that I'd like to lazy load. When it's time to use it, I'd like to check if it's nil, and initialize it if it hasn't been done yet.
I can do something like this:
if let vc = viewController? {
// do something with it
} else {
// initialize it
// do something with it
}
But this is kludgey and inefficient, and requires me to put the "do something with it" code in there twice or bury it in a closure. The obvious way to improve on this from objC experience would be something like this:
if !(let vc = viewController?) {
// initialize it
}
if let vc = viewController? {
// do something with it
}
But this nets you a "Pattern variable binding cannot appear in an expression" error, which is telling me not to put the binding inside the parenthesis and try to evaluate it as an expression, which of course is exactly what I'm trying to do...
Or another way to write that out that actually works is:
if let vc = viewController? {
} else {
// initialize it
}
if let vc = viewController? {
// do something with it
}
But this is... silly... for lack of a better word. There must be a better way!
How can I do an optional binding and check for a negative result as the default? Surely this is a common use case?
you can implicitly cast Optional to boolean value
if !viewController {
viewController = // something
}
let vc = viewController! // you know it must be non-nil
vc.doSomething()
Update: In Xcode6-beta5, Optional no longer conform to LogicValue/BooleanType, you have to check it with nil using == or !=
if viewController == nil {
viewController = // something
}
Would this work for you?
if viewController == nil {
// initialize it
}
// do something with it
One way might be to create a defer statement that handles the actions. We can ensure those actions occur after the creation of our object by checking for nil. If we run into nil, instantiate the object and return. Before returning our deference will occur within the scope of some function.
func recreateAndUse() {
defer {
viewController?.view.addSubview(UIView())
viewController!.beginAppearanceTransition(true, animated: true)
}
guard viewController != nil else {
viewController = UIViewController()
return
}
}

In a macOS Cocoa Application that has two identical NSOutlineViews side by side, is there a way to expand / collapse items in sync between the two?

I am developing a macOS Cocoa Application in Swift 5.1 . In the main window, I have two identical NSOutlineViews that have the same contents exactly. I would like to enable a sync mode, where if items are expanded/collapsed in one of the two NSOutlineViews, the corresponding item is expanded/collapsed in the other simultaneously. I tried to do this by implementing shouldExpandItem and shouldCollapseItem in the delegate. The delegate is the same object for both NSOutlineViews, and I have outlets that reference the two NSOutlineViews for distinguishing the two. The problem is, if I call expandItem programmatically in shouldExpandItem, the method is again called for the other NSOutlineView, leading to an infinite loop and a stack overflow. I have found a dirty solution that works, by momentarily setting the delegate of the relevant NSOutlineView to nil, expand/collapse the item, then set the delegate back. The code is the following:
func outlineView(_ outlineView: NSOutlineView, shouldExpandItem item: Any) -> Bool {
let filePath = (item as! FileSystemObject).fullPath
let trueItem = item as! FileSystemObject
trueItem.children = Array()
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: filePath!)
for (_, item) in contents.enumerated() {
let entry = FileSystemObject.init()
entry.fullPath = URL.init(fileURLWithPath: filePath!).appendingPathComponent(item).path
if entry.exists {
trueItem.children.append(entry)
}
}
} catch {
}
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.expandItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.expandItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
func outlineView(_ outlineView: NSOutlineView, shouldCollapseItem item: Any) -> Bool {
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.collapseItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.collapseItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
It seems to work, but I am afraid that something could go wrong with this approach. Is setting the delegate momentarily to nil a possible solution, or should I be aware of any caveats ? is there another pattern that you can suggest to achieve this ? Thanks
EDIT: According to below comments and answers
I found the simplest solution thanks to the answers / comments I received.
Instead of implementing the sync logic in the outlineViewShouldExpand/Collapse methods, it is possible to implement outlineViewDidExpand and outlineViewDidCollapse and place the sync logic there. the latter methods are not called when expanding/collapsing items programmatically, so there is no risk of infinite loop or stack overflow.
The code is as follows:
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}
func outlineViewItemDidCollapse(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().collapseItem(item)
} else {
self.leftOutlineView.animator().collapseItem(item)
}
}
Furthermore, now, I cannot understand why, the expansion / collapse of items is animated, which did not work with my original approach.
I hope this can be helpful for somebody, it was very helpful to me. Thanks a lot.
My app does something similar. I need to keep many views in sync, across windows. One of those views is an NSOutlineView. I have run into a few quirks with NSOutlineView, but I don't believe they are related to syncing.
I do take a different approach, though. Instead of manipulating the delegate, I just have a flag that suppresses certain actions. In my case, a flag makes more sense, since lots of other views are being affected. But, the effect is very similar to what you are doing.
I think the only risk would be missing a delegate call during your synchronization. Assuming your logic does not depend on this, I believe your approach will work fine.
outlineView(_:shouldExpandItem:) is called before the item is expanded and calling expandItem() back and forth causes an infite loop. outlineViewItemDidExpand(_:) is called after the item is expanded and NSOutlineView.expandItem(_:) does nothing when the item is already expanded (documented behaviour). When expanding the left outline view, expandItem() of the right outline view does call outlineViewItemDidExpand(_:) but the expandItem() of the left outline view doensn't call outlineViewItemDidExpand(_:) again.
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let item = notification.userInfo!["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}