App crashed at textview.becomeFirstResponder - ios

My app sometimes crashes at the call to textView.becomeFirstResponder(). The error thrown is strange:
-[UITextSelectionView keyboardShownWithNotif:]: unrecognized selector sent to instance 0x16899070
Sometimes it's:
-[UIImageView keyboardShownWithNotif:]: unrecognized selector sent to instance 0x178e2610
I did add notification listeners:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShown(notif:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardHidden), name: .UIKeyboardWillHide, object: nil)
But the observer is the custom view I defined, why does the system send notification to UITextSelectionView or UIImageView?
Found in iOS 8.4.1, not reproduced in iOS 9.
What is happening here?

seems like you added an notif. observer to show/hide keyboard.
Try to remove observer in dealloc method
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self]; //Or whichever observer you want to remove
}

In swift 3:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}
or
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}

Related

How to restrict NSNotification to call methods multiple times in iOS?

I am using NSNotificationCenter to send local notifications in my code and working in both Objective-C and Swift. I'm posting notifications from Objective-C And receiving in Swift. But the methods that I added in notification getting called multiple times and added observer only in viewDidLoad method.
Swift:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.serverCardSynced), name: NSNotification.Name(rawValue: NOTIF_SERVER_CARD_SYNCED), object: nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.checkForAutoSync), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateSync), name: NSNotification.Name(rawValue: NOTIF_CONTACT_ENTITY_CHANGE), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.menuRemoved), name: NSNotification.Name(rawValue: NOTIF_MENU_REMOVED), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.reloadAllCards(_:)), name: NSNotification.Name(rawValue: NOTIF_RELOAD_ALL_CARDS), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateDownloadMyCards), name: NSNotification.Name(rawValue: NOTIF_DOWNLOAD_CARD), object:nil);
}
Objective-C:
- (void)applicationDidBecomeActive:(UIApplication *)application {
self.isSyncPending = true;
[[NSNotificationCenter defaultCenter]
postNotificationName:NOTIF_CONTACT_ENTITY_CHANGE object:nil];
}
-(void)insertData(){
[[NSNotificationCenter defaultCenter]
postNotificationName:NOTIF_SERVER_CARD_SYNCED object:nil];
}
I added remove observer in my deinit but it is not even calling. How to stop calling multiple times.
//call this method in viewDidLoad
fileprivate func registerNotifs() {
//remove observer before adding to make sure that it is added only once
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NOTIF_SERVER_CARD_SYNCED), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NOTIF_CONTACT_ENTITY_CHANGE), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NOTIF_MENU_REMOVED), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NOTIF_RELOAD_ALL_CARDS), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NOTIF_DOWNLOAD_CARD), object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.serverCardSynced), name: NSNotification.Name(rawValue: NOTIF_SERVER_CARD_SYNCED), object: nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.checkForAutoSync), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateSync), name: NSNotification.Name(rawValue: NOTIF_CONTACT_ENTITY_CHANGE), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.menuRemoved), name: NSNotification.Name(rawValue: NOTIF_MENU_REMOVED), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.reloadAllCards(_:)), name: NSNotification.Name(rawValue: NOTIF_RELOAD_ALL_CARDS), object:nil);
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateDownloadMyCards), name: NSNotification.Name(rawValue: NOTIF_DOWNLOAD_CARD), object:nil);
}
you can do like this
fileprivate func registerLocalNotifications() {
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.serverCardSynced), name: NSNotification.Name(rawValue: NOTIF_SERVER_CARD_SYNCED), object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.checkForAutoSync), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateSync), name: NSNotification.Name(rawValue: NOTIF_CONTACT_ENTITY_CHANGE), object:nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.menuRemoved), name: NSNotification.Name(rawValue: NOTIF_MENU_REMOVED), object:nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.reloadAllCards(_:)), name: NSNotification.Name(rawValue: NOTIF_RELOAD_ALL_CARDS), object:nil)
NotificationCenter.default.addObserver(self, selector:#selector(MainScreen.initateDownloadMyCards), name: NSNotification.Name(rawValue: NOTIF_DOWNLOAD_CARD), object:nil)
}
add above method in viewDidLoad and in
deinit {
NotificationCenter.default.removeObserver(self)
}
REF : Where to remove observer for NSNotification in Swift?
Update:
Check you class instance if it already persist i.e
in view when you are instantiating this view controller
you can do like this
if let vc = yourNotificationViewCointrollerObj {
// USE EXISTING ONE
} else {
// CREATE NEW INSTANCE
}
NSNotificationCenter is a "publish-subscribe" mechanism where each posted notification is delivered to all subscribers (called observers). If you receive a notification multiple times it means that either you have multiple subscribers, or you are sending a notification multiple times.
I assume that you have only one subscriber for each notification type - your MainScreen UIViewController in Swift. It means that you probably send each notification multiple times.
For example applicationDidBecomeActive is called multiple times (each time the app becomes active). If you want to stop responding to this notification after the first time, you could unsubscribe from it as soon as you receive it first time:
#objc
func initateSync {
// do something first time
// ...
// unsubscribe to not receive it anymore
NotificationCenter.default.removeObserver(self,
name: NSNotification.Name(rawValue: NOTIF_CONTACT_ENTITY_CHANGE),
object: nil)
}
This is what you do in real world after receiving a newspaper trial that you didn't like :)

