Hide action buttons on Lock Screen Mode - iOS Notification Swift 4.2 - swift

I want to hide these action buttons during Lock Screen Mode.
Is there a way to detect that in iOS ?
if action == "allow.action" {
APIService.shared.updateCpeDeviceACL(cpe: cpe,vlan: vlan, device: deviceMac ?? "", portalUrl: "", acl: true, caller: self)
if(alertId != nil){
APIService.shared.deleteAlert(id: alertId ?? "", caller: self)
} else {
print("alertId = nil detetected !")
}
if(notificationType != "new-device"){
if(quarantineId != nil){
APIService.shared.allowDeviceToNetwork(id: quarantineId ?? "", caller: self)
} else {
print("quarantineId = nil detetected !")
}
}
} else if action == "delete.action" {
APIService.shared.deleteAlert(id: alertId ?? "", caller: self)
} else if action == "block.action" {
APIService.shared.updateCpeDeviceACL(cpe: cpe,vlan: vlan, device: deviceMac ?? "", portalUrl: "", acl: false, caller: self)
if(alertId != nil){
APIService.shared.deleteAlert(id: alertId ?? "", caller: self)
} else {
print("alertId = nil detetected !")
}
if(notificationType != "new-device"){
if(quarantineId != nil){
APIService.shared.denyDeviceToNetwork(id: quarantineId ?? "", caller: self)
} else {
print("quarantineId = nil detetected !")
}
}
} else {
awakeFromNotification = true
}
How would one go about debugging this further?

I guess you are referring to actionable notifications? If so somewhere in your code you should be able to see:
UNUserNotificationCenter.current().setNotificationCategories([someCategory])
When the someCategory (whatever name used in your app) was created it should receive those actions as a parameter. You can modify them there.

Related

Only one pin appears on the map - Swift

The user is pinning more than one on the map. I just want the last pin to be displayed, can you help with this?
#objc func selectPin(mapGesture : UILongPressGestureRecognizer) {
if mapGesture.state == .began {
let touchPoint = mapGesture.location(in: self.mapview)
let touchCoordinates = self.mapview.convert(touchPoint, toCoordinateFrom: self.mapview)
choosenLatitude = touchCoordinates.latitude
choosenLongtitude = touchCoordinates.longitude
let annotation = MKPointAnnotation()
annotation.coordinate = touchCoordinates
annotation.title = forWhatText.text
if forWhatText.text == "" {
makeAlert(titleInput: "Error", messageInput: "Please fill in all the fields above!")
} else if phoneName.text == "" {
makeAlert(titleInput: "Error", messageInput: "Please fill in all the fields above!")
} else if phoneNumber.text == "" {
makeAlert(titleInput: "Error", messageInput: "Please fill in all the fields above!")
} else if messageText.text == "" {
makeAlert(titleInput: "Error", messageInput: "Please fill in all the fields above!")
} else {
mapview.addAnnotation(annotation)
//saveButton.isEnabled = true
}
}
}
Instead of adding pin in mapGesture.state == .began use mapGesture.state == .ended

