Communication between main app and today extension by swift - swift

is possiblity when i put a data by UserDefaults in main app, and call the extension func in a time?
i find an other way in here
How to send and receive data in Today extensions
but it's not working, the following is my code
main app
#IBOutlet weak var nextButton: UIButton! {
didSet {
nextButton.addTarget(self, action: #selector(selectNextButton), for: UIControlEvents.touchUpInside)
}
}
func selectNextButton() {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SpecialKey"), object: nil, userInfo: ["MyData": 10])
}
today extension
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(dataReceived(notification:)), name: NSNotification.Name(rawValue: "SpecialKey"), object: nil)
}
func dataReceived(notification: NSNotification) {
print("Received Data")
}

Related

Function called for all NSWindow

When I call function, it's called for all window opened and not just for the selected window.
If the function is called by #IBAction It's applied for the selected window. Otherwhise, it's applied for all windows.
How can i call the function just for the current selected window ?
Here is an preview:
This is the minimal reproductible code:
// AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
#objc func openMyWindow()
{
let storyboard:NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
guard let controller:NSWindowController = storyboard.instantiateController(withIdentifier: "WindowMain") as? NSWindowController else { return }
controller.showWindow(self)
}
#objc func test()
{
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "TEST"), object: nil, userInfo: nil)
}
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let dockMenu = NSMenu()
dockMenu.addItem(withTitle: "New window", action: #selector(openMyWindow), keyEquivalent: "")
dockMenu.addItem(withTitle: "test", action: #selector(test), keyEquivalent: "")
return dockMenu
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
#objc func Test(){
TextView.string = "It's applied for ALL views -> it's NOT ok"
}
#IBAction func button(_ sender: Any) {
TextView.string = "It's applied just for this view -> it's ok"
}
#IBOutlet var TextView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(Test), name: NSNotification.Name(rawValue: "TEST"), object: nil)
}
override var representedObject: Any? {
didSet {
}
}
}
Notification with object nil (no object) are hard to distinguish when it is not even evaluated which of the windows invoked the post.
In other words, make use of the object: parameter when you post the Notification.
Otherwise all registered observers in multiple windows will act on one and the same Notification.
So what object could be used to know who was sending?
The window object itself of course.
Your WindowController has a window it belongs to as well, just compare its address to the posted Notifications object and act when they are the same.
Or compare against the front most windows address, which usually is the window the user expects to act on commands given.
If the target of the menu item isn't set then the action message is sent to the first responder. In your view the text view is the first responder but it doesn't handle the test message and sends it to the next responder. The view controller is in the responder chain and will receive the test message.
Set the selector of the menu item to the action of the view controller and the view controller of the front window will receive it. No notifications required.
// AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
#objc func openMyWindow() {
let storyboard:NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
guard let controller:NSWindowController = storyboard.instantiateController(withIdentifier: "WindowMain") as? NSWindowController else { return }
controller.showWindow(self)
}
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let dockMenu = NSMenu()
dockMenu.addItem(withTitle: "New window", action: #selector(openMyWindow), keyEquivalent: "")
dockMenu.addItem(withTitle: "test", action: #selector(ViewController.test), keyEquivalent: "")
return dockMenu
}
}
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#objc func test() {
TextView.string = "It's applied for this view -> it's now ok"
}
#IBAction func button(_ sender: Any) {
TextView.string = "It's applied just for this view -> it's ok"
}
#IBOutlet var TextView: NSTextView!
}

Swift 5 doesn't working method from tutorial Type 'NSNotification.Name' has no member 'UIResponder'

