Change between multiple view controllers causing app to crash; Tab View; Swift - ios

Apologies for this one, I'm sure that I'm missing a swift fundamental here.
I have a navigation app that I'm working on in swift.
Earlier on I made it as a single view and have changed it to a tab bar controller application.
In the background the location manager does its work and when is does the update locations work it displays the lat and long on a label.
When the app was a single view app this was no problem however now that it is a tab bar app, as soon as I change to a different tab when the location manager function fires it crashes the app with the following code (which I suspect means nothing much on its own) EXC_BREAKPOINT (code=1, subcode=0x10039bb54
I strongly suspect that I am doing something fundamentally wrong with trying to update a label on a view controller that is not active at the time.
distanceLabel.text = "Currently \(distanceText) from destination"
lbl_location.text = String(mylat) + ", " + String(mylong)
I have copied in all of my code below only taking out a few sensitive bits so that you can see the big picture.
Open to constructive feedback as to better coding practices.
Thanks
Bruce
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//in the original view controller which has now been incorporated into tabs
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var lbl_location: UILabel!
//on a new view controller on another tab
#IBOutlet weak var employeeNameSetup: UITextField!
#IBOutlet weak var employeeNumberSetup: UITextField!
#IBOutlet weak var contactNumberSetup: UITextField!
#IBOutlet weak var managerNameSetup: UITextField!
#IBOutlet weak var managerEmailSetup: UITextField!
#IBOutlet weak var adminPasswordSetup: UITextField!
#IBAction func setAppDefaults(_ sender: Any) {
if (employeeNameSetup.text?.isEmpty)! || (employeeNumberSetup.text?.isEmpty)! || (contactNumberSetup.text?.isEmpty)! || (managerNameSetup.text?.isEmpty)! || (managerEmailSetup.text?.isEmpty)!{
let noGoAlert = UIAlertController(title: "Insufficient Fields Filled out", message: "Only the Password field is optional, please fill out all other fields before pressing button", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Close Alert", style: .default, handler: nil)
noGoAlert.addAction(defaultAction)
present(noGoAlert, animated: true, completion: nil)
}
else{
let defaults = UserDefaults.standard
defaults.set(employeeNameSetup.text, forKey: "employeeName")
defaults.set(employeeNumberSetup.text, forKey: "employeeNumber")
defaults.set(contactNumberSetup.text, forKey: "contactNumber")
defaults.set(managerNameSetup.text, forKey: "managerName")
defaults.set(managerEmailSetup.text, forKey: "managerEmail")
}
}
#IBAction func submit_button(_ sender: Any) {
uploadData()
}
let location_process = CLLocationManager()
var mylat :Double = 0
var mylong :Double = 0
var destination = CLLocation(latitude: #####, longitude: #####)
var distanceToDest :Int = 0
var dburl = NSURL(string: "#########")
let contact_name = "#####"
let contact_number = "#####"
let unique_id = "###"
let employee_number = "#####"
var distanceText = ""
var runOnce = false
var isAdmin = false
var adminPassword = "#######"
func sendEmail(messageText: String){
let url = URL (string: "########")
let session = URLSession.shared
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let paramtosend = #########
request.httpBody = paramtosend.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request)
task.resume()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let mylocation = locations[0]
mylat = mylocation.coordinate.latitude
mylong = mylocation.coordinate.longitude
distanceToDest = Int(mylocation.distance(from: destination))
if distanceToDest > 1000 {
distanceText = String(Int(distanceToDest/1000))
distanceText = distanceText + " km"
}
else if distanceToDest < 100 {
let current_date_time = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
formatter.dateFormat = "HH:mm dd MMM yyyy"
formatter.timeZone = TimeZone.autoupdatingCurrent
let timestamp = formatter.string(from: current_date_time)
if !runOnce {
sendEmail(messageText: "\(contact_name); Employee \(employee_number) Arrived at destination \(destination.coordinate.latitude), \(destination.coordinate.longitude) at \(timestamp)")
}
distanceText = String(distanceToDest)
distanceText = distanceText + " m"
runOnce = true
}
else {
distanceText = String(distanceToDest)
distanceText = distanceText + " m"
}
distanceLabel.text = "Currently \(distanceText) from destination"
lbl_location.text = String(mylat) + ", " + String(mylong)
uploadData()
}
#IBAction func uploadData()
{
var httprequest = URLRequest(url: dburl! as URL)
httprequest.httpMethod = "POST"
var dataString = "####" // starting POST string with a secretWord
// the POST string has entries separated by &
dataString = dataString + "&unique_id=" + unique_id
dataString = dataString + "&mylat=" + String(mylat) // add items as name and value
dataString = dataString + "&mylong=" + String(mylong)
dataString = dataString + "&contact_name=" + contact_name
dataString = dataString + "&contact_number=" + contact_number
dataString = dataString + "&employee_number=" + employee_number
let current_date_time = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
formatter.dateFormat = "HH:mm dd MMM yyyy"
formatter.timeZone = TimeZone.autoupdatingCurrent
let timestamp = formatter.string(from: current_date_time)
dataString = dataString + "&timestamp=" + timestamp
// convert the post string to utf8 format
let dataD = dataString.data(using: .utf8) // convert to utf8 string
do
{
// the upload task, uploadJob, is defined here
let uploadJob = URLSession.shared.uploadTask(with: httprequest, from: dataD!)
//let uploadJob = URLSession.shared.uploadTask(with: httprequest, from: dataD)
uploadJob.resume()
}
}
override func viewDidLoad() {
super.viewDidLoad()
location_process.delegate = self
location_process.pausesLocationUpdatesAutomatically = false
location_process.desiredAccuracy = kCLLocationAccuracyBest
location_process.requestWhenInUseAuthorization()
location_process.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled()
{
location_process.distanceFilter = 50
location_process.allowsBackgroundLocationUpdates = true
location_process.startUpdatingLocation()
}
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Related

Changes in the datepicker widgets in iOS?

In our iOS app we have a timepicker widget to book appointments. This has worked fine for a long time. We just changed iOS developer and when he rebuilt the app the timepicker is broken for some reason we cannot tell. Has something changed recently in XCode/iOS that could explain why the layout suddenly is broken?
How it was
How it is now
This is the code responsible:
This timepicker is based on the standard iOS UIDatePicker.
class TimePickerController: UIViewController {
#IBOutlet weak var notAvailableLbl: UILabel!
#IBOutlet weak var fromPicker: UIDatePicker!
#IBOutlet weak var toPicker: UIDatePicker!
#IBOutlet weak var switchNotAvailable: UISwitch!
var object: EventElement?
var delegate:TimePickerControllerDelegate?
var name: DEFAULT_AVAILABILITY_CONST!
var startTime,endTime: Date?
var isFromSettings:Bool = false
var isNotAvailable: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = AppColors.Primary
switchNotAvailable.setOn(isNotAvailable, animated: true)
switchChanged(switchNotAvailable)
setPickerLocale(withIdentifier: "en_GB")
setPickerTimezone(withIdentifier: "UTC")
initPickerDates()
if object?.type == EVENT_CONST.lunch{
switchNotAvailable.isHidden = true
notAvailableLbl.isHidden = true
}else{
switchNotAvailable.isHidden = false
notAvailableLbl.isHidden = false
}
}
func setPickerLocale(withIdentifier locale: String) {
// Changing to 24 hrs
let local = NSLocale(localeIdentifier: locale) as Locale
fromPicker.locale = local
toPicker.locale = local
}
func setPickerTimezone(withIdentifier timeZone: String) {
//set timezone
fromPicker.timeZone = TimeZone.init(identifier: timeZone)
toPicker.timeZone = TimeZone.init(identifier: timeZone)
}
func initPickerDates() {
if let obj = object {
let startTime = obj.start.split(separator: "Z")
let endTime = obj.end.split(separator: "Z")
if(startTime.first == endTime.first)
{
fromPicker.date = getTimeFromString(stringTime: String("08:00"))
toPicker.date = getTimeFromString(stringTime: String("22:00"))
}
else{
fromPicker.date = getTimeFromString(stringTime: String(startTime.first ?? "08:00"))
toPicker.date = getTimeFromString(stringTime: String(endTime.first ?? "22:00"))
}
}else {
if let start = startTime,let end = endTime {
if(start == end)
{
// 8.00 to 22.00
fromPicker.date = start
toPicker.date = end.dateByAddingHours(hours: 14)
}
else{
fromPicker.date = start
toPicker.date = end
}
}
}
}
func getTimeFromString(stringTime: String) -> Date
{
let local = NSLocale(localeIdentifier: "en_GB") as Locale
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
dateFormatter.locale = local
dateFormatter.timeZone = TimeZone.init(identifier: "UTC")
let date = dateFormatter.date(from: stringTime) ?? Date()
print(date)
return date
}
#IBAction func btnDone(_ sender: Any) {
print(fromPicker.date)
print(toPicker.date)
updateEvent()
}
func updateEvent() {
var start = fromPicker.date.timeDisplay + "Z"
var end = toPicker.date.timeDisplay + "Z"
var createdDate:String = ""
var id:String = ""
var name:String = ""
if object == nil {
start = fromPicker.date.ISOISO8601StringForAvailability + "Z"
end = toPicker.date.ISOISO8601StringForAvailability + "Z"
createdDate = Date().toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm:ss")) + "Z"
id = UUID().uuidString.lowercased()
name = self.name.rawValue
}
else{
createdDate = object?.created ?? ""
id = object?.id ?? ""
name = (object?.name.rawValue) ?? ""
}
print("switch state",switchNotAvailable.isOn)
if switchNotAvailable.isOn {
if let startT = startTime,let endT = endTime {
let startDt = "08:00Z".formatDateWithTime(referenceDate: startT)
let endDt = "08:00Z".formatDateWithTime(referenceDate: endT)
start = startDt!.toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm"),timezone: "UTC") + "Z"
end = endDt!.toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm"),timezone: "UTC") + "Z"
}
else{
start = "08:00Z"
end = "08:00Z"
}
}
ActivityIndicator().showIndicator(backgroundColor: nil)
print("start and end date : ",start," ",end)
let type:String?
if isFromSettings == true{
type = object?.type.rawValue
}
else{
type = "AVAILABILITY_PATCH"
}
print(type ?? "AVAILABILITY_PATCH")
APICalls().updateAvailability(start: start, end: end, name: name, type: type ?? "AVAILABILITY_PATCH", created: createdDate,id: id ) { (response) in
ActivityIndicator().hideIndicator()
if let result = response
{
let res = Event.init(dictionary: result as? NSDictionary ?? [:])
DatabaseManager.getInstance().saveAvailability(object: res)
self.delegate?.didSelectTime(from: self.fromPicker.date, to: self.toPicker.date)
self.navigationController?.popViewController(animated: true)
}
}
}
To disable this time picker style, you have to add below line.
fromPicker.preferredDatePickerStyle = .wheels
In IOS 14 we have to define DatePickerStyle.
Please write these two lines in initPickerDates() method to set picker wheel style
fromPicker.preferredDatePickerStyle = .wheels
toPicker.preferredDatePickerStyle = .wheels
In IOS 14, we have multiple styles for date pickers. So this is the default behavior in iOS 14.
You just need to add the following line in the initPickerDates() method.
fromPicker.preferredDatePickerStyle = .wheels
toPicker.preferredDatePickerStyle = .wheels