Swift crash when player gets notification for their turn

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file HATurnBasedMatchHelper.swift, line 244
2021-01-25 20:36:22.317564-0500 QuizApp[14288:4507393] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file HATurnBasedMatchHelper.swift, line 244
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
// Crash on Line 244
// state of self when using notification
/*
self QuizApp.HATurnBasedMatchHelper 0x0000000281a4c310
ObjectiveC.NSObject NSObject
reachability QuizApp.Reachability? 0x0000000281a4c7e0
saveToLoseList Bool? false some
isMultiplayerGame Bool? false some
currentMatch GKTurnBasedMatch? 0x0000000283e29530
selectedCategory QuizApp.HACategory? nil none
delegate QuizApp.HATurnbasedMatchHelperDelegate? nil none
presentingViewController UIViewController? nil none
myWins Int64 1
pendingWins Int64 0
takeAnotherChallenge Bool? false some
*/
/* I added the if condition to check if the VC is nil */
if self.presentingViewController != nil {
self.presentingViewController.dismiss(animated: true, completion: nil)
}
self.currentMatch = match
let firstParticipant = match.participants[0]
if firstParticipant.lastTurnDate == nil
{
delegate.enterNewGame(match: match)
}
else{
var statusString : String? = nil
var thisParticipant : GKTurnBasedParticipant? = nil
var otherParticipant : GKTurnBasedParticipant? = nil
for participant in match.participants
{
if participant.player?.playerID == GKLocalPlayer.local.playerID
{
thisParticipant = participant
}
else{
otherParticipant = participant
}
}
if thisParticipant?.matchOutcome == .quit {
statusString = "You have Quit this match"
}
else if otherParticipant?.matchOutcome == .quit
{
statusString = "Opponent has Quit this match"
}
else if otherParticipant?.status == .declined
{
statusString = "Opponent has Declined your challenge"
}
if statusString == nil{
if match.currentParticipant?.player?.playerID == GKLocalPlayer.local.playerID
{
// crash here
// delegat is nil. I posted all values of self above
delegate.takeTurn(match: match)
}
else{
delegate.layout(match: match)
}
}
else{
let alertController = UIAlertController(title: "Match status\n", message:statusString , preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default) { (action) in
}
alertController.addAction(okAction)
self.presentingViewController.present(alertController, animated: true, completion: nil)
}
}
}
I am not sure what I am doing wrong here.
The exact error is
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

List all available audio devices

