How to know when NSHashTable changed count? - ios

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

Related

xcode 8.3 error with generics

protocol PubSubEvent {
associatedtype EventResult
static func eventName() -> String
func event() -> EventResult
func send()
}
class BGEventBus: EventBus {
static let sharedInstance = BGEventBus()
init() {
super.init(queue: OperationQueue())
}
}
class BGEventBusEvent: PubSubEvent {
typealias EventResult = BGEventBusEvent
class func eventName() -> String {
return String(describing: self)
}
func send() {
BGEventBus.sharedInstance.send(event: self)
}
func event() -> BGEventBusEvent.EventResult {
return self
}
}
class BGDidLoginEvent: BGEventBusEvent {
typealias EventResult = BGDidLoginEvent
var password: String?
var facebookToken: String?
init(password: String? = nil, facebookToken: String? = nil) {
self.password = password
self.facebookToken = facebookToken
}
}
class EventBus {
var queue: OperationQueue
init(queue: OperationQueue) {
self.queue = queue
}
func send(event: AnyObject) {
}
func handleEvent<T: PubSubEvent>(target:EventBusObservable, handleBlock: ((T.EventResult) -> Void)!) where T.EventResult == T {
}
}
class EventBusObserver {
var objectProtocol: NSObjectProtocol?
func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: #escaping (Notification) -> Swift.Void) {
self.objectProtocol = NotificationCenter.default.addObserver(forName: name, object: obj, queue: queue, using: block)
}
deinit {
if let obj = self.objectProtocol {
NotificationCenter.default.removeObserver(obj)
}
self.objectProtocol = nil
print("deinit observer!")
}
}
protocol EventBusObservable {
func handleBGEvent<T: PubSubEvent>(handleBlock: ((T.EventResult) -> Void)!) where T.EventResult == T
}
extension EventBusObservable {
func handleBGEvent<T>(handleBlock: ((T) -> Void)!) where T : PubSubEvent, T.EventResult == T {
BGEventBus.sharedInstance.handleEvent(target: self, handleBlock:handleBlock)
}
}
class sample: EventBusObservable {
func test() {
self.handleBGEvent { (event: BGDidLoginEvent) in
}
}
}
Hello guys I updated the Xcode to 8.3 and now I'm getting some errors like this:
Cannot convert value of type '(BGDidLoginEvent) -> ()' to expected argument type '((_) -> Void)!''
can anybody help me?
here the sample file https://drive.google.com/open?id=0B1zPtsTG7crPQncxYnEyWTBpSXM
I think you have to write the generic requirement exactly the same way every time. So, in EventBus:
class EventBus {
// ...
func handleEvent<T>(target:EventBusObservable, handleBlock: ((T) -> Void)!) where T : PubSubEvent, T.EventResult == T {
}
}
In EventBusObservable:
protocol EventBusObservable {
func handleBGEvent<T>(handleBlock: ((T) -> Void)!) where T : PubSubEvent, T.EventResult == T
}
In the EventBusObservable extension:
extension EventBusObservable {
func handleBGEvent<T>(handleBlock: ((T) -> Void)!) where T : PubSubEvent, T.EventResult == T {
BGEventBus.sharedInstance.handleEvent(target: self, handleBlock: handleBlock)
}
}
That compiles. Finally we are left with your class sample. This one wasn't so easy; I found I had to declare event as a BGEventBusEvent:
class sample: EventBusObservable {
func test() {
self.handleBGEvent {
(event:BGEventBusEvent) in
}
}
}

why the setter of var for KVO crashed (swift 2.2)?

I'm using KVO for manual notifications, but why the code crashed for the reason:
Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)" when click
run?
Please see below the codes:
ChildrenViewController.swift (class to be observed)
import UIKit
class ChildrenViewController: UIViewController {
dynamic var name: String? {
get {
return ""
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
self.setValue(value, forKey: "name") //crashed here!SAID "Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)"
didChangeValueForKey("name")
}
}
dynamic var age = 0
var child: ChildrenViewController?
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "name" {
return false
}
return super.automaticallyNotifiesObserversForKey(key)
}
}
ViewController.swift (the observer)
import UIKit
private var child1Context = 1
class ViewController: UIViewController {
var child1 = ChildrenViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.child1.setValue("George", forKey: "name")
self.child1.setValue(15, forKey: "age")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
child1.addObserver(self, forKeyPath: "name", options: [.New,.Old], context: &child1Context)
child1.addObserver(self, forKeyPath: "age", options: [.New, .Old], context: &child1Context)
self.child1.name = "Michael" //set the name String
self.child1.setValue(20, forKey: "age")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &child1Context {
if keyPath == "name" {
print("The name of FIRST has been changed, \(change)")
}
if keyPath == "age" {
print("The age of FIRST has been changed, \(change)")
}
}
}
}
You are setting value of name in it's own setter by this line:
self.setValue(value, forKey: "name")
Why can't do this:
private var _name: String?//create private variable to hold value
dynamic var name: String? {
get {
return _name
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
_name = value
didChangeValueForKey("name")
}
}
You added addObserver in viewWillAppear method, so it means you added it every time when your screen is show. For this case you need call removeObserver in viewWillDisappear method
override func viewWillDisappear(animated: Bool) {
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
super.viewWillDisappear(animated)
}

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 can i auto update value of bool variable

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)
}
}
}