How to integrate PayUMoney in swift

I want to integrate the PayUMoney SDK in my app using
I am using this link to integrate PayUMoney: How to integrate PayU Money in swift
I can see only the option to pay by credit card referring to the link's sample code however it is not showing up the net banking option. I don't understand what I am doing wrong in my code.
Here's what I have tried -
import UIKit
import PlugNPlay
import CommonCrypto
class PaymentVC: UIViewController {
var type = String()
var email = String()
var name = String()
var phone = String()
var address = String()
var state = String()
var zipcode = String()
var officetype = Int()
var TotalAmmount = String()
override func viewDidLoad() {
super.viewDidLoad()
continueWithCardPayment()
// Do any additional setup after loading the view.
}
#IBAction func backButtonAction(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
func continueWithCardPayment()
{
var paymentParam = PUMTxnParam()
paymentParam.key = "Zegfsgh"
paymentParam.merchantid = "7085776"
paymentParam.txnID = "xyz"
paymentParam.phone = "8770338859"
paymentParam.amount = TotalAmmount
paymentParam.productInfo = "Nokia"
paymentParam.surl = "https://test.payumoney.com/mobileapp/payumoney/success.php"
paymentParam.furl = "https://test.payumoney.com/mobileapp/payumoney/failure.php"
paymentParam.firstname = "john"
paymentParam.email = "john#john.com"
paymentParam.environment = PUMEnvironment.test
paymentParam.udf1 = "udf1"
paymentParam.udf2 = "udf2"
paymentParam.udf3 = "udf3"
paymentParam.udf4 = "udf4"
paymentParam.udf5 = "udf5"
paymentParam.udf6 = ""
paymentParam.udf7 = ""
paymentParam.udf8 = ""
paymentParam.udf9 = ""
paymentParam.udf10 = ""
paymentParam.hashValue = self.getHashForPaymentParams(paymentParam)
// paymentParam. = ""
// Set this property if you want to give offer:
// paymentParam.userCredentials = ""
PlugNPlay.presentPaymentViewController(withTxnParams: paymentParam, on: self, withCompletionBlock: { paymentResponse, error, extraParam in
if error != nil {
UIUtility.toastMessage(onScreen: error?.localizedDescription, from: .none)
} else {
var message = ""
if paymentResponse?["result"] != nil && (paymentResponse?["result"] is [AnyHashable : Any]) {
print(paymentResponse!)
message = "Hello Asad sucess"
// message = paymentResponse?["result"]?["error_Message"] as? String ?? ""
// if message.isEqual(NSNull()) || message.count == 0 || (message == "No Error") {
// message = paymentResponse?["result"]?["status"] as? String ?? ""
// }
} else {
message = paymentResponse?["status"] as? String ?? ""
}
UIUtility.toastMessage(onScreen: message, from: .none)
}
})
PlugNPlay.presentPaymentViewController(withTxnParams: paymentParam, on: self, withCompletionBlock: .none)
}
func sha512(_ str: String) -> String {
let data = str.data(using:.utf8)!
var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
data.withUnsafeBytes({
_ = CC_SHA512($0, CC_LONG(data.count), &digest)
})
return digest.map({ String(format: "%02hhx", $0) }).joined(separator: "")
}
func getHashForPaymentParams(_ txnParam: PUMTxnParam?) -> String? {
let salt = "vw8LigfjD"
var hashSequence: String? = nil
if let key = txnParam?.key, let txnID = txnParam?.txnID, let amount = txnParam?.amount, let productInfo = txnParam?.productInfo, let firstname = txnParam?.firstname, let email = txnParam?.email, let udf1 = txnParam?.udf1, let udf2 = txnParam?.udf2, let udf3 = txnParam?.udf3, let udf4 = txnParam?.udf4, let udf5 = txnParam?.udf5, let udf6 = txnParam?.udf6, let udf7 = txnParam?.udf7, let udf8 = txnParam?.udf8, let udf9 = txnParam?.udf9, let udf10 = txnParam?.udf10 {
hashSequence = "\(key)|\(txnID)|\(amount)|\(productInfo)|\(firstname)|\(email)|\(udf1)|\(udf2)|\(udf3)|\(udf4)|\(udf5)|\(udf6)|\(udf7)|\(udf8)|\(udf9)|\(udf10)|\(salt)"
}
let hash = self.sha512(hashSequence!).description.replacingOccurrences(of: "<", with: "").replacingOccurrences(of: ">", with: "").replacingOccurrences(of: " ", with: "")
return hash
}
func paymentResponseReceived(notify:NSNotification) {
print(notify)
}
}

