Detect when iPhone is being unplugged and plugged in - swift

I am just trying to write simple code that detects when the iPhone is unplugged and then plugged in. But being event-driven, instead of using a while loop. This code does work, but doesn't detect if the phone is unplugged and will not update the printed text.
EDIT
In the long run I just want the phone to play a sound whenever the iPhone is unplugged. (Sorry for leaving the end goal out.). I was using this print statements just so I can make sure it was working.
func startCharger()
{
printscreen.text="Started charger protocol"
UIDevice.current.isBatteryMonitoringEnabled=true
if(UIDevice.current.batteryState == .unplugged)
{
printscreen.text="Battery is unplugged"
}
else
{
printscreen.text="Battery is plugged in"
}
}

Quick implementation of Dule' code.
Check if the power is connected.
Copy these functions into your class.
Call isPowerConnected() in the viewDidLoad
func isPowerConnected(){
UIDevice.current.isBatteryMonitoringEnabled = true
NotificationCenter.default.addObserver(
self,
selector: #selector(batteryStateChanged),
name: .UIDeviceBatteryStateDidChange,
object: nil)
}
var chargingVar = "" //Empty Variable
func batteryStateChanged(){
if (UIDevice.current.batteryState == .charging) { //Here we check if the device is charging
UIApplication.shared.isIdleTimerDisabled = true //Here we disable the lock screen time out value
self.chargingVar = "is charging. \u{1F603}" //Here we change the variable to "is charging" 😀
chargingAlaer() //If power is pluged in we send an Alert
}else{
self.chargingVar = "is not charging. \u{1F622} " //Here we change the variable to "is not charging" 😥
chargingAlaer() //If power is not pluged we send an Alert
}
}
func chargingAlaer(){
let alertController = UIAlertController(title: "Charging Status",
message: "Your device \(chargingVar)",
preferredStyle: UIAlertControllerStyle.alert)
let ok = UIAlertAction(title: "OK",
style: UIAlertActionStyle.default,
handler: {(action) -> Void in
})
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}

You can get a notification on battery state change with UIDeviceBatteryStateDidChange, the code looking something like this:
NotificationCenter.default.addObserver(
self,
selector: #selector(batteryStateChanged),
name: .UIDeviceBatteryStateDidChange,
object: nil)
Of course, you need a method in ViewController (or wherever you want to get notification). In this example:
func batteryStateChanged(){
// Your logic
}

Related

How to filter the images shown on the PHPickerViewController to those selected for limited access

How do I filter the images shown on the PHPickerViewController to those that have been selected under limited access by the user? Or do I need to use a different picker? I've been struggling with this for a few days now. Any help would be appreciated. Thanks in advance.
When user taps the button:
1-5 are automatic
The alert appears with Camera or Photo Library
They choose Photo Library
The authorization alert appears with Select Photo…, Allow Access to All Photos or Don’t Allow
They tap Select Photos = .limited
The presentLimitedLibraryPicker is displayed, for the user to choose the photos thay want to allow and taps Done.
Now I want the picker to appear with a filtered choice of the images the user has just chosen. Seems like this would be automatic too. Not...
This only displays the same picker where the user made the selections for limited access.
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
What goes in the .limited case?
var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = PHPickerFilter.any(of: [.images, .livePhotos])
let picker_Photo = PHPickerViewController(configuration: config)
picker_Photo.delegate = self
let libCell = UIAction(title: "Photo Library", image: UIImage(systemName: "photo"), identifier: .none, discoverabilityTitle: .none) { (libAction) in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary)
{
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
switch status
{
case .limited:
DispatchQueue.main.async
{
// PHPhotoLibrary.shared().register(self)
// PHPhotoLibrary.shared().presentLimitedLibraryPicker
self.present(picker_Photo, animated: true, completion: nil)
}
case .authorized:
DispatchQueue.main.async
{
self.present(picker_Photo, animated: true, completion: nil)
}
case .notDetermined:
DispatchQueue.main.async
{
self.present(picker_Photo, animated: true, completion: nil)
}
case .restricted:
self.view.sendConfirmationAlert(theTitle: "Photo Library Restricted",
theMessage: "Photo Library access was previously denied. Please update your Settings to allow access.", buttonTitle: "Ok")
case .denied:
let settingsAlert = UIAlertController(title: "Photo Library Access Denied",
message: "Photo Library access was previously denied. Please update your Settings to allow access.", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Go to Settings", style: .default) { ( action ) in
let settingsUrl = URL(string: UIApplication.openSettingsURLString)
UIApplication.shared.open(settingsUrl!, options: [:])
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
DispatchQueue.main.async
{
settingsAlert.addAction(settingsAction)
settingsAlert.addAction(cancelAction)
self.present(settingsAlert, animated: true)
}
default:
return
}
}
}
}
You are mistaken about what limited access means. It restricts what information you can read and write into the photo library.
But your code does not read from the photo library in the first place. You are saying
config = PHPickerConfiguration()
So this means you have no photo library access at all by way of the picker. You can only get image copies. You therefore do not need any kind of permission to show the picker, and the user limitation makes no difference to you. You can in fact just throw away all your authorization related code; it has no effect. Your picker will work exactly the same without it.
If you needed photo library access by way of the picker, you would have called
https://developer.apple.com/documentation/photokit/phpickerconfiguration/3616114-init
In that case the user limitation would matter to you — when you tried to access an actual PHAsset. But your code never does that. You are asking for a permission you do not need.

