Calling UIApplication subclass method to start scheduledTimer - ios

I am using Xcode 9.2/iOS 10 and Swift to detect user inactivity in iOS applications using information based on an excellent tutorial example from https://blog.gaelfoppolo.com/detecting-user-inactivity-in-ios-application-684b0eeeef5b The enhancements I am making are to start the timer when viewDidLoad is called and adding a snooze function in applicationDidTimeout. At compile time, the following error message is displayed: "Instance member 'snoozeTimer' cannot be used on type 'TimerApplication'; did you mean to use a value of this type instead?"
applicationDidTimeout will use snoozeTimer to control whether it needs to have another timer alarm depending on additional logic to be added to the method.
What is the correct way to call startTimer and snoozeTimer?
A code listing is shown below:
main.swift
UIApplicationMain(
CommandLine.argc,
UnsafeMutableRawPointer(CommandLine.unsafeArgv)
.bindMemory(
to: UnsafeMutablePointer<Int8>.self,
capacity: Int(CommandLine.argc)),
NSStringFromClass(TimerApplication.self),
NSStringFromClass(AppDelegate.self)
)
AppDelegate.swift
import UIKit
import Foundation
extension Notification.Name {
static let appTimeout = Notification.Name("appTimeout")
}
//#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var backgroundTaskRunCount : Int = 0
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
NotificationCenter.default.addObserver(self,
selector: #selector(AppDelegate.applicationDidTimeout(notification:)),
name: .appTimeout,
object: nil
)
return true
}
. //other func
.
.
#objc func applicationDidTimeout(notification: NSNotification) {
backgroundTaskRunCount = backgroundTaskRunCount + 1
print("application did timeout, perform actions. Count=", backgroundTaskRunCount)
TimerApplication.snoozeTimer()
}
}
TimerApplication.swift
import UIKit
class TimerApplication: UIApplication {
private var wakeupInSeconds: TimeInterval {
// 2 minutes for for timer to fire first time
return 2 * 60
}
private let snoozeInSeconds: TimeInterval = 5
// the timeout in seconds, after which should perform custom actions
// such as disconnecting the user
private var timeoutInSeconds: TimeInterval = 120
private var idleTimer: Timer?
// resent the timer because there was user interaction
private func resetIdleTimer() {
if timeoutInSeconds > 0 {
if let idleTimer = idleTimer {
idleTimer.invalidate()
}
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds,
target: self,
selector: #selector(TimerApplication.timeHasExceeded),
userInfo: nil,
repeats: false
)
} else {
stopTimer()
}
}
// if the timer reaches the limit as defined in timeoutInSeconds, post this notification
#objc private func timeHasExceeded() {
NotificationCenter.default.post(name: .appTimeout,
object: nil
)
}
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
if idleTimer != nil {
self.resetIdleTimer()
}
if let touches = event.allTouches {
for touch in touches where touch.phase == UITouchPhase.began {
self.resetIdleTimer()
}
}
}
func startTimer() {
self.timeoutInSeconds = wakeupInSeconds
self.resetIdleTimer()
}
func stopTimer() {
if let idleTimer = idleTimer {
idleTimer.invalidate()
}
}
func snoozeTimer() {
self.timeoutInSeconds = snoozeInSeconds
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
TimerApplication.startTimer()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

You want to use TimerApplication.shared.snoozeTimer() instead of TimerApplication.snoozeTimer() because snoozeTimer() is an instance method, not class or static method.
You should verify that TimerApplication.shared returns the instance of TimerApplication you're expecting because I've never subclassed a system singleton like this. If that doesn't work, you'll need a way to get a reference to the TimerApplication instance you want.

Related

How can I remove an element of main.storyboard

How do I remove the "Wrapper" element and buttons "back" and "menu" from main.storyboard? but to keep loading bar
I tried to delete part of the code but it gives errors.
I will be very happy if someone helps me. Thank you very much in advance for your help.
Below you will find the code I use
ViewController.swift:
import WebKit
class ViewController: UIViewController {
// MARK: Outlets
#IBOutlet weak var leftButton: UIBarButtonItem!
#IBOutlet weak var rightButton: UIBarButtonItem!
#IBOutlet weak var webViewContainer: UIView!
#IBOutlet weak var offlineView: UIView!
#IBOutlet weak var offlineIcon: UIImageView!
#IBOutlet weak var offlineButton: UIButton!
#IBOutlet weak var activityIndicatorView: UIView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
// MARK: Globals
var webView: WKWebView!
var tempView: WKWebView!
var progressBar : UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.title = appTitle
setupApp()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// UI Actions
// handle back press
#IBAction func onLeftButtonClick(_ sender: Any) {
if (webView.canGoBack) {
webView.goBack()
// fix a glitch, as the above seems to trigger observeValue -> WKWebView.isLoading
activityIndicatorView.isHidden = true
activityIndicator.stopAnimating()
} else {
// exit app
UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
}
}
// open menu in page, or fire alternate function on large screens
#IBAction func onRightButtonClick(_ sender: Any) {
if (changeMenuButtonOnWideScreens && isWideScreen()) {
webView.evaluateJavaScript(alternateRightButtonJavascript, completionHandler: nil)
} else {
webView.evaluateJavaScript(menuButtonJavascript, completionHandler: nil)
}
}
// reload page from offline screen
#IBAction func onOfflineButtonClick(_ sender: Any) {
offlineView.isHidden = true
webViewContainer.isHidden = false
loadAppUrl()
}
// Observers for updating UI
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == #keyPath(WKWebView.isLoading)) {
// show activity indicator
/*
// this causes troubles when swiping back and forward.
// having this disabled means that the activity view is only shown on the startup of the app.
// ...which is fair enough.
if (webView.isLoading) {
activityIndicatorView.isHidden = false
activityIndicator.startAnimating()
}
*/
}
if (keyPath == #keyPath(WKWebView.estimatedProgress)) {
progressBar.progress = Float(webView.estimatedProgress)
rightButton.isEnabled = (webView.estimatedProgress == 1)
}
}
// Initialize WKWebView
func setupWebView() {
// set up webview
webView = WKWebView(frame: CGRect(x: 0, y: 0, width: webViewContainer.frame.width, height: webViewContainer.frame.height))
webView.navigationDelegate = self
webView.uiDelegate = self
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webViewContainer.addSubview(webView)
// settings
webView.allowsBackForwardNavigationGestures = true
webView.configuration.preferences.javaScriptEnabled = true
if #available(iOS 10.0, *) {
webView.configuration.ignoresViewportScaleLimits = false
}
// user agent
if #available(iOS 9.0, *) {
if (useCustomUserAgent) {
webView.customUserAgent = customUserAgent
}
if (useUserAgentPostfix) {
if (useCustomUserAgent) {
webView.customUserAgent = customUserAgent + " " + userAgentPostfix
} else {
tempView = WKWebView(frame: .zero)
tempView.evaluateJavaScript("navigator.userAgent", completionHandler: { (result, error) in
if let resultObject = result {
self.webView.customUserAgent = (String(describing: resultObject) + " " + userAgentPostfix)
self.tempView = nil
}
})
}
}
webView.configuration.applicationNameForUserAgent = ""
}
// bounces
webView.scrollView.bounces = enableBounceWhenScrolling
// init observers
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.isLoading), options: NSKeyValueObservingOptions.new, context: nil)
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: NSKeyValueObservingOptions.new, context: nil)
}
// Initialize UI elements
// call after WebView has been initialized
func setupUI() {
// leftButton.isEnabled = false
// progress bar
progressBar = UIProgressView(frame: CGRect(x: 0, y: 0, width: webViewContainer.frame.width, height: 40))
progressBar.autoresizingMask = [.flexibleWidth]
progressBar.progress = 0.0
progressBar.tintColor = progressBarColor
webView.addSubview(progressBar)
// activity indicator
activityIndicator.color = activityIndicatorColor
activityIndicator.startAnimating()
// offline container
offlineIcon.tintColor = offlineIconColor
offlineButton.tintColor = buttonColor
offlineView.isHidden = true
// setup navigation bar
if (forceLargeTitle) {
if #available(iOS 11.0, *) {
navigationItem.largeTitleDisplayMode = UINavigationItem.LargeTitleDisplayMode.always
}
}
if (useLightStatusBarStyle) {
self.navigationController?.navigationBar.barStyle = UIBarStyle.black
}
// handle menu button changes
/// set default
rightButton.title = menuButtonTitle
/// update if necessary
updateRightButtonTitle(invert: false)
/// create callback for device rotation
let deviceRotationCallback : (Notification) -> Void = { _ in
// this fires BEFORE the UI is updated, so we check for the opposite orientation,
// if it's not the initial setup
self.updateRightButtonTitle(invert: true)
}
/// listen for device rotation
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: deviceRotationCallback)
/*
// #DEBUG: test offline view
offlineView.isHidden = false
webViewContainer.isHidden = true
*/
}
// load startpage
func loadAppUrl() {
let urlRequest = URLRequest(url: webAppUrl!)
webView.load(urlRequest)
}
// Initialize App and start loading
func setupApp() {
setupWebView()
setupUI()
loadAppUrl()
}
// Cleanup
deinit {
webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.isLoading))
webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}
// Helper method to determine wide screen width
func isWideScreen() -> Bool {
// this considers device orientation too.
if (UIScreen.main.bounds.width >= wideScreenMinWidth) {
return true
} else {
return false
}
}
// UI Helper method to update right button text according to available screen width
func updateRightButtonTitle(invert: Bool) {
if (changeMenuButtonOnWideScreens) {
// first, check if device is wide enough to
if (UIScreen.main.fixedCoordinateSpace.bounds.height < wideScreenMinWidth) {
// long side of the screen is not long enough, don't need to update
return
}
// second, check if both portrait and landscape would fit
if (UIScreen.main.fixedCoordinateSpace.bounds.height >= wideScreenMinWidth
&& UIScreen.main.fixedCoordinateSpace.bounds.width >= wideScreenMinWidth) {
// both orientations are considered "wide"
rightButton.title = alternateRightButtonTitle
return
}
// if we land here, check the current screen width.
// we need to flip it around in some cases though, as our callback is triggered before the UI is updated
let changeToAlternateTitle = invert
? !isWideScreen()
: isWideScreen()
if (changeToAlternateTitle) {
rightButton.title = alternateRightButtonTitle
} else {
rightButton.title = menuButtonTitle
}
}
}
}
// WebView Event Listeners
extension ViewController: WKNavigationDelegate {
// didFinish
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// set title
if (changeAppTitleToPageTitle) {
navigationItem.title = webView.title
}
// hide progress bar after initial load
progressBar.isHidden = true
// hide activity indicator
activityIndicatorView.isHidden = true
activityIndicator.stopAnimating()
}
// didFailProvisionalNavigation
// == we are offline / page not available
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
// show offline screen
offlineView.isHidden = false
webViewContainer.isHidden = true
}
}
// WebView additional handlers
extension ViewController: WKUIDelegate {
// handle links opening in new tabs
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if (navigationAction.targetFrame == nil) {
webView.load(navigationAction.request)
}
return nil
}
// restrict navigation to target host, open external links in 3rd party apps
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let requestUrl = navigationAction.request.url {
if let requestHost = requestUrl.host {
if (requestHost.range(of: allowedOrigin) != nil ) {
decisionHandler(.allow)
} else {
decisionHandler(.cancel)
if (UIApplication.shared.canOpenURL(requestUrl)) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(requestUrl)
} else {
// Fallback on earlier versions
UIApplication.shared.openURL(requestUrl)
}
}
}
} else {
decisionHandler(.cancel)
}
}
}
}
AppDelegate.swift:
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Change Navigation style
UINavigationBar.appearance().barTintColor = navigationBarColor
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: navigationTitleColor]
UIBarButtonItem.appearance().tintColor = navigationButtonColor
if #available(iOS 11.0, *) {
UINavigationBar.appearance().largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: navigationTitleColor]
}
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
These are called Navigation buttons and, If you hide them, Apple will reject your WebView app with the 4.2 minimum functionality, But either way, to do so- Paste this below viewDidLoad():
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated) // Hide
}
More info 👉 How to hide a navigation bar from first ViewController in Swift?