Why do I need to press this button twice to display the data?

I'm not understanding why I need to press the button twice in order to get the labels to populate with the gathered data. I'm not sure if I'm doing something wrong with the json parsing or if there is some kind of scope issue. I'm sure there is redundant code here, but I was trying everything I could think of and find through google.
import UIKit
struct SkillStats {
var Rank, Level, Experience: String
init(Rank: String = "", Level: String = "", Experience: String = "") {
self.Rank = Rank
self.Level = Level
self.Experience = Experience
}
}
var rsn: String = ""
class ViewController: UIViewController {
var Total = SkillStats()
#IBOutlet weak var totalXP: UILabel!
#IBOutlet weak var totalLevel: UILabel!
#IBOutlet weak var totalRank: UILabel!
#IBOutlet weak var getrsn: UITextField!
#IBAction func rsnButton(_ sender: Any) {
rsn = getrsn.text!
rsn = rsn.replacingOccurrences(of: " ", with: "%20")
rsn = rsn.replacingOccurrences(of: "-", with: "%2D")
print(rsn)
//let rsn = "Real%20Nice"
let jsonUrlString = "http://services.runescape.com/m=hiscore_oldschool/index_lite.ws?player=" + rsn
guard let url = URL(string: jsonUrlString) else
{return}
URLSession.shared.dataTask(with: url) { (data, repsonse, err) in
guard let data = data else {return}
let dataAsString = String(data: data, encoding: .utf8)
//print(dataAsString)
let result = dataAsString?.components(separatedBy: [",","\n"])
//comment this block
//Total.Rank = result![0]
//Total.Level = result![1]
//Total.Experience = result![2]
self.Total = SkillStats(Rank: result![0], Level: result![1], Experience: result![2])
//printing stats to console
print("Total Rank: " + self.Total.Rank)
print("Total Level: " + self.Total.Level)
print("Total Experience: " + self.Total.Experience)
}.resume()
totalRank.text = "Rank: " + Total.Rank
totalLevel.text = "Level: " + Total.Level
totalXP.text = "XP: " + Total.Experience
}
Press button once and wait for new seconds as you are making api call it will take some time to get data from server and update your label text.
Updated
To updated your labels after few seconds you can do like below:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.totalRank.text = "Rank: " + self.Total.Rank
self.totalLevel.text = "Level: " + self.self.Total.Level
self.totalXP.text = "XP: " + self.Total.Experience
}
PS - yes, you can pass second value less then 1 as above code.

