how can i auto update value of bool variable - ios

i have one bool variable isValidSession i want to observer this variable in whole application whenever it becomes false it will automatically call method and renew the session.
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let newValue = change?[NSKeyValueChangeNewKey] {
}
}

Try this:
var isValidSession: Bool = true {
didSet {
if isValidSession == false {
//do something (renew session here)
}
}
}

Related

object communication when object is not kvc compliant in Swift

I want to create a custom view that display it when an action occurs (like changed property) on another object (subclass of UIControl)
My approach was it
create a protocol whose UIControl objects can conform
create my custom view in which I can observe my delegate
Unfortunately, it's not work, worse, it's crash because compiler say "its not kvc-compliant"
Bellow, my code :
protocol CustomDelegate: class where Self : UIControl {
func respondeToControl() -> Bool
}
class CustomView: UIView {
weak var delegate: CustomDelegate? {
didSet{
observeIfControlIsPressed()
}
}
func observeIfControlIsPressed(){
if delegate != nil {
let keypath = delegate! as! UIControl
keypath.addObserver(keypath,
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
}
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("background changed")
}
}
My question is, how I can re-design my solution to make it
work ?
You addObserver is wrong, the "observer" should be self no "keypath"
Try this:
keypath.addObserver(self, forKeyPath: "backgroundColor", options: [.new, .old], context: nil)
You did not read your error correctly.. For example:
protocol CustomDelegate : class where Self : UIControl {
func respondeToControl() -> Bool
}
class CustomView: UIView {
weak var delegate: CustomDelegate? {
didSet{
observeIfControlIsPressed()
}
}
func observeIfControlIsPressed(){
if delegate != nil {
let keypath = delegate! as! UIControl
keypath.addObserver(keypath,
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
}
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("background changed")
}
}
class Foo : UIControl, CustomDelegate {
func respondeToControl() -> Bool {
return true
}
}
class ViewController: UIViewController {
let testBlock = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let view = CustomView()
let button = Foo()
view.delegate = button
button.backgroundColor = UIColor.red
}
}
Throws an exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<Meh.Foo: 0x7fc977c1a0c0; baseClass = UIControl; frame = (0 0; 0 0); layer = <CALayer: 0x608000226480>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: backgroundColor
It means Foo is observing foo but did not implement the observeValueForKeyPath function..
Now why is that? Well.. it's because you did:
keypath.addObserver(keypath, //keypath is observing itself..
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
where keypath is your delegate.. and is adding observer on itself..
It should be:
keypath.addObserver(self, //self is observing keypath..
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
The first parameter of addObserver needs to be the class that wants to do the observing.
That change will cause it to print: background changed.. Don't forget to remove the observer later.

NSBundleResourceRequest No Progress Update

When I try to observe the progress of a NSBundleResourceRequest, observeValue(forKeyPath: object: change: context:) is not called for the .new observing option. Therefore, the NSProgressIndicator isn't updated. Here is the setup and code:
Setup:
Xcode 8.3.1
Deployment Target iOS 10.3
Device: iPad 4
Resource tags (368 KB) are located located in Download Only On Demand and consist of thumbnails displayed in a UICollectionView. Thumbnail images are located in MyCollection.xcassets. All IBOutlets are connected.
Images are correctly displayed in the collection view, but progress bar remains at zero.
Code:
final class MyCollectionVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var myCollectionVCResourceRequest: NSBundleResourceRequest!
var myCollectionVCResourceRequestLoaded = false
static var myCollectionProgressObservingContext = UUID().uuidString
private let designThumbnailTags: Set<String> = ["Resource1", "Resource2", "Resource3"]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadOnDemandResources()
}
func loadOnDemandResources() {
myCollectionVCResourceRequest = NSBundleResourceRequest(tags: designThumbnailTags)
myCollectionVCResourceRequest.progress.addObserver(self, forKeyPath: "fractionCompleted", options: [.new, .initial], context: &MyCollectionVC.myCollectionProgressObservingContext)
myCollectionVCResourceRequest.beginAccessingResources(completionHandler: { (error) in
print("Complete: \(self.myCollectionVCResourceRequest.progress.fractionCompleted)") // Prints Complete: 0.0
self.myCollectionVCResourceRequest.progress.removeObserver(self, forKeyPath: "fractionCompleted", context: &MyCollectionVC.myCollectionProgressObservingContext)
OperationQueue.main.addOperation({
guard error == nil else { self.handleOnDemandResourceError(error! as NSError); return }
self.myCollectionVCResourceRequestLoaded = true
self.updateViewsForOnDemandResourceAvailability()
self.fetchDesignThumbnailsWithOnDemandResourceTags(self.myCollectionVCResourceRequest) // Correctly creates Core Data Instances
})
})
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &MyCollectionVC.myCollectionProgressObservingContext {
OperationQueue.main.addOperation({
let progressObject = object as! Progress
self.progressView.progress = Float(progressObject.fractionCompleted)
print(Float(progressObject.fractionCompleted)) // Prints 0.0 as a result of including the .initial option
self.progressDetailLabel.text = progressObject.localizedDescription
})
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}

rectForRowAtIndexPath crashing with defined indexPath in UITableView

The following code is crashing at
currentCellRect = tableView.rectForRowAtIndexPath(indexPaths[0])
But only sometimes.
public func showCellScrollCount(animated:Bool) {
self.tableView.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
self.tableView.addObserver(self, forKeyPath: "dragging", options: NSKeyValueObservingOptions.New, context: nil)
}
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
switch (keyPath, object) {
case (.Some("contentOffset"), _):
self.updateScrollPosition()
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
func updateScrollPosition() {
let indexPaths = tableView.indexPathsForVisibleRows
var currentCellRect:CGRect?
if let indexPaths = indexPaths {
if indexPaths.count > 0 {
currentCellRect = tableView.rectForRowAtIndexPath(indexPaths[0])
scrollCountView.currentScrollCountNum = indexPaths[0].row
}
}
}
It crashes with "BAD_ACCESS". Does anyone have any idea why?
...
EDIT: Is it possible it's happening because I'm calling tableView.reloadData() right before I add observers, and the call isn't finished yet?
Updated
Your code works fine since I added it to my tableView controller and called showCellScrollCount func once. Where you call showCellScrollCount, do you call it multiple times? It can be problem if so.
Update 3
here is the problem:
public var totalScrollCountNum = 0 {
didSet {
scrollCountView.totalScrollCountNum = totalScrollCountNum
showCellScrollCount(false)
}
}
move showCellScrollCount(false) to initializers

How to know when NSHashTable changed count?

I've tried with KVO but looks like did anything wrong.
My code below
class A : NSObject {
var s: String?
init(s: String) {
super.init()
self.s = s
print("\(self.s) init")
}
deinit {
print("\(self.s) deinit")
}
}
class B : NSObject {
weak var a:A? {
willSet {
print("\(self.a?.s) willSet a \(newValue?.s)")
}
didSet {
print("\(self.a?.s) didSet a \(oldValue?.s)")
}
}
dynamic var hashTable: NSHashTable = NSHashTable.weakObjectsHashTable()
init(a: A?) {
super.init()
self.a = a
print("\(self) init")
hashTable.addObserver(self, forKeyPath: "count", options: .New, context:nil)
hashTable.addObject(a)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
print("observe")
}
deinit {
print("\(self) deinit")
}
}
Thank you in advance

How to get if an observer is registered in Swift

I want to remove an observer after it run or when the view dissapeared. Here is the code but sometimes the observer was already removed when i want to remove it again. How to check if it is still registered?
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if(!didOnce){
if(keyPath == "myLocation"){
location = mapView.myLocation.coordinate;
self.mapView.animateToLocation(self.location!);
self.mapView.animateToZoom(15);
didOnce = true;
self.mapView.removeObserver(self, forKeyPath: "myLocation");
}
}
}
override func viewDidAppear(animated: Bool) {
didOnce = false;
}
override func viewWillDisappear(animated: Bool) {
if(!didOnce){
self.mapView.removeObserver(self, forKeyPath: "myLocation");
didOnce = true;
}
}
You're on the right track. Add an isObserving property to your class. Set it to true when you start observing, and set it to false when you stop observing. In all cases check the flag before starting/stopping observing to make sure you are not already in that state.
You can also add a willSet method to the property and have that code start/stop observing when the property changes states.
Main reason: addObserver() is called 1 time (at viewDidLoad or init), but removeObserver() can be called 2 times or more (base on the times viewWillDisappear() was called).
To resolve: move addObserver() to viewWillAppear():
private var didOnce = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.mapView.addObserver(self, forKeyPath: "myLocation", options: .new, context: nil)
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if(!didOnce){
if(keyPath == "myLocation"){
location = mapView.myLocation.coordinate;
self.mapView.animateToLocation(self.location!);
self.mapView.animateToZoom(15);
didOnce = true;
self.mapView.removeObserver(self, forKeyPath: "myLocation");
}
}
}
override func viewWillDisappear(animated: Bool) {
if(!didOnce){
self.mapView.removeObserver(self, forKeyPath: "myLocation");
didOnce = true;
}
}

Resources