Swift SFSafariViewController present show blank page

EDIT2
here is a MWC containing 2 classes with only the SFSafariViewController for opening an url embedded. What I get on screen is a blank page
The files are also here https://github.com/camillegallet/testSwiftSafariEmbeded
AppDelegate
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
View controller
import UIKit
import WebKit
import SafariServices
class ViewController: UIViewController {
#IBOutlet weak var KronosWebsite: WKWebView!
override func loadView() {
KronosWebsite = WKWebView()
self.view = KronosWebsite
}
override func viewDidLoad() {
super.viewDidLoad()
openGoogle()
}
func openGoogle(){
let url2=URL(string: "http://www.google.com")
let web = SFSafariViewController(url: url2!)
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
do{
topController.present(web, animated: true, completion: nil)
}catch let error {
DispatchQueue.main.async {
print("ERROR \(error)")
}
}
}
}
}
I've this old function in a library
public func authorizeSafariEmbedded(from controller: UIViewController, at url: URL) throws -> SFSafariViewController {
safariViewDelegate = OAuth2SFViewControllerDelegate(authorizer: self)
let web = SFSafariViewController(url: url)
web.title = oauth2.authConfig.ui.title
web.delegate = safariViewDelegate as! OAuth2SFViewControllerDelegate
if #available(iOS 10.0, *), let barTint = oauth2.authConfig.ui.barTintColor {
web.preferredBarTintColor = barTint
}
if #available(iOS 10.0, *), let tint = oauth2.authConfig.ui.controlTintColor {
web.preferredControlTintColor = tint
}
web.modalPresentationStyle = oauth2.authConfig.ui.modalPresentationStyle
willPresent(viewController: web, in: nil)
controller.present(web, animated: true, completion: nil)
return web
}
I call this function with those lines
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
do{
let web = try authorizer.authorizeSafariEmbedded(from: topController,at: url!)
}catch let error {
DispatchQueue.main.async {
print("ERROR \(error)")
}
}
}
But I keep having a blank page, I've not found a correct working solution on the web
I've also tested with
let url=URL(string: "http://www.google.com")
even this don't works
public func authorizeSafariEmbedded(from controller: UIViewController, at url: URL) throws -> SFSafariViewController {
let web = SFSafariViewController(url: url)
web.title = oauth2.authConfig.ui.title
controller.present(web, animated: true, completion: nil)
return web
}
Thanks in advance
Here what I've done to make it works
import UIKit
import WebKit
import SafariServices
class ViewController: UIViewController {
var safariVC = SFSafariViewController(url: URL(string: "https://apple.com")!)
#IBOutlet weak var KronosWebsite: WKWebView!
override func loadView() {
KronosWebsite = WKWebView()
self.view = KronosWebsite
}
func addViewControllerAsChildViewController() {
addChild(safariVC)
self.view.addSubview(safariVC.view)
safariVC.didMove(toParent: self)
self.setUpConstraints()
}
override func viewDidLoad() {
super.viewDidLoad()
addViewControllerAsChildViewController()
}
func setUpConstraints() {
self.safariVC.view.translatesAutoresizingMaskIntoConstraints = false
self.safariVC.view.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true
self.safariVC.view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -30).isActive = true
self.safariVC.view.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 30).isActive = true
self.safariVC.view.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: -30).isActive = true
}
}