save Array of strings to core data and retrieve it. iOS, Swift

Is it possible to save an attribute of an array of strings into core data and then retrieve array?
I am saving data to core data and retrieving some of it shown in the prints below.
Here is my save data function..
func saveToCoreData(completion: (_ finished: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let saveTrip = SavedTrips(context: managedContext)
saveTrip.desc = tripDescription
saveTrip.name = tripName
saveTrip.data = ["12345"]
let time = Time()
time.date = Date()
saveTrip.date = time.date
do {
try managedContext.save()
completion(true)
print("Successfuly Saved to core Data")
}catch {
debugPrint("Tony: Could not save \(error.localizedDescription)")
completion(false)
}
}
And my tripCell where I am calling the data.
class TripsCell: UITableViewCell {
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var tripName: UILabel!
#IBOutlet weak var tripDescription: UILabel!
func configureCell(savedTrip: SavedTrips) {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .medium
self.tripName.text = savedTrip.name
self.tripDescription.text = savedTrip.desc
print("Tony: trip name: \(savedTrip.name!)")
print("Tony: trip description: \(savedTrip.desc!)")
if let tripData = savedTrip.data {
print("Tony Calling Plates: \(tripData)")
}else {
print("Tony: There is no trip Data")
}
if let date = savedTrip.date {
let dateFormater = DateFormatter()
dateFormater.dateFormat = "h:mm a"
dateLabel.text = dateFormater.string(from: date)
}
}
}
My core Data Model...
and my printout.Showing nothing in the array.

Execute function on startup

I'm trying to develop an application for IOS using swift language that is a news for me. I want to fill a dictionary (tobaccoList) on the application startup. I have a csv file, so I take data from this file and than i fill the dictionary:
class DataManager{
var latitudes = Array<Double>()
var longitudes = Array<Double>()
var tobaccoList = Dictionary<Double, Tabacchino>()
init(){
if let url = NSURL(fileURLWithPath: "/Users/brunopistone/Developer/apptabacchi/LocationList_sorted.csv" , isDirectory: true) {
var error: NSErrorPointer = nil
if let csv = CSV(contentsOfURL: url, error: error) {
//put every tabbacchino in a Dictionary tobaccoList
let rows = csv.rows
let totalRows = rows.count
for var index = 1; index < totalRows; index++ {
let temp = csv.rows[index]
let tabacchino = Tabacchino(
name: temp["Name"]!, phone: temp["tnumber"]!, lat: NSString(string: temp["Latitude"]!).doubleValue, lon: NSString(string: temp["Longitude"]!).doubleValue
)
let keyGeo = NSString(string: temp["Latitude"]!).doubleValue
storeTobaccoShop(keyGeo, value: tabacchino)
var doubleLatitude = NSString(string: temp["Latitude"]!).doubleValue
var doubleLongitude = NSString(string: temp["Longitude"]!).doubleValue
storeLatitude(doubleLatitude)
storeLongitudes(doubleLongitude)
}
}
}
}
func storeTobaccoShop(key: Double, value: Tabacchino) {
self.tobaccoList[key] = value
}
In the viewController file of the home page i have:
class ViewController: UIViewController, CLLocationManagerDelegate {
let startFunction = DataManager()
let locationManager = CLLocationManager()
var latitude = Double()
var longitude = Double()
var tobaccoList = Dictionary<Double, Tabacchino>()
override func viewDidLoad() {
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
tobaccoList = startFunction.getTobaccoList()
}
In the home page, I have a button that calls another view, and i want to pass the dictionary to the other view in order to use it, so I use this method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "tobaccoListSegue"{
let viewList = segue.destinationViewController as! ViewList
viewList.tabacchini = tobaccoList
}
}
The problem is, when i click on the button in order to call viewList, the application fills again the dictionary. What i want is to fill the dictionary only when I open the application.
Please help me fix this thing. Thanks
Put this line
let startFunction = DataManager()
Inside viewdidload() method.

Resources