Using selector in Swift 3 NotificationCenter observer

NotificationCenter.default.addObserver(self, selector: Selector(("uploaded")), name: NSNotification.Name(rawValue: "uploaded"), object: nil)
I was writing name: "uploaded:" and xcode corrected it to the above code. The problem is when running the app i get unrecognized selector.
Any one know how to fix this to work with swift 3
Use the (identifier checking) #selector syntax:
Without parameter:
#selector(uploaded)
With parameter:
#selector(uploaded(_:))
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.update), name: NSNotification.Name(rawValue: "uploaded"), object: nil)
func update() {
// do what you want
}
please note that "ViewController" is the class name where your function is

Segmentation fault: 11; (xcode 8, Swift 3) after using NotificationCenter

Getting Segmentation fault in Xcode 8
I have recently migrated my project to Swift 3. Xcode Version 8.0 (8A218a)
I get this error whenever I use UIKeyboardWillShow notification :
Command failed due to signal: Segmentation fault: 11`
This is how I am using the notification in my code:
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillShow)
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillHide)
NotificationCenter.default.addObserver(self, selector: #selector(myViewController.keyboardWillShow(_:)), name:NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(myViewController.keyboardWillHide(_:)), name:NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShow(_ sender: Notification) {
//keyboardWillShow Method
}
func keyboardWillHide(_ sender: Notification) {
// keyboardWillHide Method
}
Project runs successfully when I comment out the code in viewWillAppear method.
The main topic: Segmentation fault: 11, it's a bug of Xcode8/Swift3 and you should send a bug report.
And about your code:
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillShow)
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillHide)
There are no methods removing observers specifying only their names. You need to specify observer object for removeObserver(_:).
I'm not sure this is what you intend, but you can use removeObserver(_:name:object:) method like this:
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
I'm sure this would not crash your Xcode.
I had the same issue in the same conditions and environment (Swift 3, Xcode 8) and to resolve this problem you should put:
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
instead of:
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillShow)
NotificationCenter.default.removeObserver(NSNotification.Name.UIKeyboardWillHide)

Function called on viewController close (when application exit)?

I am looking for a function which is called on a UIViewController when the application exits.
I tried it out with viewWillDisappear and with applicationWillTerminate but nothing works.
I want to save my settings from the UIViewController in this function.
Like #Jeff said, but
In Swift 3:
override func viewDidLoad () {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(suspending), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(suspending), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
}
func suspending () {
print("suspending...")
}
UIApplicationWillResignActive happens when the app is swapped out, while
UIApplicationWillTerminate is triggered when exiting, such as when the Home button is pressed.
You need to register for notifications to know when the appropriate calls are made as an app terminates.
NSNotificationCenter .defaultCenter() .addObserver(self, selector: Selector("callBack"), name: UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter .defaultCenter() .addObserver(self, selector: Selector("callBack"), name: UIApplicationWillTerminateNotification, object: nil)
func callback() {
// Save your settings
}
Remember to remove the observers in viewWillDisappear:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter .defaultCenter() .removeObserver(self)
}
you can call deinit inside the ViewController
deinit {
deleteUnsavedPhotoFromServer()
}
Swift 5.5
Use For App Enter Background
NotificationCenter.default.addObserver(self, selector: #selector(appResignActiveNotify), name: UIApplication.willResignActiveNotification, object: nil)
#objc func appResignActiveNotify() {
//called when App enter background
}
For App Terminate
NotificationCenter.default.addObserver(self, selector: #selector(appTerminateNotify), name: UIApplication.willTerminateNotification, object: nil)
#objc func appTerminateNotify() {
//called when Terminate
}

Handling applicationDidBecomeActive - "How can a view controller respond to the app becoming Active?"

I have the UIApplicationDelegate protocol in my main AppDelegate.m class, with the applicationDidBecomeActive method defined.
I want to call a method when the application returns from the background, but the method is in another view controller. How can I check which view controller is currently showing in the applicationDidBecomeActive method and then make a call to a method within that controller?
Any class in your application can become an "observer" for different notifications in the application. When you create (or load) your view controller, you'll want to register it as an observer for the UIApplicationDidBecomeActiveNotification and specify which method that you want to call when that notification gets sent to your application.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someMethod:)
name:UIApplicationDidBecomeActiveNotification object:nil];
Don't forget to clean up after yourself! Remember to remove yourself as the observer when your view is going away:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidBecomeActiveNotification
object:nil];
More information about the Notification Center.
Swift 3, 4 Equivalent:
adding observer
NotificationCenter.default.addObserver(self,
selector: #selector(applicationDidBecomeActive),
name: .UIApplicationDidBecomeActive, // UIApplication.didBecomeActiveNotification for swift 4.2+
object: nil)
removing observer
NotificationCenter.default.removeObserver(self,
name: .UIApplicationDidBecomeActive, // UIApplication.didBecomeActiveNotification for swift 4.2+
object: nil)
callback
#objc func applicationDidBecomeActive() {
// handle event
}
Swift 2 Equivalent:
let notificationCenter = NSNotificationCenter.defaultCenter()
// Add observer:
notificationCenter.addObserver(self,
selector:Selector("applicationWillResignActiveNotification"),
name:UIApplicationWillResignActiveNotification,
object:nil)
// Remove observer:
notificationCenter.removeObserver(self,
name:UIApplicationWillResignActiveNotification,
object:nil)
// Remove all observer for all notifications:
notificationCenter.removeObserver(self)
// Callback:
func applicationWillResignActiveNotification() {
// Handle application will resign notification event.
}
Swift 5
fileprivate func addObservers() {
NotificationCenter.default.addObserver(self,
selector: #selector(applicationDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)
}
fileprivate func removeObservers() {
NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc fileprivate func applicationDidBecomeActive() {
// here do your work
}
Swift 4.2
Add observer-
NotificationCenter.default.addObserver(self, selector: #selector(handleEvent), name: UIApplication.didBecomeActiveNotification, object: nil)
Remove observer-
NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
Handle Event-
#objc func handleEvent() {
}
With Swift 4, Apple advises via a new compiler warning that we avoid the use of #selector in this scenario. The following is a much safer way to accomplish this:
First, create a variable that will hold the observer instance (that will be used to cancel it):
var didBecomeActiveObserver: NSObjectProtocol
Then create a lazy var that can be used by the notification:
lazy var didBecomeActive: (Notification) -> Void = { [weak self] _ in
// Do stuff
}
If you require the actual notification be included, just replace the _ with notification.
Next, we set up the notification to observe for the app becoming active.
func setupObserver() {
didBecomeActiveObserver = NotificationCenter.default.addObserver(
forName: UIApplication.didBecomeActiveNotification,
object: nil,
queue:.main,
using: didBecomeActive)
}
The big change here is that instead of calling a #selector, we now call the var created above. This can eliminate situations where you get invalid selector crashes.
Finally, we remove the observer.
func removeObserver() {
NotificationCenter.default.removeObserver(didBecomeActiveObserver)
}
Swift 5 version:
NotificationCenter.default.addObserver(self,
selector: #selector(loadData),
name: UIApplication.didBecomeActiveNotification,
object: nil)
Removing the observer is no longer required in iOS 9 and later.
In Swift 5
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc private func applicationWillResignActive() {
}
#objc private func applicationDidBecomeActive() {
}
The Combine way:
import Combine
var cancellables = Set<AnyCancellable>()
NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
.sink { notification in
// do stuff
}.store(in: &cancellables)
If any of you is using SwiftUI:
.onReceive(NotificationCenter.default.publisher(
for: UIApplication.didBecomeActiveNotification)) { _ in
print("DID BECOME ACTIVE")
}
)
Cleaner Swift 5+ solution
Add the observer to init or viewDidLoad:
NotificationCenter.default.addObserver(self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)
You don't need to remove the observer as other answers suggest. It will be done automatically.
#objc private func appDidBecomeActive() {
// do your magic
}
For Swift5 MacOS, you need to use NSApplication instead of UIApplication.
NotificationCenter.default.addObserver(self,
selector: #selector(applicationDidBecomeActive),
name: (NSApplication.didBecomeActiveNotification),
object: nil)
}

Resources