I don't understand why my object doesn't receive notifications

I've create a custom class that looks like:
class FooDevice: Device {
private var controller:FooController?
private var device:Foo?
override init() {
super.init()
if super.authGranted {
NotificationCenter.default.addObserver(self, selector: #selector(self.discovered(_:)), name: NSNotification.Name(rawValue: FooDiscover), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.connected(_:)), name: NSNotification.Name(rawValue: FooConnected), object: nil)
}
}
#objc private func discovered(_ notification:Notification) {
DDLogVerbose("FOO - discovered - \(notification)")
super.scanner?.stopScanFor(._FOO)
// TODO: Call super.connector and connect
}
#objc private func connected(_ notification:Notification) {
DDLogVerbose("FOO - connected - \(notification)")
// Do more stuff after connect
}
func start() {
DDLogVerbose("FOO - startMeasurement()")
super.scanner?.scanFor(._FOO)
}
}
The super class looks like:
class Device: NSObject {
private let LICENSE = "my_license"
private var authenticator:SDKAuthenticator?
internal var scanner:SDKScanner?
internal var connector:SDKConnector?
internal var authGranted = false
override init() {
super.init()
authGranted = self.authRequest(LICENSE)
if authGranted {
scanner = SDKScanner.getInstance()
connector = SDKConnector.getInstance()
} else {
// TODO: Show error to user
}
}
private func authRequest(_ data:String) -> Bool {
// Do stuff using authenticator and authenticated, we can assume we return a true
return status // true
}
}
In my ViewController I make an instance of FooDevice and start the process. I'm doing with the following:
class MyViewController:UIViewController {
// A lot of properties
override viewDidLoad() {
// ViewDidLoad stuff
}
#IBAction func connectToDevice(_ sender: Any) {
// Here I instantiate the object and start the scanning
let myFooDevice = FooDevice()
myFooDevice.start()
}
}
In the console I could see how the scanner start and found the bluetooth device but the notification isn't captured and the log isn't printed. Also the notification names are right, I'm sure because the SDK return the strings.
I don't know what I'm missing. Hope you could throw some light to it.
Your problem is that ARC will cleanup your myFooDevice variable before any notification can reach it.
You better store it in a property:
class MyViewController:UIViewController {
var myFooDevice:FooDevice?
override viewDidLoad() {
// ViewDidLoad stuff
}
#IBAction func connectToDevice(_ sender: Any) {
// Here I instantiate the object and start the scanning
myFooDevice = FooDevice()
myFooDevice!.start()
}
}

