Swift CMMotionActivityManager not displaying correct authorization Status - ios

I am trying to create a very simple step counter and am following Kamil Wysocki's article at https://brightinventions.pl/blog/coremotion-pedometer-swift/. However, my .isActivityAvailable() is returning False preventing any of my other functions from initiating. Also, my CMMotionActivityManager.authorizationStatus() call is returning 3 (denied). I cannot for the life of me figure out why this is happening.
I have added Motion Usage Description to my info.plist with a description and have enabled authorization on my iPhone simulator. I have also tried simulating the "City walk" mode in the debug menu, which doesn't help. My code is below.
import UIKit
import CoreMotion
import CoreLocation
class ViewController: UIViewController {
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var activityTypeLabel: UILabel!
#IBOutlet weak var stepsCountLabel: UILabel!
private let activityManager = CMMotionActivityManager()
private let pedometer = CMPedometer()
private var shouldStartUpdating: Bool = false
private var startDate: Date? = nil
override func viewDidLoad() {
super.viewDidLoad()
startButton.addTarget(self, action: #selector(didTapStartButton), for: .touchUpInside)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let startDate = startDate else { return }
updateStepsCountLabelUsing(startDate: startDate)
}
#objc private func didTapStartButton() {
shouldStartUpdating = !shouldStartUpdating
shouldStartUpdating ? (onStart()) : (onStop())
}
}
extension ViewController {
private func onStart() {
startButton.setTitle("Stop", for: .normal)
startDate = Date()
checkAuthorizationStatus()
startUpdating()
}
private func onStop() {
startButton.setTitle("Start", for: .normal)
startDate = nil
stopUpdating()
}
private func startUpdating() {
if CMMotionActivityManager.isActivityAvailable() {
startTrackingActivityType()
} else {
activityTypeLabel.text = "Not available"
}
if CMPedometer.isStepCountingAvailable() {
startCountingSteps()
} else {
stepsCountLabel.text = "Not available"
}
}
private func checkAuthorizationStatus() {
switch CMMotionActivityManager.authorizationStatus() {
case CMAuthorizationStatus.denied:
onStop()
activityTypeLabel.text = "Not available"
stepsCountLabel.text = "Not available"
default:break
}
}
private func stopUpdating() {
activityManager.stopActivityUpdates()
pedometer.stopUpdates()
pedometer.stopEventUpdates()
}
private func on(error: Error) {
//handle error
}
private func updateStepsCountLabelUsing(startDate: Date) {
pedometer.queryPedometerData(from: startDate, to: Date()) {
[weak self] pedometerData, error in
if let error = error {
self?.on(error: error)
} else if let pedometerData = pedometerData {
DispatchQueue.main.async {
self?.stepsCountLabel.text = String(describing: pedometerData.numberOfSteps)
}
}
}
}
private func startTrackingActivityType() {
activityManager.startActivityUpdates(to: OperationQueue.main) {
[weak self] (activity: CMMotionActivity?) in
guard let activity = activity else { return }
DispatchQueue.main.async {
if activity.walking {
self?.activityTypeLabel.text = "Walking"
} else if activity.stationary {
self?.activityTypeLabel.text = "Stationary"
} else if activity.running {
self?.activityTypeLabel.text = "Running"
} else if activity.automotive {
self?.activityTypeLabel.text = "Automotive"
}
}
}
}
private func startCountingSteps() {
pedometer.startUpdates(from: Date()) {
[weak self] pedometerData, error in
guard let pedometerData = pedometerData, error == nil else { return }
DispatchQueue.main.async {
self?.stepsCountLabel.text = pedometerData.numberOfSteps.stringValue
}
}
}
}
Here is my viewController upon running and clicking start
Big thanks in advance for anyone who can offer any knowledge!!!