I want to list all available audio devices in swift to provide a selection for input and output. My application should listen on a audio channel and "write" to another. I do not want the system default!
let devices = AVCaptureDevice.devices(for: .audio)
print(devices.count)
for device in devices {
print(device.localizedName)
}
The Code lists 0 devices. But I expect at least the internal output.
Some links to CoreAudio, AudioToolbox and AVFoundation that explain the audio source selection would be nice.
Here's some Swift 5 code that will enumerate all the audio devices.
You can use the uid with AVAudioPlayer's currentDevice property to output to a specific device.
import Cocoa
import AVFoundation
class AudioDevice {
var audioDeviceID:AudioDeviceID
init(deviceID:AudioDeviceID) {
self.audioDeviceID = deviceID
}
var hasOutput: Bool {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyStreamConfiguration),
mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement:0)
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size);
var result:OSStatus = AudioObjectGetPropertyDataSize(self.audioDeviceID, &address, 0, nil, &propsize);
if (result != 0) {
return false;
}
let bufferList = UnsafeMutablePointer<AudioBufferList>.allocate(capacity:Int(propsize))
result = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, bufferList);
if (result != 0) {
return false
}
let buffers = UnsafeMutableAudioBufferListPointer(bufferList)
for bufferNum in 0..<buffers.count {
if buffers[bufferNum].mNumberChannels > 0 {
return true
}
}
return false
}
}
var uid:String? {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var name:CFString? = nil
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
if (result != 0) {
return nil
}
return name as String?
}
}
var name:String? {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var name:CFString? = nil
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
if (result != 0) {
return nil
}
return name as String?
}
}
}
class AudioDeviceFinder {
static func findDevices() {
var propsize:UInt32 = 0
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioHardwarePropertyDevices),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var result:OSStatus = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &address, UInt32(MemoryLayout<AudioObjectPropertyAddress>.size), nil, &propsize)
if (result != 0) {
print("Error \(result) from AudioObjectGetPropertyDataSize")
return
}
let numDevices = Int(propsize / UInt32(MemoryLayout<AudioDeviceID>.size))
var devids = [AudioDeviceID]()
for _ in 0..<numDevices {
devids.append(AudioDeviceID())
}
result = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propsize, &devids);
if (result != 0) {
print("Error \(result) from AudioObjectGetPropertyData")
return
}
for i in 0..<numDevices {
let audioDevice = AudioDevice(deviceID:devids[i])
if (audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
}
}
}
The code you posted works perfectly fine for audio input devices when I paste it into an Xcode Playground.
Note, however, that AVCaptureDevice API does not list audio output devices as they are no capture devices but playback devices. If a device supports both, input and output, you can still use the device's uniqueID in an output context, for example with AVPlayer's audioOutputDeviceUniqueID.
(Also note, that if you want your code to work on iOS as well, devices(for:) is marked as deprecated since iOS 11 and you should move to AVCaptureDevice.DiscoverySession instead.)
Regarding your request for additional info on Core Audio and AudioToolbox, this SO question has some pretty comprehensive answers on the matter. The question asks for input devices but the answers provide enough context to let you understand handling of the output side as well. There's even an answer with some (dated) Swift code. On a personal note I have to say calling Core Audio API from Swift is oftentimes more pain than gain. Because of that it might be faster, although a bit unsafer, wrapping those portions of code into Objective-C or plain C and exposing them via the Swift bridging header, if your project allows it.
If you want something like a actionSheet and need to switch between audio devices seamlessly. Use this code.
Code
import Foundation
import AVFoundation
import UIKit
#objc class AudioDeviceHandler: NSObject {
#objc static let shared = AudioDeviceHandler()
/// Present audio device selection alert
/// - Parameters:
/// - presenterViewController: viewController where the alert need to present
/// - sourceView: alertController source view in case of iPad
#objc func presentAudioOutput(_ presenterViewController : UIViewController, _ sourceView: UIView) {
let speakerTitle = "Speaker"
let headphoneTitle = "Headphones"
let deviceTitle = (UIDevice.current.userInterfaceIdiom == .pad) ? "iPad" : "iPhone"
let cancelTitle = "Cancel"
var deviceAction = UIAlertAction()
var headphonesExist = false
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
guard let availableInputs = AVAudioSession.sharedInstance().availableInputs else {
print("No inputs available ")
return
}
for audioPort in availableInputs {
switch audioPort.portType {
case .bluetoothA2DP, .bluetoothHFP, .bluetoothLE :
let bluetoothAction = UIAlertAction(title: audioPort.portName, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: audioPort.portType) {
bluetoothAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(bluetoothAction)
case .builtInMic, .builtInReceiver:
deviceAction = UIAlertAction(title: deviceTitle, style: .default, handler: { _ in
self.setToDevice(port: audioPort)
})
case .headphones, .headsetMic:
headphonesExist = true
let headphoneAction = UIAlertAction(title: headphoneTitle, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: .headphones) || isCurrentOutput(portType: .headsetMic) {
headphoneAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(headphoneAction)
case .carAudio:
let carAction = UIAlertAction(title: audioPort.portName, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: audioPort.portType) {
carAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(carAction)
default:
break
}
}
// device actions only required if no headphone available
if !headphonesExist {
if (isCurrentOutput(portType: .builtInReceiver) ||
isCurrentOutput(portType: .builtInMic)) {
deviceAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(deviceAction)
}
// configure speaker action
let speakerAction = UIAlertAction(title: speakerTitle, style: .default) { _ in
self.setOutputToSpeaker()
}
if isCurrentOutput(portType: .builtInSpeaker) {
speakerAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(speakerAction)
// configure cancel action
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel)
optionMenu.addAction(cancelAction)
optionMenu.modalPresentationStyle = .popover
if let presenter = optionMenu.popoverPresentationController {
presenter.sourceView = sourceView
presenter.sourceRect = sourceView.bounds
}
presenterViewController.present(optionMenu, animated: true, completion: nil)
// auto dismiss after 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
optionMenu.dismiss(animated: true, completion: nil)
}
}
#objc func setOutputToSpeaker() {
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch let error as NSError {
print("audioSession error turning on speaker: \(error.localizedDescription)")
}
}
fileprivate func setPreferredInput(port: AVAudioSessionPortDescription) {
do {
try AVAudioSession.sharedInstance().setPreferredInput(port)
} catch let error as NSError {
print("audioSession error change to input: \(port.portName) with error: \(error.localizedDescription)")
}
}
fileprivate func setToDevice(port: AVAudioSessionPortDescription) {
do {
// remove speaker if needed
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.none)
// set new input
try AVAudioSession.sharedInstance().setPreferredInput(port)
} catch let error as NSError {
print("audioSession error change to input: \(AVAudioSession.PortOverride.none.rawValue) with error: \(error.localizedDescription)")
}
}
#objc func isCurrentOutput(portType: AVAudioSession.Port) -> Bool {
AVAudioSession.sharedInstance().currentRoute.outputs.contains(where: { $0.portType == portType })
}
}
How to use
class ViewController: UIViewController {
#IBOutlet weak var audioButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func selectAudio(_ sender: Any) {
// present audio device selection action sheet
AudioDeviceHandler.shared.presentAudioOutput(self, audioButton)
}
}
Result
It is possible to list input and output devices. This is a simplification of stevex's answer.
For output devices:
if (audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
For input devices:
if (!audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
(Notice the ! before audioDevice.hasOutput.)

Confusion about setting a optional variable

I want to add the option for a user to add their phone number. If they add any phone number I want to add an alert informing them if they have not added a valid 10 digit phone number. However if they do not add anything in the phone number field I want the phoneInput variable to be set to "0". How would I go about doing this.
var phoneInput = ""
func signUp(){
if profileImage.image == nil {
showAvatarError()
} else if phoneNumber.text == "" {
self.phoneInput = "0"
} else if (phoneNumber.text?.characters.count)! != 10 {
showphoneNumberError()
}else if email.text == "" {
showEmailError()
}else if isValid(email.text!) != true{
showEmailError()
} else{
submitPressed()
print("Set info")
}
}
I'm not sure why you get the result that you do but here is a cleaner version
var phoneInput = ""
func signUp(){
// This check doesn't have anything to do with the number, so separe it
if profileImage.image == nil {
showAvatarError()
return
}
guard let temp = planEndValue.text else {
return
}
let userInput = temp.trimmingCharacters(in: .whitespaces)
if userInput.count == 0 {
self.phoneInput = "0"
} else if userInput.count != 10 {
showphoneNumberError()
}
}

How do you update a users profile settings using firebase and swift?

I am trying to update a users email and full name. This is my code:
func saveTapped() {
var performSegue = false
if updateEmail.text == "" && updateFullName.text == "" {
self.cleanUrCodeRohan("Please fill in one or more of the missing text fields that you would like to update.")
}
if updateEmail.text != "" {
let user = FIRAuth.auth()?.currentUser
user?.updateEmail(updateEmail.text!) { error in
self.ref.child("users").child(self.currentUser).child("email").setValue(self.updateEmail.text!)
}
let emailUpdateRef = FIRDatabase.database().reference().child(currentUser).child("email")
print(emailUpdateRef)
emailUpdateRef.setValue(self.updateEmail.text)
performSegue = true
}
if updateFullName.text != "" {
let user = FIRAuth.auth()?.currentUser
if let user = user {
let changeRequest = user.profileChangeRequest()
changeRequest.displayName = self.updateFullName.text!
}
performSegue = true
}
if performSegue == true {
self.navigationController!.popViewControllerAnimated(true)
}
}
I am able to update the email under authorization but not under the database. Any help would be appreciated.
If JSON tree is something like this:-
appName{
users :{
userID :{
email : "..",
username : ".."
}
}
}
Use this Code to update your node's child value's:-
func saveTapped(){
if ((updateEmail.text != "" || updateFullName.text != "") && (updateEmail.text != nil || updateFullName.text != nil)){
let userRef = FIRDatabase.database().reference().child("users").child(FIRAuth.auth()!.currentUser!.uid)
if let new_Email = updateEmail.text as? String{
FIRAuth.auth()!.currentUser!.updateEmail(updateEmail.text!) { error in
if error == nil{
userRef.updateChildValues(["email" : new_Email ], withCompletionBlock: {(errEM, referenceEM) in
if errEM == nil{
print(referenceEM)
}else{
print(errEM?.localizedDescription)
}
})
}
}else{
self.cleanUrCodeRohan("Email couldn't be updated in auth")
}
}
if let new_Name = updateFullName.text as? String{
userRef.updateChildValues(["username" : new_Name ], withCompletionBlock: {(errNM, referenceNM) in
if errNM == nil{
print(referenceNM)
}else{
print(errNM?.localizedDescription)
}
})
}
}else{
self.cleanUrCodeRohan("Please fill in one or more of the missing text fields that you would like to update.")
}
}