Detect volume button press

Volume button notification function is not being called.
Code:
func listenVolumeButton(){
// Option #1
NSNotificationCenter.defaultCenter().addObserver(self, selector: "volumeChanged:", name: "AVSystemController_SystemVolumeDidChangeNotification", object: nil)
// Option #2
var audioSession = AVAudioSession()
audioSession.setActive(true, error: nil)
audioSession.addObserver(self, forKeyPath: "volumeChanged", options: NSKeyValueObservingOptions.New, context: nil)
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if keyPath == "volumeChanged"{
print("got in here")
}
}
func volumeChanged(notification: NSNotification){
print("got in here")
}
listenVolumeButton() is being called in viewWillAppear
The code is not getting to the print statement "got in here", in either case.
I am trying two different ways to do it, neither way is working.
I have followed this: Detect iPhone Volume Button Up Press?
Using the second method, the value of the key path should be "outputVolume". That is the property we are observing.
So change the code to,
var outputVolumeObserve: NSKeyValueObservation?
let audioSession = AVAudioSession.sharedInstance()
func listenVolumeButton() {
do {
try audioSession.setActive(true)
} catch {}
outputVolumeObserve = audioSession.observe(\.outputVolume) { (audioSession, changes) in
/// TODOs
}
}
The code above won't work in Swift 3, in that case, try this:
func listenVolumeButton() {
do {
try audioSession.setActive(true)
} catch {
print("some error")
}
audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume" {
print("got in here")
}
}
With this code you can listen whenever the user taps the volume hardware button.
class VolumeListener {
static let kVolumeKey = "volume"
static let shared = VolumeListener()
private let kAudioVolumeChangeReasonNotificationParameter = "AVSystemController_AudioVolumeChangeReasonNotificationParameter"
private let kAudioVolumeNotificationParameter = "AVSystemController_AudioVolumeNotificationParameter"
private let kExplicitVolumeChange = "ExplicitVolumeChange"
private let kSystemVolumeDidChangeNotificationName = NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification")
private var hasSetup = false
func start() {
guard !self.hasSetup else {
return
}
self.setup()
self.hasSetup = true
}
private func setup() {
guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
return
}
let volumeView = MPVolumeView(frame: CGRect.zero)
volumeView.clipsToBounds = true
rootViewController.view.addSubview(volumeView)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.volumeChanged),
name: kSystemVolumeDidChangeNotificationName,
object: nil
)
volumeView.removeFromSuperview()
}
#objc func volumeChanged(_ notification: NSNotification) {
guard let userInfo = notification.userInfo,
let volume = userInfo[kAudioVolumeNotificationParameter] as? Float,
let changeReason = userInfo[kAudioVolumeChangeReasonNotificationParameter] as? String,
changeReason == kExplicitVolumeChange
else {
return
}
NotificationCenter.default.post(name: "volumeListenerUserDidInteractWithVolume", object: nil,
userInfo: [VolumeListener.kVolumeKey: volume])
}
}
And to listen you just need to add the observer:
NotificationCenter.default.addObserver(self, selector: #selector(self.userInteractedWithVolume),
name: "volumeListenerUserDidInteractWithVolume", object: nil)
You can access the volume value by checking the userInfo:
#objc private func userInteractedWithVolume(_ notification: Notification) {
guard let volume = notification.userInfo?[VolumeListener.kVolumeKey] as? Float else {
return
}
print("volume: \(volume)")
}
import AVFoundation
import MediaPlayer
override func viewDidLoad() {
super.viewDidLoad()
let volumeView = MPVolumeView(frame: CGRect.zero)
for subview in volumeView.subviews {
if let button = subview as? UIButton {
button.setImage(nil, for: .normal)
button.isEnabled = false
button.sizeToFit()
}
}
UIApplication.shared.windows.first?.addSubview(volumeView)
UIApplication.shared.windows.first?.sendSubview(toBack: volumeView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
do { try AVAudioSession.sharedInstance().setActive(true) }
catch { debugPrint("\(error)") }
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume")
do { try AVAudioSession.sharedInstance().setActive(false) }
catch { debugPrint("\(error)") }
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let key = keyPath else { return }
switch key {
case "outputVolume":
guard let dict = change, let temp = dict[NSKeyValueChangeKey.newKey] as? Float, temp != 0.5 else { return }
let systemSlider = MPVolumeView().subviews.first { (aView) -> Bool in
return NSStringFromClass(aView.classForCoder) == "MPVolumeSlider" ? true : false
} as? UISlider
systemSlider?.setValue(0.5, animated: false)
guard systemSlider != nil else { return }
debugPrint("Either volume button tapped.")
default:
break
}
}
When observing a new value, I set the system volume back to 0.5. This will probably anger users using music simultaneously, therefore I do not recommend my own answer in production.
If interested here is a RxSwift version.
func volumeRx() -> Observable<Void> {
Observable<Void>.create {
subscriber in
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
} catch let e {
subscriber.onError(e)
}
let outputVolumeObserve = audioSession.observe(\.outputVolume) {
(audioSession, changes) in
subscriber.onNext(Void())
}
return Disposables.create {
outputVolumeObserve.invalidate()
}
}
}
Usage
volumeRx()
.subscribe(onNext: {
print("Volume changed")
}).disposed(by: disposeBag)

Resources