Use this code to check authorization status :
let manager = CMMotionActivityManager()
let today = Date()
manager.queryActivityStarting(from: today, to: today, to: OperationQueue.main, withHandler: { (activities: [CMMotionActivity]?, error: Error?) -> () in
if error != nil {
let errorCode = (error! as NSError).code
if errorCode == Int(CMErrorMotionActivityNotAuthorized.rawValue) {
print("NotAuthorized")
}
} else {
print("Authorized")
//Start Tracking Activity
}
manager.stopActivityUpdates()
})
Hope this will work for you.

Related

How to get the registered user Notification API data come for guest user too?

Current Scenario?:
Th registered user of the book app gets the notifications such as 'books available for download', q&A portal answers, live streaming link.
What is the goal?:
To get the notifications for guest where it can show the available books for download, and other notification that comes for registered user.
What is the issue/errors?:
When user clicks on the notification button , nothing comes on the screen. Everything blank and no notification is shown. It show the below error::
What i tried?:
When we call API, the token is generated from the backend. This token is then used to get the access. I tried to copy the API method to the home-screen that is used from 'signUpVC'(sign up view controller) to get the token bit showing above error.
Admin user notification looks as below:
Guest user (notification from our android app)
Code: for SignUp
import UIKit
import PKHUD
import SDWebImage
class SignupVC: ThemeController {
// MARK: - Outlets
#IBOutlet weak var imgProfile: TappableImageView!
#IBOutlet weak var passwordView: UIStackView!
#IBOutlet weak var lblRegister: UILabel!
var isFromUpdateProfile = Bool()
// -----------------------------------------------------------------------------------------------
// MARK: - Class Properties
#IBOutlet weak var txtFirstName: UITextField!
#IBOutlet weak var txtLastName: UITextField!
#IBOutlet weak var txtEmail: UITextField!
#IBOutlet weak var txtCity: UITextField!
#IBOutlet weak var txtPassword: UITextField!
// -----------------------------------------------------------------------------------------------
// MARK: - Memory Management Functions
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit {
}
// -----------------------------------------------------------------------------------------------
// MARK: - Class Functions
private func errorsInTextFields() -> String? {
self.view.endEditing(true)
guard !txtFirstName.isEmpty else { return UserMessages.kBlankFirstName }
guard txtFirstName.hasValid(.alphabetWithSpace) else { return UserMessages.kValidFirstName }
guard !txtLastName.isEmpty else { return UserMessages.kBlankLastName }
guard txtLastName.hasValid(.alphabetWithSpace) else { return UserMessages.kValidLastName }
guard !txtEmail.isEmpty else { return UserMessages.kBlankEmail }
guard txtEmail.hasValid(.email) else { return UserMessages.kValidEmail }
guard !txtCity.isEmpty else { return UserMessages.kBlankCity }
guard txtCity.hasValid(.alphabetWithSpace) else { return UserMessages.kValidCity }
guard !txtPassword.isEmpty else { return UserMessages.kBlankPassword }
//guard txtPassword.hasValid(.password) else { return UserMessages.kValidPassword }
// No Errors
return nil
}
private func errorsInEditProfileTextFields() -> String? {
self.view.endEditing(true)
guard !txtFirstName.isEmpty else { return UserMessages.kBlankFirstName }
guard txtFirstName.hasValid(.alphabetWithSpace) else { return UserMessages.kValidFirstName }
guard !txtLastName.isEmpty else { return UserMessages.kBlankLastName }
guard txtLastName.hasValid(.alphabetWithSpace) else { return UserMessages.kValidLastName }
guard !txtEmail.isEmpty else { return UserMessages.kBlankEmail }
guard txtEmail.hasValid(.email) else { return UserMessages.kValidEmail }
guard !txtCity.isEmpty else { return UserMessages.kBlankCity }
guard txtCity.hasValid(.alphabetWithSpace) else { return UserMessages.kValidCity }
// No Errors
return nil
}
// -----------------------------------------------------------------------------------------------
// MARK: - Action Functions
#IBAction func btnRegisterAction(_ sender: RoundButton) {
if let _ = User.current?.accessToken{
//TextField Verification
if let error = errorsInEditProfileTextFields() {
SnackBar.show(error)
return
}
//API Calling
self.apiEditProfileCall()
}else{
//TextField Verification
if let error = errorsInTextFields() {
SnackBar.show(error)
return
}
//API Calling
apiRegisterDeviceCall()
}
}
// -----------------------------------------------------------------------------------------------
// MARK: - Web Service Functions
private func apiRegisterDeviceCall() {
HUD.show(.progress)
var deviceToken:String = UserDefaults.standard.string(forKey: "DeviceToken") ?? "empty"
let parameters: [String: Any] = [
"vDeviceUniqueId" : DeviceManager.deviceUniqueId,
"txDeviceToken" : deviceToken,
"tDeviceOs" : DeviceManager.deviceOS,
"vDeviceName" : DeviceManager.modelName,
"vResolution" : DeviceManager.resolution,
"vOsVersion" : DeviceManager.osVersion,
]
print(parameters)
APIManager.shared.makeRequest(method: .registerDevice, parameters: parameters, withLoader: false) { (response, error) in
print(response)
if let accessToken = response["data"]["access_token"].string {
UserDefaults.standard.setValue(accessToken, forKey: "AccessToken")
self.apiRegisterCall()
} else {
HUD.hide()
SnackBar.show("Something went wrong")
}
}
}
private func apiRegisterCall() {
let parameters: [String: Any] = [
"vFirstName" : txtFirstName.trimmedText,
"vLastName" : txtLastName.trimmedText,
"vEmail" : txtEmail.trimmedText,
"vPassword" : txtPassword.trimmedText,
"vCityName" : txtCity.trimmedText,
]
var images: [String: UIImage] = [:]
if let image = imgProfile.image {
images["txProfileImageUrl"] = image
}
APIManager.shared.makeRequest(method: .registerUser, parameters: parameters, imageParameters: images, withLoader: false) { (response, error) in
HUD.hide()
if response["data"].exists() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.popViewController()
}
// Alert.showWith("User Registered", message: "Please check your email inbox for varification email", positiveTitle: "Ok", shouldResignOnTouchOutside: false) { isOk in
// if isOk {
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
// self.popViewController()
// }
// }
// }
} else {
SnackBar.show(response["message"].stringValue)
}
}
}
private func apiEditProfileCall() {
HUD.show(.progress)
let parameters: [String: Any] = [
"vFirstName" : txtFirstName.trimmedText,
"vLastName" : txtLastName.trimmedText,
"vEmail" : txtEmail.trimmedText,
"vCityName" : txtCity.trimmedText,
]
var images: [String: UIImage] = [:]
if let image = imgProfile.image {
images["txProfileImageUrl"] = image
}
APIManager.shared.makeRequest(method: .editProfile, parameters: parameters, imageParameters: images, withLoader: false) { (response, error) in
HUD.hide()
if response["data"].exists(){
if let accessToken = User.current?.accessToken{
var updateUser = User(withJSON: response["data"])
updateUser.accessToken = accessToken
User.current = updateUser
SnackBar.show("Profile successfully updated.")
self.navigationController?.popViewControllers(viewsToPop: 2)
}
}else{
SnackBar.show(response["message"].stringValue)
}
}
}
// -----------------------------------------------------------------------------------------------
// MARK: - Life Cycle Functions
override func viewDidLoad() {
super.viewDidLoad()
if let _ = User.current?.accessToken{
self.passwordView.isHidden = true
self.lblRegister.text = "Update"
self.title = "Edit Profile"
self.imgProfile.sd_imageIndicator = SDWebImageActivityIndicator.gray
self.imgProfile.sd_setImage(with: URL(string: User.current!.profileImage), placeholderImage: nil)
self.txtFirstName.text = User.current!.firstName
self.txtLastName.text = User.current!.lastName
self.txtEmail.text = User.current!.email
self.txtCity.text = User.current!.cityName
}
}
// -----------------------------------------------------------------------------------------------
}