Swift 3 Xcode: How to display battery levels as an integer?

I am making an app to read battery percentage using Swift!
Right now my out is something like this:
61.0% or 24.0% or 89.0%
What I'm trying to fix is getting rid of the .0 so it's an Int.
This is my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var infoLabel: UILabel!
var batteryLevel: Float {
return UIDevice.current.batteryLevel
}
var timer = Timer()
func scheduledTimerWithTimeInterval(){
timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(self.someFunction), userInfo: nil, repeats: true)
}
func someFunction() {
self.infoLabel.text = "\(batteryLevel * 100)%"
}
override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.isBatteryMonitoringEnabled = true
someFunction()
scheduledTimerWithTimeInterval()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I have tried something like this:
var realBatteryLevel = Int(batteryLevel)
However, I get this error
I have tried other method but none with any luck. Please, any solutions would be awesome! Thanks in advance!
EDIT
I was considering making the float batteryLevel into a String and then replacing ".0" with "" and I have seen this somewhere, however, I'm not sure how!
Try this instead:
func someFunction() {
self.infoLabel.text = String(format: "%.0f%%", batteryLevel * 100)
}
For future reference, all string format specifiers are listed here.
You just need to convert It inside your function :
func someFunction() {
self.infoLabel.text = "\(Int(batteryLevel * 100))%" }
Alternately, you could create an Int computed property for batteryLevel:
var batteryLevel: Int {
return Int(round(UIDevice.current.batteryLevel * 100))
}
Note that you might not be able to get the battery level. You should test for that and display a different string:
if UIDevice.current.batteryState == .unknown {
self.batteryLevelLabel.text = "n/a"
} else {
self.batteryLevelLabel.text = "\(self.batteryLevel)%"
}
Also note that rather than running a timer to fetch the battery level, you should subscribe to the .UIDeviceBatteryLevelDidChange notification. The "meat" of a view controller that handles all of this might look as follows:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var batteryLevelLabel: UILabel!
///Holds the notification handler for battery notifications.
var batteryNotificationHandler: Any?
///A computed property that returns the battery level as an int, using rounding.
var batteryLevel: Int {
return Int(round(UIDevice.current.batteryLevel * 100))
}
///A function to display the current battery level to a label,
////or the string "n/a" if the battery level can't be determined.
func showBatteryLevel() {
if UIDevice.current.batteryState == .unknown {
self.batteryLevelLabel.text = "n/a"
} else {
self.batteryLevelLabel.text = "\(self.batteryLevel)%"
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
///If we have a battery level observer, remove it since we're about to disappear
if let observer = batteryNotificationHandler {
NotificationCenter.default.removeObserver(observer: observer)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
showBatteryLevel() //display the battery level once as soon as we appear
//Create a notifiation handler for .UIDeviceBatteryLevelDidChange
//notifications that calls showBatteryLevel()
batteryNotificationHandler =
NotificationCenter.default.addObserver(forName: .UIDeviceBatteryLevelDidChange,
object: nil,
queue: nil, using: {
(Notification) in
self.showBatteryLevel()
})
}
override func viewDidLoad() {
super.viewDidLoad()
//Tell UIDevice that we want battery level notifications
UIDevice.current.isBatteryMonitoringEnabled = true
}
}

Notify user reachability (Ashley Mills' Reachability)

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.

Resources