Login and Logout authentication button to change from 'Login' to 'Logout' on main view controller, looking for best practices

first of all.... I am new to developing in swift 4.0 / Xcode 10.1. the design problem I am having is that I have created a login page with firebase authentication (which works) and I am currently trying to find out the best practices to set this up so that my 'login' button located in the top right of the page changes to 'logout' once the firebase authentication process has been for-filled. I have attached an image which should help. I can't seem to find any documentation on best practices for this type of thing...
my guess would be that I setup a second view controller but 'protected' once the user has been authenticated. however, im asking this question in case there is an alternate / better way to complete this which I am not aware of?
#IBAction func login(_ sender: UIBarButtonItem) {
if Auth.auth().currentUser != nil {
} else {
let authUI = FUIAuth.defaultAuthUI()
authUI!.delegate = self
let providers: [FUIAuthProvider] = [
FUIGoogleAuth()]
authUI?.providers = providers
let authViewController = authUI!.authViewController()
self.present(authViewController, animated: true, completion: nil)
}
do {
try Auth.auth().signOut()
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
// added variable for login button as a boolean value and then added button for values if login is true and false. Im now looking for assistance on how to then call the login function above. apologies I am very new to swift 4.0. any assistance is greatly appreciated
#IBAction func pressedNavButtonRight(sender: UIBarButtonItem) {
if isLogin
{isLogin = false
self.navigationItem.rightBarButtonItem! = UIBarButtonItem(title:
"Logout", style: UIBarButtonItemStyle.done, target: self,
action:"pressedNavButtonRight")
}
else
{
isLogin = true
self.navigationItem.rightBarButtonItem! = UIBarButtonItem(title:
"Login", style: UIBarButtonItemStyle.plain, target: self,
action:"pressedNavButtonRight")
}
}
This is resolved, I figured it out. I was able to use a Boolean value to get the desired results. I might post the code later for anyone that is interested

Capture self was never used