Swift: How can I store array data, so it can be accessed more than once after leaving class?

I'm currently trying to develop a prototype for a quiz application.
The questions for the quiz are read out of a JSON file and the answers are generated within the program. Those two parts are then matched to each other within a Mediator class.
Displaying the question in my ViewController works perfectly fine for the first question. When trying to retrieve the second question the array that stores the question in the Mediator class is empty. That obviously is the case, because I'm re-entering the Mediator after going to my ViewController.
How can I store the data more efficiently?
If you need more information, please let me know. I'm really grateful for every suggestion I can get. I'm totally stuck at right now!
ViewController:
[...]
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type) {
stackView.addArrangedSubview(answerSection)
}
}
[...]
Mediator Class:
import Foundation
protocol Mediator {
func sendRequest (_ request: Request, sender: Sender)
}
protocol MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _ array: [QuestionData])
}
enum Sender {
case answer
case question
}
class QuestionAnswerMediator: Mediator {
var delegate: MediatorDelegate?
var questionArray = [QuestionData]()
var answerArray = [String]()
var request = Request()
var questionNum = 0
func getCategory(_ category: CategoryType) {
request.categoryType = category
matchQuestionAndAnswers()
}
func sendRequest (_ request: Request, sender: Sender){
if sender == .question {
let array = QuestionManager.shared.send(category: request.categoryType!)
questionArray.append(contentsOf: array)
}
else if sender == .answer {
let array = AnswerManager.shared.send(request: request)
if array?.isEmpty == false {
answerArray = []
answerArray.append(contentsOf: array ?? ["default"])
}
}
}
func fetchAnswers(for type: String, question: String) {
request.answerType = AnswerType(rawValue: type)
request.question = question
sendRequest(request, sender: .answer)
}
func fetchQuestions (for category: CategoryType){
request.categoryType = category
sendRequest(request, sender: .question)
}
func matchQuestionAndAnswers(){
var i = 0
fetchQuestions(for: request.categoryType!)
for question in questionArray {
fetchAnswers(for: question.answerType, question: question.question)
if answerArray.isEmpty != true {
questionArray[i].answers = answerArray
}
i += 1
}
self.delegate?.didUpdate(mediator: self, questionArray)
}
func getQuestion() -> String {
return questionArray[questionNum].question
}
func getAnswers() -> [String] {
return questionArray[questionNum].answers!
}
func getAnswerType() -> String {
return questionArray[questionNum].answerType
}
func nextQuestion () {
if questionNum + 1 < questionArray.count {
questionNum += 1
}
else {
questionNum = 0
}
}
}
ok, that was hard ;) i had to create my own project file, because yours was still missing. After doing that i tried to compile and run...but you did not fill the assets so it crashed. After faking your assets...it runs. ;)
You already analyzed your problem correctly. You should never create a viewcontroller mutliple times if you just call him once. So i deleted this and you now only have one instance.
i routed the "didchooseAnswer" through your classes...but you still have some work to do: i had to give a frame to your view class, so fill that frame you need, i don't know what frame you need.
I just corrected it for MultipleChoiceView...you have to do it for the other views as well like PolarView etc...
i hope i have everything copied what i changed, if not, just tell me. but maybe then you should give me rights on your github project so that i can commit it there...
Quizviewcontroller:
import UIKit
import CoreLocation
import os
class QuizViewController: UIViewController {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var navigationBar: UINavigationItem!
#IBOutlet var mainView: UIView!
#IBOutlet weak var quizStackView: UIStackView!
var barTitle: String?
//variables for location services
var locationManager = CLLocationManager()
var locationData: LocationModel?
//variables for communication with Mediator
var QAMatcher = QuestionAnswerMediator()
var questions : [QuestionData]?
var categoryType: CategoryType!
var timer = Timer()
//Change UI depending on answer type
var uiBuilder = AnswerViewBuilder()
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
}
//MARK: - Setup & updating UI
extension QuizViewController: MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _: [QuestionData]) {
self.updateUI()
}
func setup(_ category: CategoryType?) {
switch category {
case .shortterm:
print("No function yet")
case .longterm:
print("No function yet")
case .problemsolving:
print("No function yet")
case .orientation:
locationManager.delegate = self
checkLocationServiceSupport()
locationManager.requestWhenInUseAuthorization()
gecodeCurrentLocation()
default:
let message: StaticString = M.Errors.invalidCategoryError
os_log(message, log: OSLog.default, type: .error)
}
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type, didChooseAnswer: self.didChooseAnswer(_:)) {
stackView.addArrangedSubview(answerSection)
}
}
}
//MARK: - CLLocationManagerDelegate
extension QuizViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
let message: StaticString = M.Errors.locationDetectionError
os_log(message, log: OSLog.default, type: .error)
}
func checkLocationServiceSupport () {
if CLLocationManager.significantLocationChangeMonitoringAvailable() != true {
let alert = UIAlertController(title: M.Alters.locationAlertTitle, message: M.Alters.locationAlert, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
NSLog("The user acknowledge that location services are not available.")
}))
self.present(alert, animated: true, completion: nil)
self.dismiss(animated: true, completion: nil)
}
}
func gecodeCurrentLocation() {
locationManager.requestLocation()
if let lastLocation = self.locationManager.location {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(lastLocation) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?.first {
let city = placemark.locality!
let country = placemark.country!
self.locationData = LocationModel(city: city,country: country)
}
}
else {
let message: StaticString = M.Errors.locationDecodingError
os_log(message, log: OSLog.default, type: .error)
}
}
}
}
}
//MARK: - UITextFieldDelegate
//extension CategoryViewController: UITextFieldDelegate {
//
// func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
// if textField.text != "" {
// return true
// }
// else {
// textField.placeholder = "Enter your answer here"
// return false
// }
// }
//
// func textFieldDidEndEditing(_ textField: UITextField) {
// }
//
// func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// textField.endEditing(true)
// return true
// }
//}
MulitpleChoiceView
import UIKit
class MultipleChoiceView: UIStackView {
var button1 = UIButton()
var button2 = UIButton()
var button3 = UIButton()
var didChooseAnswer : ((UIButton)->())?
init(frame: CGRect, didChooseAnswer: #escaping (UIButton)->()) {
super.init(frame: frame)
self.didChooseAnswer = didChooseAnswer
self.addAnswerView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addAnswerView() {
setupStackView()
costumeButtons()
}
func setupStackView() {
self.alignment = .fill
self.distribution = .fillEqually
self.spacing = 10
self.axis = .vertical
}
func costumeButtons() {
let buttons = [button1 , button2, button3]
let dimensions = CGRect(x: 0, y: 0, width: 120, height: 20)
let color = #colorLiteral(red: 0.7294117647, green: 0.7450980392, blue: 1, alpha: 1)
for button in buttons {
button.frame = dimensions
button.backgroundColor = color
button.layer.cornerRadius = button.frame.size.height / 4
button.isUserInteractionEnabled = true
button.addTarget(self, action: M.Selector.buttonAction, for: .touchUpInside)
self.addArrangedSubview(button)
}
}
#objc func didChooseAnswer(_ sender: UIButton) {
if let didChooseAnswer = didChooseAnswer {
didChooseAnswer(sender)
}
}
}
AnswerViewBuilder
import UIKit
class AnswerViewBuilder {
func getAnswerSection(answerType: AnswerType, didChooseAnswer: #escaping (UIButton)->()) -> UIView?{
switch (answerType) {
case .multipleChoice:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
case .textField:
return TextFieldView()
case .polarQuestion:
return PolarQuestionView()
case .ranking:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
default:
print("Answer section could not be created.")
return nil
}
}
}

