I have an app using Reachability library to detect when wifi is turned on while the app is active. I have the following code sample below in my view controller. The view controller is loaded when the app becomes active. When I run this code on my iPhone the selector method is not called. What's wrong with this code and how do I fix it?
let reachability = Reachability.reachabilityForInternetConnection()
override func viewDidLoad() {
super.viewDidLoad()
println("view did load")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: "ReachabilityChangedNotification", object: reachability)
reachability.startNotifier()
}
func reachabilityChanged(note: NSNotification) {
// I don't see this line printed out!!!
println("selector method called")
let reachability = note.object as! Reachability
if reachability.isReachable() {
println("yay internet is on")
} else {
println("internet is off")
}
}
Related
I have three custom swift classes in my app. One is a NetworkServices class and the other two are custom UIViewController classes. The NetworkServices class posts a notification to the other two classes when wifi is turned on and there is a network connection and also when wifi is turned off and there is no network connection. Currently, the app starts up posting the isConnected notification and code executes as expected. I then turn off wifi and isDisconnected is posted and code executes as expected. However, when I then turn wifi back on again the isConnected post is sent but it is not received by either of the two custom UIViewController classes. My problem is why not?
here are relevant lines of code in my NetworkServices class;
protocol NetworkServicesDelegate: AnyObject {
func sendStockInfo(stocksInfo: [String: StockInfo])
}
final class NetworkServices {
static let sharedInstance = NetworkServices()
...
public func startMonitoring() {
monitor.start(queue: queue)
self.monitor.pathUpdateHandler = { [weak self] path in
if path.status == .satisfied {
// connect the socket
self?.socket.connect()
print("DEBUG: self?.socket.connect() called")
} else {
// disconnect the socket
self?.socket.disconnect()
// print("DEBUG: self?.socket.disconnect() called")
self?.isConnected = false
// post notification that socket is now disconnected
DispatchQueue.main.async {
print("DEBUG: Notification.Name(rawValue) called")
let name = Notification.Name(rawValue: isDisconnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
}
}
}
}
...
}
extension NetworkServices: WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(_):
self.isConnected = true
// post notification that socket is now connected
let name = Notification.Name(rawValue: isConnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
print("DEBUG: Notification isConnected posted")
case .disconnected(let reason, let code):
print("DEBUG: Got disconnected reason = \(reason) code = \(code)")
self.isConnected = false
case .cancelled:
print("DEBUG: cancelled.")
// reconnect socket
socket.connect()
self.isConnected = true
case .reconnectSuggested(let suggestReconnect):
print("DEBUG: suggestReconnect = \(suggestReconnect)")
// post notification that socket is not connected
case .viabilityChanged(let viabilityChanged):
print("DEBUG: viabilityChanged = \(viabilityChanged)")
case .error(let error):
print("DEBUG error: \(String(describing: error?.localizedDescription))")
case .text(let socketString):
// print("DEBUG: .text available")
parseJSONSocketData(socketString)
default:
break
}
}
}
Here are the relevant lines of code from my two custom UIViewController classes;
protocol CompanyPriceListDelegate: AnyObject {
func sendPriceHistory(_ priceHistory: PriceHistory)
}
class CompanyPriceListVC: UITableViewController {
...
let isConnected = Notification.Name(rawValue: isConnectedNotificationKey)
let isNotConnected = Notification.Name(rawValue: isDisconnectedNotificationKey)
override func viewDidLoad() {
super.viewDidLoad()
...
createObservers()
...
}
deinit {
NotificationCenter.default.removeObserver(self)
}
private func createObservers() {
print("DEBUG: createObservers() called")
NotificationCenter.default.addObserver(self, selector: #selector(CompanyPriceListVC.dismissAndFetchStockInfo), name: isConnected, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CompanyPriceListVC.notifyUserOnScreen(notification:)), name: isNotConnected, object: nil)
}
#objc private func fetchStockInfo() {
NetworkServices.sharedInstance.fetchStockInfo(symbols: companyStockSymbols, delegate: self)
}
#objc private func notifyUserOnScreen(notification: NSNotification) {
self.present(alertController, animated: true)
}
#objc public func dismissAndFetchStockInfo() {
if presentedViewController == alertController {
self.alertController.dismiss(animated: true, completion: nil)
}
fetchStockInfo()
}
...
}
//
// CompanyDetailsVC.swift
// BetVictorTask
//
// Created by Stephen Learmonth on 02/03/2022.
//
import UIKit
class CompanyDetailsVC: UIViewController {
...
let isConnected = Notification.Name(rawValue: isConnectedNotificationKey)
let isNotConnected = Notification.Name(rawValue: isDisconnectedNotificationKey)
override func viewDidLoad() {
super.viewDidLoad()
configureNavigationBar()
addSubViews()
activateConstraints()
createObservers()
fetchCompanyDetails(symbol: symbol)
let controller = navigationController?.viewControllers.first as! CompanyPriceListVC
controller.delegate = self
}
deinit {
NotificationCenter.default.removeObserver(self)
}
private func createObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(CompanyDetailsVC.dismissAndFetchCompanyDetails), name: isConnected, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CompanyDetailsVC.notifyUserOnScreen(notification:)), name: isNotConnected, object: nil)
}
#objc private func dismissAndFetchCompanyDetails() {
print("DEBUG: dismissAndFetchCompanyDetails() called")
if presentedViewController == alertController {
self.alertController.dismiss(animated: true, completion: nil)
}
fetchCompanyDetails(symbol: symbol)
}
#objc private func notifyUserOnScreen(notification: NSNotification) {
print("DEBUG: notifyUserOnScreen(notification) called")
self.present(alertController, animated: true)
}
...
}
extension CompanyDetailsVC: CompanyPriceListDelegate {
func sendPriceHistory(_ priceHistory: PriceHistory) {
self.updatePrices(priceHistory)
}
}
I’m testing on a real device.
Removed socket.connect() from the.cancelled case
First time poster. I am very new to Swift and coding and general, and have run into a problem I can't seem to solve.
In my code, I have two view controllers. The first view controller allows a user to view Bluetooth devices, and select a device to connect to. When the user selects a device, it segues to the second view controller, which presents temperature data from the Bluetooth device.
This all works fine and dandy, but if I segue back to the first view controller, and then select the same device again, I now receive two of the same temperature readings from the device. (The bluetooth device is receiving two of the same commands from my code and is sending two values back).
Essentially every time I segue back and forth between view controllers, it seems that another instance of the view controller is created, thus creating a memory leak. (If I segued back and forth five times I would receive five Bluetooth readings for every one time I clicked the button to receive a value)
I believe my problem lies in my creation and dismissal of Notification Center Observers, but I can't seem to figure out the correct solution.
I left out code I felt wasn't pertinent to my problem, so if I'm missing any code necessary to solve the problem let me know. Any help would be greatly appreciated!
// First View Controller
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("*****************************")
print("Connection complete")
print("Peripheral info: \(String(describing: blePeripheral))")
//Stop Scan- We don't need to scan once we've connected to a peripheral. We got what we came for.
centralManager?.stopScan()
print("Scan Stopped")
//Erase data that we might have
data.length = 0
//Discovery callback
peripheral.delegate = self
//Only look for services that matches transmit uuid
peripheral.discoverServices(nil)
performSegue(withIdentifier: "Go", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! TempPage
destination.peripheral = blePeripheral
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == rxCharacteristic {
if let ASCIIstring = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) {
characteristicASCIIValue = ASCIIstring
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: nil)
connectionStatus = "Connected!"
}
}
// Second View Controller
override func viewDidLoad() {
super.viewDidLoad()
//Create and start the peripheral manager
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
//-Notification for updating the text view with incoming text
updateIncomingData()
}
override func viewDidDisappear(_ animated: Bool) {
peripheralManager?.stopAdvertising()
self.peripheralManager = nil
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
func updateIncomingData () {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "Notify"), object: nil , queue: nil){
notification in
if characteristicASCIIValue != nil
{
self.rawValue = characteristicASCIIValue as String
print(characteristicASCIIValue)
}
self.batteryLevelLabel.text = ("\(String(batteryLevel))%")
}
#IBAction func returnToFirstViewController(_ sender: Any) {
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
}
Try capturing self as unowned or weak in the notification center callback:
func updateIncomingData () {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "Notify"), object: nil , queue: nil) { [unowned self] notification in
if characteristicASCIIValue != nil
{
self.rawValue = characteristicASCIIValue as String
print(characteristicASCIIValue)
}
self.batteryLevelLabel.text = ("\(String(batteryLevel))%")
}
This article might be useful: https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html
I have the following methods in my "ViewController" class to populate custom cells in a UITableView from a GET API call. This works fine when app first loads.
//ViewController
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadDataInUITableViewCell()
}
func loadDataInUITableViewCell() {
apiCentral.sharedInstance.fetchData() {
result in
guard result.error == nil else {
self.handleError(result.error!)
return
}
if let fetchedData = result.value {
self.data = fetchedData
}
self.messagesTableView.reloadData()
}
}
How do I refresh the data when the app comes to the foreground? Well, I added the following to AppDelegate to run the "loadDataInUITableViewCell" method so that the data reloads.
//AppDelegate
let mainViewController = ViewController()
func applicationWillEnterForeground(_ application: UIApplication) {
mainViewController.loadDataInUITableViewCell()
}
ISSUE: The above mechanism works fine however, I get the following error when the app comes to the foreground.
"fatal error: unexpectedly found nil while unwrapping an Optional value"
The issue seems to be happening at "self.messagesTableView.reloadData()" line of code. I'm using Xcode 8.3 and coding in Swift 3. Any guidance would be appreciated. Thank you.
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
Write this above line in viewWillAppear()
func willEnterForeground() {
tableView.reloadData()
}
Used this above code in your ViewController where there is UITableView
Solutions 1. In appdelegate:
//AppDelegate
let mainViewController : ViewController?
func applicationWillEnterForeground(_ application: UIApplication) {
mainViewController.loadDataInUITableViewCell()
}
And in Your viewcontroller:
//ViewController
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
(UIApplication.sharedApplication().delegate as! AppDelegate).mainViewController = self
loadDataInUITableViewCell()
}
Solutions 2.
listen in viewcontroller:
//ViewController
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(ViewController.loadDataInUITableViewCell(), name: UIApplicationWillEnterForegroundNotification, object: nil)
loadDataInUITableViewCell()
}
UIApplicationWillEnterForeground Notification
Posted shortly before an app leaves the background state on its way to becoming the active app.
Discussion
The object of the notification is the UIApplication object. There is no userInfo dictionary.
Declaration
static let UIApplicationWillEnterForeground: NSNotification.Name
From Apple's Documentation.
It's super easy to use:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.handler), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
}
func handler() {
// self.tableView.reloadData()
}
Hope it helps!
I've tried multiple methods to add an observer, and call this function:
In viewDidLoad
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
and also;
deinit {
NotificationCenter.default.removeObserver(self)
}
func deviceOrientationDidChange() {
print(UIDevice.current.orientation.rawValue)
// all return false
print(UIDevice.current.orientation.isFlat)
print(UIDevice.current.orientation.isPortrait)
print(UIDevice.current.orientation.isLandscape)
print(UIDevice.current.orientation.isValidInterfaceOrientation)
}
I've also tried:
UIDevice.current.orientation.isLandscape
and all the other possibilities like .isLandscape, .IsFlat, etc and none of them seem to be working either.
You forgot to call beginGeneratingDeviceOrientationNotifications(). So the device will never send UIDeviceOrientationDidChange. And that is also why UIDevice.current.orientation doesn't work; it only works if you have called beginGeneratingDeviceOrientationNotifications(). The docs are quite clear about this.
Example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(
self, selector: #selector(deviceOrientationDidChange),
name: .UIDeviceOrientationDidChange, object: nil)
}
func deviceOrientationDidChange() {
print(UIDevice.current.orientation.rawValue)
}
}
Output:
0
1
3
2
4
1
This is how I ended up getting the information:
func deviceOrientation() {
if UIScreen.main.bounds.size.width < UIScreen.main.bounds.size.height {
print("portrait")
} else {
print("landscape")
}
}
I made an app, I want to add function that notify user when app's internet reachability is changed.
I use Ashley Mills' Reachability.swift file.
now I understand how it works, So I put code that when internet reachability is changed, it will print it's status in appDelegate.
However when I tried to put in function that alert user there isn't internet connection, It gets an error.
here is my code in app delegate.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var reachability : Reachability?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
do {
let reachability = try Reachability.reachabilityForInternetConnection()
self.reachability = reachability
} catch ReachabilityError.FailedToCreateWithAddress(let address) {
}
catch {}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: ReachabilityChangedNotification, object: reachability)
do {
try reachability?.startNotifier()
} catch {}
return true
}
func reachabilityChanged(notification: NSNotification) {
let reachability = notification.object as! Reachability
if reachability.isReachable() {
print("reached")
} else {
print("not reached")
}
}
This works well.
However the code in Viewcontroller,
class ViewController: UIViewController {
var reachability : Reachability?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "HeyUserInternetDoesntWork", name: ReachabilityChangedNotification, object: nil)
//get call from appDelegate Notification.
}
func HeyUserInternetDoesntWork() {
if reachability!.isReachable() {
print("notify User working")
} else {
print("Notify user not working")
}
}
unexpectedly found nil while unwrapping an Optional value
It gets this error.
I am going to put code for alerting user after it works.
Question here,
How Can I make it this work?
It doesn't have to be use that method, but I want to keep using NSNotification.
Actually I am a new guy for coding, So please explain details.
Where do you init reachability property? this variable is always nil.
In func HeyUserInternetDoesntWork you try to use reachability and of course it gets error. You need to init property like this:
private let reachability = Reachability.reachabilityForInternetConnection()
After use func HeyUserInternetDoesntWork with 'dynamic' keyword like this:
dynamic func HeyUserInternetDoesntWork() {
if reachability!.isReachable() {
print("notify User working")
} else {
print("Notify user not working")
}
}
Because NSNotificationCenter observer selector should be dynamic.