I don't post the entire code because it's really too large. Anyway, it's a user registration built this way.
There is a modal whose child is a pageViewController:
the first page is for Login;
the second page is for Registration;
From Login you can reach Registration and, once registered, pageViewController should automatically transition to the first page, so that the user can login with his new credentials.
The process of user registration is managed by a button and Alamofire: once the button is clicked, the values the user inserted in the textfields are validated, then I start a post request, sending data to the server and receiving JSON data back in a while.
It's anything very simple (sorry for speaking too much), but in the end, after I receive the JSON, something strange happens here:
let j = JSON as! NSDictionary
let userStatus = j.object(forKey: "status")!
if((userStatus as! Int) == 0){
//
print("user registerd")
let alert = UIAlertController(title: "Registration ok", message: "Registration OK", preferredStyle:UIAlertControllerStyle.alert)
let okAlert = UIAlertAction(title:"OK", style: .default, handler: {action in v.scrollToPreviousViewController() })
alert.addAction(okAlert)
self.present(alert, animated: true, completion: nil)
What is supposed to do this code? Once the user clicks the alert button, the pageviewcontroller should return with an animation to the login screen.
What happens? It returns back there but without animation.
This led me to think I should avoid "polluting" the global thread reserved to the GUI. And, in fact, I tried to put all the code inside a:
DispatchQueue.main.async { [unowned self] in
without any success.
Then, I tried another thing:
let okAlert = UIAlertAction(title:"OK", style: .default, handler:
{action in DispatchQueue.main.async { [unowned self] in v.scrollToPreviousViewController() }})
I don't perfectly understand why, but this practically works and eliminates the issue with the animation.
But that unowned self there generates a warning: "Capture self was never used". What am I doing wrong?
If you didn't use self inside the async block then you will get this warning obviously. Please try with _ instead of self.
to see what is swift capture list for, check this example ...
var i = 25
var cl = {print(i)}
var clc = {[i] in print(i)}
i = 35
cl() // 35
clc() // 25
the warning message "Capture self was never used" is therefore self-explanatory. you don't need to put self in your capture list, because you don't use it there

Is there anyway to display UIAlertController ONLY when app launches?

so I have the following code in the viewDidAppear section
let theAlert = UIAlertController(title: "SUP", message: "DAWG", preferredStyle: UIAlertControllerStyle.Alert)
theAlert.addAction(UIAlertAction(title: "sup!", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(theAlert, animated: true, completion: nil)
Don't mind the messages, I just came up with them randomly :3
Okay, so is there anyway for me to ONLY display this message when the app launches? Because when I come back from another controller, this message pops up again.
Set a flag to indicate if the message has shown or not.
// first check to see if the flag is set
if alertShown == false {
// show the alert
alertShown = true
}
For this behavior to persist through launches, and show only on FIRST launch, save to NSUserDefaults.
// when your app loads, check the NSUserDefaults for your saved value
let userDefaults = NSUserDefaults.standardUserDefaults()
let alertShown = userDefaults.valueForKey("alertShown")
if alertShown == nil {
// if the alertShown key is not found, no key has been set.
// show the alert.
userDefaults.setValue(true, forKey: "alertShown")
}
You can handle both of these in the root view controller viewDidLoad.

Swift - AVAudioPlayer, sound doesn't play correctly

Because UILocalNotification is not being displayed while the application is in active stat, I'm trying to configure an UIAlertController and playing a little sound when it appears.
I've no problem, in the AppDelegate, to handle the notification/create the alert. My problem concerns the sound. Indeed, it doesn't play correctly.
Here is what I have so far :
//...
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Notifications permissions
let types: UIUserNotificationType = UIUserNotificationType.Sound | UIUserNotificationType.Alert
let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
return true
}
func application(application: UIApplication!, didReceiveLocalNotification notification: UILocalNotification!) {
let state : UIApplicationState = application.applicationState
var audioPlayer = AVAudioPlayer()
if (state == UIApplicationState.Active) {
// Create sound
var error:NSError?
var audioPlayer = AVAudioPlayer()
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
let soundURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("sound", ofType: "wav")!)
audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: &error)
if (error != nil) {
println("There was an error: \(error)")
} else {
audioPlayer.prepareToPlay()
audioPlayer.play()
}
// Create alert
let alertController = UIAlertController(title: "Alert title", message: "Alert message.", preferredStyle: .Alert)
let noAction = UIAlertAction(title: "No", style: .Cancel) { (action) in
// ...
}
let yesAction = UIAlertAction(title: "Yes", style: .Default) { (action) in
// ...
}
alertController.addAction(noAction)
alertController.addAction(yesAction)
self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
}
With that, when the player go through this line : audioPlayer.play()
It only play for less then a second. Like if it was suddently deallocated maybe (?).
I tried the two things below :
Switching back the AVAudioPlayer status to inactive: AVAudioSession.sharedInstance().setActive(false, error: nil) just before the alert creation (or just after showing it). If I do that, the sound is played correctly. But, this method is a synchronous (blocking) operation, so it delays other thing (alert being shown after the sound). Apparently not a good solution.
Move the audioPlayer property (var audioPlayer = AVAudioPlayer()) to the class level, just under the window (var window: UIWindow?). If I do that, the sound is played correctly and the alert is shown correctly too.
I don't understand why it works like that. Am I missing something? Is it the proper way to solve my problem?
Thanks in advance to everyone who could help me understand/fixing this.
You already provided the correct solution to your problem and that would be #2; to have a class-level audioPlayer property. Why is this so?
Well, that's because in the code you setup the player, start the playback, show the pop-up and after everything is done, the method exits its scope. The automated memory management (ARC) works so that any local variables get released when you exit the scope of that variable. If you consider that sound playback is asynchronous (eg. it will play without blocking the main thread), you exit the scope before the audio player is finished playing the sound. ARC notices that audioPlayer is a local variable and releases it at that point, but the sound is not done playing yet.
I hope I was clear enough, if not feel free to ask more!