Charts update chart in realtime - data recorded on one VC and charted in another

I have a tabbed app that starts recording on one tab, and plots the mic levels on another tab.
In the first VC, I'm gathering mic levels and storing them in an array in the model. I'm using another method in the model to update the data, and I'm calling it in the second VC in order to update the view.
What I want to do is update the chart in the second view controller from the first view controller (where the logic for storing data in the model is)
Model:
Chart.swift
import Charts
class Chart {
static let sharedInstance = Chart()
var lineChartView: LineChartView!
func setChartValues() {
let entries = (0..<GraphData.sharedInstance.array.count).map { (i) -> ChartDataEntry in
let val = GraphData.sharedInstance.array[i]
print(ChartDataEntry(x: Double(i), y: val))
return ChartDataEntry(x: Double(i), y: val)
}
let set1 = LineChartDataSet(values: entries, label: "DataSet 1")
let data = LineChartData(dataSet: set1)
lineChartView.data = data
}
}
GraphData.swift
class GraphData {
static let sharedInstance = GraphData()
var array = [Double]()
}
View Controllers:
First VC: (complete code per comment)
import UIKit
import AVFoundation
class SoundController: UIViewController, AVAudioRecorderDelegate {
var recordingSession: AVAudioSession!
var audioRecorder: AVAudioRecorder!
var timer = Timer()
#IBOutlet weak var errorLbl: UILabel!
#IBOutlet weak var recordBtn: UIButton!
#IBAction func recordButton(_ sender: UIButton) {
if audioRecorder == nil {
startRecording()
} else {
finishRecording(success: true)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(false)
errorLbl.text = ""
}
override func viewDidLoad() {
super.viewDidLoad()
recordPermission()
}
func recordPermission() {
recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(.playAndRecord, mode: .default)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission() { allowed in
DispatchQueue.main.async {
if allowed {
print("recording allowed")
} else {
self.errorLbl.text = "Recording Permission was Denied. Please open settings and allow Cry It Out to access the microphone."
}
}
}
} catch {
self.errorLbl.text = "Recording Permission was Denied. Please open settings and allow the app to access the microphone."
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
func startRecording() {
if recordBtn.titleLabel?.text == "Tap to Re-record" {
//reset values array
GraphData.sharedInstance.array = []
}
let audioFilename = getDocumentsDirectory().appendingPathComponent("baby.m4a")
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
runTimer()
audioRecorder.record()
runTimer()
recordBtn.setTitle("Tap to Stop", for: .normal)
} catch {
finishRecording(success: false)
}
}
func levelTimerCallback() -> Float {
if audioRecorder != nil {
audioRecorder.updateMeters()
//If we are beyond a threshold value (-15)
if audioRecorder.averagePower(forChannel: 0) > -15 {
return audioRecorder.averagePower(forChannel: 0)
}
}
return 0
}
func finishRecording(success: Bool) {
//stop recording and reset recorder to nil for other checks
audioRecorder.stop()
audioRecorder = nil
if success {
recordBtn.setTitle("Tap to Re-record", for: .normal)
if timer.isValid {
timer.invalidate()
}
} else {
//Recording Failed
recordBtn.setTitle("Tap to Record", for: .normal)
//disable timer if running (might be running or might not)
if timer.isValid {
timer.invalidate()
}
}
}
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
if !flag {
finishRecording(success: false)
}
}
//MARK: Timers
#objc func updateTimer() {
if levelTimerCallback() != 0 {
let date = Date()
let calendar = Calendar.current
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
let seconds = calendar.component(.second, from: date)
let prettyDate = "\(month)/\(day) \(hour):\(minutes) and \(seconds) seconds"
print(prettyDate)
GraphData.sharedInstance.array.append(Double(levelTimerCallback()))
//does this run the method? It should
GraphController.sharedInstance.lineChartView?.data = Chart.sharedInstance.setChartValues()
}
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(SoundController.updateTimer)), userInfo: nil, repeats: true)
}
func stopTimer() {
timer.invalidate()
}
}
Second VC:
import UIKit
import Charts
class GraphController: UIViewController {
static let sharedInstance = GraphController()
#IBOutlet weak var lineChartView: LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.lineChartView.data = Chart.sharedInstance.setChartValues()
}
}
Try this solution without lambda functions. You don't need use static values.
1. Prepare your GraphController to have a function to receive data
class GraphController: UIViewController {
...
func dataReceived ( gData : GraphData ) {
DispatchQueue.main.async {
// Update your chart with gData
}
}
}
2. Get the reference of GraphController and use the function of step 1 to make your updates.
Please, get the reference of your GraphController from tab and use this reference to call a function to make your chart updates. I don't know exactely your situation, but if you have problems to make it, please look this: https://stackoverflow.com/a/39499751/5140756
class SoundController: UIViewController, AVAudioRecorderDelegate {
var graphController : GraphController?
...
override func viewDidLoad() {
super.viewDidLoad()
...
// get graph controller reference from tabbar.
self.graphController = self.tabBarController.viewControllers![INDEX_OF_VIEW_CONTROLLER] as! GraphController
}
// finally on your function call the function's graph controller receive data
#objc func updateTimer() {
if levelTimerCallback() != 0 {
let date = Date()
let calendar = Calendar.current
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
let seconds = calendar.component(.second, from: date)
let prettyDate = "\(month)/\(day) \(hour):\(minutes) and \(seconds) seconds"
print(prettyDate)
GraphData.sharedInstance.array.append(Double(levelTimerCallback()))
//does this run the method? It should
//GraphController.sharedInstance.lineChartView?.data = Chart.sharedInstance.setChartValues()
if graphController != nil {
self.graphController!.dataReceived( gData: GraphData.sharedInstance )
}
}
}
}
Please, look the code, and make some changes that you need, I tried automate the max that I can.