I'm very new at programming and swift, so i I study tutorials on YouTube. I wrote the code as in the example, but I ran into a bug. Tell me how I can fix it. In tutorials, you should have a keyboard that appears and disappears.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
registerForKeyBoardNotification()
}
deinit {
removeKeyBoardNotification()
}
#IBOutlet weak var scrollViewLogInScreen: UIScrollView!
#IBOutlet weak var topTextField: UITextField!
#IBOutlet weak var bottomTextField: UITextField!
#IBAction func logInButtonTapped(_ sender: UIButton) {
topTextField.resignFirstResponder()
bottomTextField.resignFirstResponder()
}
#objc
This code doesn't work with following errors:
Type 'NSNotification.Name' has no member 'UIResponder'
**func registerForKeyboardNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(kbWillShow), name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(kbWillHide), name: NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}**
This code also doesn't work with following errors:
Type 'NSNotification.Name' has no member 'UIResponder'
**func removeKeyBoardNotification() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}**
func kbWillShow(_ notification: Notification) {
let userInfo = notification.userInfo
let kbFrameSize = (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
scrollViewLogInScreen.contentOffset = CGPoint.init(x: 0, y: kbFrameSize.height)
}
func kbWillHide() {
scrollViewLogInScreen.contentOffset = CGPoint.zero
}
}
Just remove NSNotification.Name in name
NotificationCenter.default.addObserver(self, selector: #selector(kbWillShow), name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
It must be like :
NotificationCenter.default.addObserver(self, selector: #selector(kbWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

App crashing when adding completion to the observer

I'm trying to add a block to the notification observer, but the app is getting crashed when moving foreground and background. If I'm adding method to observer its working fine, only in case of block its getting crashed. Here is my code which I'm trying for the same.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(foregroundEntered(closure:)), name: UIApplication.willEnterForegroundNotification, object: nil)
// Do any additional setup after loading the view.
}
#objc func foregroundEntered(closure: () -> Void) {
/// do some stuff
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
}
}
Thanks in advance.
Replace
#objc func foregroundEntered(closure: () -> Void) {
With
#objc func foregroundEntered(_ notif: NSNotification) {
And change
NotificationCenter.default.addObserver(self, selector: #selector(foregroundEntered), name: UIApplication.willEnterForegroundNotification, object: nil)

App crashing: Exception - unrecognised selector sent to instance

I've got a UITableviewController with following logic to slide up / down the entire view once the keyboard toggles like so:
class ChatDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// variables...
override func viewDidLoad() {
super.viewDidLoad()
// do stuff...
NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillShow:")), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillHide:")), name: UIResponder.keyboardWillHideNotification, object: nil)
// do other stuff...
}
...
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.view.frame.origin.y -= keyboardSize.height
}
}
func keyboardWillHide(notification: NSNotification) {
self.view.frame.origin.y = 0
}
...
}
Toggling the keyboard then crashes the App with following exception: ChatDetailViewController keyboardWillShow:]: unrecognized selector sent to instance 0x7f82fc41fdf0
The error message seems clear at first glance but I still can't figure out what's wrong with my selector. No code warnings, no typos,...
What is it I'm doing wrong here?
In Swift 4 i have previously used in like this
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
print("keyboardWillShow")
}
#objc func keyboardWillHide(notification: NSNotification){
print("keyboardWillHide")
}
In Swift 5 they renamed the way to access the keyboard notifications to go through UIResponder:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
Three issues:
The creation of the selector is wrong (use #selector)
The signature of the action is wrong (missing underscore)
The action must be marked as #objc
And the type in Swift is Notification without NS prefix
override func viewDidLoad() {
super.viewDidLoad()
// do stuff...
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
// do other stuff...
}
...
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.view.frame.origin.y -= keyboardSize.height
}
}
#objc func keyboardWillHide(_ notification: Notification) {
self.view.frame.origin.y = 0
}
...
You forgot to add #objc part:
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
#objc func keyboardWillShow(_ sender: Notification) {}
#objc func keyboardWillHide(_ sender: Notification) {}

Unrecognized selectors sent to instance, Notification center getting the Keyboard size

If I try to tap into my textfields I get an error, related to these few lines of code that try to get the size of the keyboard on a mobile ios device. The Notification Center lines of code are inside the overriding ViewDidAppear.
NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillShow:")), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillHide:")), name: UIResponder.keyboardDidHideNotification, object: nil)
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.bottomConstraint.constant = keyboardSize.height
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.bottomConstraint.constant = 0
}
Use the type safe syntax
#selector(keyboardWillShow)
and
#objc func keyboardWillShow(_ notification: Notification) { ...
However I highly recommend to use the modern closure based syntax
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] notification in
if let userInfo = notification.userInfo,
let keyboardSize = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self?.bottomConstraint.constant = keyboardSize.height
}
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] _ in
self?.bottomConstraint.constant = 0
}
Try Following code :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
print("notification: Keyboard will show")
}
}
#objc func keyboardWillHide(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
}
}
You can try this:
This code in viewDidLoad():
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
and then add this in ViewController
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
print("Keyboard opened \(keyboardSize)")
}
}
#objc func keyboardWillHide(notification: NSNotification) {
print("Keyboard hide")
}
Hope this will help.
You should deregister any notification you register in a view.
func registerForKeyboardNotifications()
{
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
func deRegisterFromKeyboardNotifications()
{
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWasShown(_ notification: NSNotification)
{
//todo
}
#objc func keyboardWillBeHidden(_ notification: NSNotification)
{
//todo
}
override func viewDidLoad() {
super.viewDidLoad()
registerForKeyboardNotifications()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
deRegisterFromKeyboardNotifications()
}
You've got this error because of notification parameter. With current signature you should use:
#selector(keyboardWillShow(notification:))
#selector(keyboardWillHide(notification:))
Or rewrite your methods in that way:
#objc func keyboardWillShow(_ notification: Notification) {
// Code
}
#objc func keyboardWillHide(_ notification: Notification) {
// Code
}
And use the next syntax:
#selector(keyboardWillShow(_:))
#selector(keyboardWillHide(_:))
Edited:
You can also use simplified syntax:
#selector(keyboardWillShow)
#selector(keyboardWillHide)
Use like below code
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(notification:)),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide(notification:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
// Your code
}
}
#objc func keyboardWillHide(notification: NSNotification) {
// Your code
}
Hope this works, If any doubt plz comment.