How do I know that the contacts have been modified, if the application was not active on iOS?

I need to run a function when the contacts have changed. If the application is active, you can do this with NotificationCenter as narrated in this post (sometimes It works when I add a new number to an existing contact). How do I know that the contact (or contacts) have been changed after the launch of the application?
I made the following functions for my task
#objc private func matchingContacts() {
if isSuccessContactUploading {
contactManager.matchingContacts(notMatch: { [weak self] in
guard let _self = self else { return }
debugPrint("matchingContacts != equals")
_self.isSuccessContactUploading = false
_self.syncContacts()
})
}
}
These functions are in ContactManager
func matchingContacts(notMatch: (() -> Void)?) {
getContacts { (contacts, error) in
if error == nil {
debugPrint("contacts count", contacts.count)
self.getContactsDictionaryFromCache(contacts, notMatch: {
notMatch?()
})
}
}
}
private func getContactsDictionaryFromCache(_ contacts: [CNContact], notMatch: (() -> Void)?) {
var isMatching = true
for contact in contacts {
let key = contact.identifier
do {
let cache = try Cache<NSDictionary>(name: "Contacts")
if let contactDictionary = cache[key] {
if !contactDictionary.isEqual(to: contact.dictionary) {
debugPrint("contactDictionary not matching")
isMatching = false
}
} else {
debugPrint("contactDictionary isn't here")
isMatching = false
}
} catch {
debugPrint(error.localizedDescription)
isMatching = false
}
}
if !isMatching {
notMatch?()
}
cacheContacts(contacts)
}
private func cacheContacts(_ contacts: [CNContact]) {
for contact in contacts {
let contactDictionary = contact.dictionary as NSDictionary
let key = contact.identifier
do {
let cache = try Cache<NSDictionary>(name: "Contacts")
cache[key] = contactDictionary
} catch {
debugPrint(error.localizedDescription)
}
}
}

Check for nil in data before saving to CoreData

I have this code below that when run I'd like it to check and see if the fields itemName, itemNote, etc are empty and if so display UIAlertController and prevent the user from continuing. The code that I'm attempting to use is under the saveTapped IBAction function. Currently, in runtime it seems to bypass all the checking and goes directly to the "if(item != nil) {" check.
Any insight would be appreciated.
import UIKit
import CoreData
class AddEditViewController: UIViewController, NSFetchedResultsControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var item : Item? = nil
#IBOutlet weak var itemName: UITextField!
#IBOutlet weak var itemNote: UITextField!
#IBOutlet weak var hoursPlayed: UITextField!
#IBOutlet weak var imageHolder: UIImageView!
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
if item != nil {
itemName.text = item?.name
itemNote.text = item?.note
hoursPlayed.text = item?.hoursPlayed
imageHolder.image = UIImage(data: (item?.image)!)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func AddImage(sender: AnyObject) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
pickerController.allowsEditing = true
self.presentViewController(pickerController, animated: true, completion: nil)
}
#IBAction func addImageFromCamera(sender: AnyObject) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.Camera
pickerController.allowsEditing = true
self.presentViewController(pickerController, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
self.imageHolder.image = image
}
#IBAction func saveTapped(sender: AnyObject) {
var nameMissing = ""
if itemName == nil {
nameMissing = "name"
print("item missing")
}
else if itemNote == nil {
nameMissing = "note"
print("note missing")
}
else if hoursPlayed == nil {
nameMissing = "hours played"
print("hours played missing")
}
else if imageHolder == nil {
nameMissing = "image"
print("image missing")
}
if (itemName == nil || itemNote == nil || hoursPlayed == nil || imageHolder == nil) {
let missingDetailsAlertController = UIAlertController(title: "Alert", message: "Complete field for \(nameMissing).", preferredStyle: UIAlertControllerStyle.Alert)
missingDetailsAlertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(missingDetailsAlertController, animated: true, completion: nil)
return
}
if(item != nil) {
editItem()
} else {
createNewItem()
}
dismissViewController()
}
#IBAction func cancelTapped(sender: AnyObject) {
dismissViewController()
}
func dismissViewController() {
navigationController?.popViewControllerAnimated(true)
}
func createNewItem() {
let entityDescription = NSEntityDescription.entityForName("item", inManagedObjectContext: moc)
let item = Item(entity: entityDescription!, insertIntoManagedObjectContext: moc)
item.name = itemName.text
item.note = itemNote.text
item.hoursPlayed = hoursPlayed.text
item.image = UIImagePNGRepresentation(imageHolder.image!)
do {
try moc.save()
} catch {
print("Create Save Failed.")
return
}
}
func editItem() {
item?.name = itemName.text
item?.note = itemNote.text
item?.hoursPlayed = hoursPlayed.text
item!.image = UIImagePNGRepresentation(imageHolder.image!)
do {
try moc.save()
} catch {
print("Edit Save Failed.")
return
}
}
}
You should check for text property of your textFields not for nil.
Try this:
if let name = itemName.text where name.characters.count > 0 {
//show your alert
} else if let note = itemNote.text where note.characters.count > 0 {
//show your alert
} else if let hours = hoursPlayed.text where hours.characters.count > 0 {
//show your alert
} else if let image = imageHolder.image {
//show your alert
}
And if you want to access the value without alert then you could try this also for UITextFields:
let name = itemName.text ?? ""
let note = itemNote.text ?? ""
let hours = hoursPlayed.text ?? ""
No need to do extra code every time
Use this simple code for check empty string
let bananaName = " " //Empty String
let mango = "Mango"
//Make file for this function and call like my example below return Boolean value for check the value
//Function
struct Checker {
static func isEmptyfor(content:String) -> Bool? {
guard content.trimString.isEmpty else {
print("Not Empty")
return true
}
print("Empty")
return false
}
}
// String Extenion here for Validate trim String
extension String {
var trimString:String {
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
}
//***Final Code here for use this Only for check your string
//Call Function here Like This
//##exp 1
if Checker.isEmptyfor(bananaName)! {
print("Not Empty")
//Do something here
}else{
print("Empty")
}
//##exp 2
if Checker.isEmptyfor(mango)! {
print("Not Empty")
//Do something here
}else{
print("Empty")
}
Output of the code inside Playground

Resources