Store Username and Password with UserDefaults - swift

I want to keep the user name, password and Chip number in the memory of the application. It keeps it in memory right now, but when I open and close the application from the background, the data is lost. But if I close the application on the loginview page, the data is lost if I close the application on the MainView page. This means that if I close the application on another page Other than the loginview page, the data is deleted. What should I do?
I can remember or recall the user's data with Labelcheckbox.
override func viewDidLoad() {
super.viewDidLoad()
chipField.text = chipInfoString
let save = UserDefaults.standard.bool(forKey: "RememberMe")
self.circleBox.isChecked = save
self.labelcheckbox.text = UserDefaults.standard.string(forKey: "RememberMe1")
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(setdata) , userInfo: nil, repeats: true)
rememberaction()
}
let mainTableVC = MainTableViewController()
mainTableVC.chip1InfoString = self.chipField.text
mainTableVC.emailString = self.emailField.text
mainTableVC.passwordString = self.passwordField.text
let navController = UINavigationController(rootViewController: mainTableVC)
self.present(navController, animated: true, completion: {
})
#objc func rememberaction(){
if labelcheckbox.text == "Remember" {
rememberme()
}
if labelcheckbox.text == "Don't Remember" {
deleteee((Any).self)
}
}
#objc func rememberme() {
let mail = UserDefaults.standard.string(forKey: "mail")
let password = UserDefaults.standard.string(forKey: "password")
if let x = mail {
self.emailField.text = x
}
if let y = password {
self.passwordField.text = y
}
let save3 = UserDefaults.standard.object(forKey: "chipnumber")
if let z = save3 as? String {
self.chipField.text = z
}
}
#objc func setdata()
{
if labelcheckbox.text == "Remember" {
UserDefaults.standard.set(true, forKey:"RememberMe");
UserDefaults.standard.set(labelcheckbox.text, forKey: "RememberMe1")
UserDefaults.standard.set(emailField.text, forKey: "mail")
UserDefaults.standard.set(passwordField.text, forKey: "password")
UserDefaults.standard.set(chipField.text, forKey: "chipnumber")
UserDefaults.standard.synchronize()
}
else{
if labelcheckbox.text == "Don't remember"{
UserDefaults.standard.set(false, forKey:"RememberMe");
UserDefaults.standard.set(labelcheckbox.text, forKey: "RememberMe1")
UserDefaults.standard.set(chipField.text, forKey: "chipnumber")
}
}
}
class MainTableViewController: UITableViewController, UITabBarControllerDelegate{
#objc func action(_ sender: Any){
UserDefaults.standard.set(chip1InfoString, forKey: "chipnumber")
UserDefaults.standard.set(emailString, forKey: "email")
UserDefaults.standard.set(passwordString, forKey: "password")
UserDefaults.standard.synchronize()
}
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
self.action((Any).self)
})

Related

Getting error when using Google place api in swift

I am using google place api to get location in my swift application. In my app when i am going to place api screen by clicking on the textfield present inside the cell of my tableview it is working fine. But when i am using the same code to show the place api screen by clicking on the textfield present inside the uiview, after selecting any place it is comming back to the previous screen, but once again the place api screen opening automatically. i have added my code, can anyonce see it and let me know the solution?
import GooglePlaces
class AddPatientViewController: UIViewController{
//MARK:- Properties
#IBOutlet weak var location_TextField: UITextField!
//MARK:- Variables
var locationManager = CLLocationManager()
var mAppDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate;
//MARK:- LifeCycles Methods
override func viewDidLoad() {
super.viewDidLoad()
setDelegate()
}
//MARK:- Helpers
private func setDelegate(){
location_TextField.delegate = self
}
//MARK: - getLocation
func getCurrentLatLongFromCLlocation() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
print("No access")
getPermissionAgain()
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
if let coordinateValue = locationManager.location?.coordinate {
mAppDelegate.cordinateVal = coordinateValue
self.getPlaceDataUsingLocation()
}
#unknown default:
getPermissionAgain()
}
} else {
print("Location services are not enabled")
getPermissionAgain()
}
}
func getPermissionAgain(){
let alert = UIAlertController(title: "Simplr", message: "Required location permission to set your search location", preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Dismiss", style: .default, handler: nil)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (action) in
if let appSettings = URL(string: UIApplication.openSettingsURLString) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(appSettings, options: [:], completionHandler: { (enabled) in
DispatchQueue.main.async {
//UIApplication.shared.registerForRemoteNotifications()
}
})
} else {
// Fallback on earlier versions
}
}
}
alert.addAction(okayAction)
alert.addAction(settingsAction)
present(alert, animated: false, completion: nil)
}
func getPlaceDataUsingLocation(){
DispatchQueue.global().async {
// Specify the place data types to return.
let placesClient = GMSPlacesClient.init()
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) | UInt(GMSPlaceField.coordinate.rawValue) | UInt(GMSPlaceField.formattedAddress.rawValue))
placesClient.currentPlace { (list, error) in
if let error = error {
print("An error occurred: \(error.localizedDescription)")
return
}
if let placeLikelihoodList = list?.likelihoods {
for likelihood in placeLikelihoodList {
let place = likelihood.place
print("\n\n")
print("Current Place name \(String(describing: place.name)) at likelihood \(likelihood.likelihood)")
print("Current PlaceID \(String(describing: place.placeID))")
print("Formatted Address: \(String(describing: place.formattedAddress))")
// print("Fields: \(place.)")
print("\nAddress Component: \(String(describing: place.addressComponents))\n")
print("Type: \(String(describing: place.types))")
}
}
}
}
}
}
//MARK: - UITextfield Delegate Methods
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool{
if textField == location_TextField{
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
// autocompleteController.primaryTextColor = UIColor(hex: "#4C2B6D")
// autocompleteController.secondaryTextColor = UIColor(hex: "#4C2B6D")
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) | UInt(GMSPlaceField.coordinate.rawValue) |
UInt(GMSPlaceField.formattedAddress.rawValue) | UInt(GMSPlaceField.addressComponents.rawValue))
autocompleteController.placeFields = fields
autocompleteController.navigationController?.navigationBar.barTintColor = .blue
autocompleteController.navigationController?.navigationBar.prefersLargeTitles = true
// Specify a filter.
let filter = GMSAutocompleteFilter()
filter.type = .geocode
autocompleteController.autocompleteFilter = filter
// Display the autocomplete view controller.
autocompleteController.modalPresentationStyle = .fullScreen
present(autocompleteController, animated: true, completion: nil)
}
return true
}
}
//MARK: - GMSAutocompleteViewControllerDelegate
#available(iOS 13.0, *)
extension AddPatientViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("selected place is \(getFormattedAddressFromAddressComponents(place: place))")
location_TextField.text = getFormattedAddressFromAddressComponents(place: place)
self.dismiss(animated: true)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
self.dismiss(animated: true)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func getFormattedAddressFromAddressComponents(place: GMSPlace) -> String{
var neighborhood = ""
var locality = ""
var sublocality = ""
var administrative_area_level_1 = ""
var formatedAddress = ""
if let addressComponents : Array<GMSAddressComponent> = place.addressComponents{
print(addressComponents)
for component in addressComponents{
if component.types.contains("administrative_area_level_1"){
if let shortName = component.shortName{
administrative_area_level_1 = shortName
}
}
else if component.types.contains("neighborhood")
{
neighborhood = component.name
} else if component.types.contains("sublocality")
{
sublocality = component.name
}else if component.types.contains("locality")
{
locality = component.name
}
}
var shouldAddComma = false
if !isNullObject(anyObject: administrative_area_level_1){
shouldAddComma = true
}
if !isNullObject(anyObject: neighborhood){
formatedAddress = neighborhood
}else if !isNullObject(anyObject: sublocality){
formatedAddress = sublocality
}
else if !isNullObject(anyObject: locality){
formatedAddress = locality
}
if !isNullObject(anyObject: formatedAddress){
if shouldAddComma{
formatedAddress = "\(formatedAddress),\(administrative_area_level_1)"
}
}
}
return formatedAddress
}
}

User profile information is not showing up using swift and firestore database

pic of database
I am trying to show a label with the user's status on their profile page. after logging in the user gets presented with a VC that has a side menu. on that side menu is a "profile" option. once choosing this they go to their profile controller. right now i simply need to search users/current uid/ "MembershipStatus" and present this result into a label called "welcomeLabel".
I am returning nul
import UIKit
import Firebase
class NonMemberProfileController: UIViewController {
// MARK: - Properties
var welcomeLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = UIFont.systemFont(ofSize: 28)
label.translatesAutoresizingMaskIntoConstraints = false
label.alpha = 0
return label
}()
// MARK: - Init
override func viewDidLoad()
{
super.viewDidLoad()
authenticateUserAndConfigureView()
}
func loadUserData()
{
guard let uid = Auth.auth().currentUser?.uid else {return}
//.child("MembershipStatus")
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value) {
(snapshot) in
if snapshot.hasChild("MembershipStatus"){
print("true we have bingo")
} else {
print("no bueno")
dump(snapshot)
}
guard let status = snapshot.value as? String else { return }
self.welcomeLabel.text = "Welcome, \(status)"
print("this is lkjfdskjklsfad" + status)
UIView.animate(withDuration: 0.5, animations: {
self.welcomeLabel.alpha = 1
})
}
}
func authenticateUserAndConfigureView(){
if Auth.auth().currentUser == nil {
DispatchQueue.main.async {
let navController = UINavigationController(rootViewController: LoginViewController())
navController.navigationBar.barStyle = .black
self.present(navController, animated: true, completion: nil)
}
} else {
configureNavigationBar()
loadUserData()
}
}
// MARK: - Selectors
#objc func handleDismiss() {
dismiss(animated: true, completion: nil)
}
// MARK: - Helper Functions
func configureNavigationBar() {
view.backgroundColor = UIColor.lightGray
navigationItem.title = "Profile"
navigationController?.navigationBar.barTintColor = .darkGray
navigationController?.navigationBar.barStyle = .black
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Home_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "baseline_settings_white_24dp").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
view.addSubview(welcomeLabel)
welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
welcomeLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
You are using Cloud Firestore for data storage but your code is reading data form RealTime Database. You have to read data like this:
let userRef = Firestore.firestore().collection("users").document(uid)
userRef.getDocument { (documentSnapshot, error) in guard
let document = documentSnapshot?.data() else {
print(error)
return
}
print(document)
}

Why does my segue not wait until completion handler finished?

I have a page based app, using RootViewController, ModelViewController, DataViewController, and a SearchViewController.
In my searchViewController, I search for an item and then add or remove that Item to an array which is contained in a Manager class(and UserDefaults), which the modelViewController uses to instantiate an instance of DataViewController with the correct information loaded using the dataObject. Depending on whether an Item was added or removed, I use a Bool to determine which segue was used, addCoin or removeCoin, so that the RootViewController(PageView) will show either the last page in the array, (when a page is added) or the first (when removed).
Everything was working fine until I ran into an error which I can not diagnose, the problem is that when I add a page, the app crashes, giving me a "unexpectadely found nil when unwrapping an optional value"
This appears to be the problem function, in the searchViewController 'self.performSegue(withIdentifier: "addCoin"' seems to be called instantly, even without the dispatchque:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
Meaning that In my DataViewController, this function will find nil:
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
I can't find out why it does not wait for completion.
I also get this error about threads:
[Assert] Cannot be called with asCopy = NO on non-main thread.
which is why I try to do the push segue using dispatch que
Here is my searchViewController full code:
import UIKit
class SearchViewController: UIViewController, UISearchBarDelegate {
let selectionLabel = UILabel()
let searchBar = UISearchBar()
let addButton = UIButton()
let removeButton = UIButton()
var filteredObject: [String] = []
var dataObject = ""
var isSearching = false
//Add Button Action.
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
//Remove button action.
#objc func removeButtonActon(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.removeCoin(coin: dataObject)
self.performSegue(withIdentifier: "addCoin", sender: self)
}
searchBar.text = ""
}
//Prepare for segue, pass removeCoinSegue Bool depending on remove or addCoin.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = true
}
} else if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = false
}
}
}
//Remove button action.
#objc func removeButtonAction(sender: UIButton!) {
if Manager.shared.coins.count == 1 {
removeAlert()
} else {
Manager.shared.removeCoin(coin: dataObject)
print(Manager.shared.coins)
print(dataObject)
searchBar.text = ""
self.removeButton.isHidden = true
DispatchQueue.main.async {
self.performSegue(withIdentifier: "removeCoin", sender: self)
}
}
}
//Search/Filter the struct from CGNames, display both the Symbol and the Name but use the ID as dataObject.
func filterStructForSearchText(searchText: String, scope: String = "All") {
if !searchText.isEmpty {
isSearching = true
filteredObject = CGNames.shared.coinNameData.filter {
// if you need to search key and value and include partial matches
// $0.key.contains(searchText) || $0.value.contains(searchText)
// if you need to search caseInsensitively key and value and include partial matches
$0.name.range(of: searchText, options: .caseInsensitive) != nil || $0.symbol.range(of: searchText, options: .caseInsensitive) != nil
}
.map{ $0.id }
} else {
isSearching = false
print("NoText")
}
}
//Running filter function when text changes.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterStructForSearchText(searchText: searchText)
if isSearching == true && filteredObject.count > 0 {
addButton.isHidden = false
dataObject = filteredObject[0]
selectionLabel.text = dataObject
if Manager.shared.coins.contains(dataObject) {
removeButton.isHidden = false
addButton.isHidden = true
} else {
removeButton.isHidden = true
addButton.isHidden = false
}
} else {
addButton.isHidden = true
removeButton.isHidden = true
selectionLabel.text = "e.g. btc/bitcoin"
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the UI.
self.view.backgroundColor = .gray
setupView()
}
override func viewDidLayoutSubviews() {
}
//Hide keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//Alerts
func removeAlert() {
let alertController = UIAlertController(title: "Can't Remove", message: "\(dataObject) can't be deleted, add another to delete \(dataObject)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func Duplicate() {
let alertController = UIAlertController(title: "Duplicate", message: "\(dataObject) is already in your pages!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func max() {
let alertController = UIAlertController(title: "Maximum Reached", message: "\(dataObject) can't be added, you have reached the maximum of 5 coins. Please delete a coin to add another.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
and here is the DataViewController
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
//Variables and Objects.
//The dataObject carries the chosen cryptocurrencies ID from the CoinGecko API to use to get the correct data to load on each object.
var dataObject = String()
//The DefaultCurrency (gbp, eur...) chosen by the user.
var defaultCurrency = ""
//The Currency Unit taken from the exchange section of the API.
var currencyUnit = CGExchange.shared.exchangeData[0].rates.gbp.unit
var secondaryUnit = CGExchange.shared.exchangeData[0].rates.eur.unit
var tertiaryUnit = CGExchange.shared.exchangeData[0].rates.usd.unit
//Index of the dataObject
var dataIndex = Int()
//Objects
let cryptoLabel = UILabel()
let cryptoIconImage = UIImageView()
let secondaryPriceLabel = UILabel()
let mainPriceLabel = UILabel()
let tertiaryPriceLabel = UILabel()
//Custom Fonts.
let customFont = UIFont(name: "AvenirNext-Heavy", size: UIFont.labelFontSize)
let secondFont = UIFont(name: "AvenirNext-BoldItalic" , size: UIFont.labelFontSize)
//Setup Functions
//Get the index of the dataObject
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
//Label
func setupLabels() {
//cryptoLabel from dataObject as name.
cryptoLabel.text = CGPrices.shared.coinData[dataIndex].name
//Prices from btc Exchange rate.
let btcPrice = CGPrices.shared.coinData[dataIndex].current_price!
let dcExchangeRate = CGExchange.shared.exchangeData[0].rates.gbp.value
let secondaryExchangeRate = CGExchange.shared.exchangeData[0].rates.eur.value
let tertiaryExchangeRate = CGExchange.shared.exchangeData[0].rates.usd.value
let realPrice = (btcPrice * dcExchangeRate)
let secondaryPrice = (btcPrice * secondaryExchangeRate)
let tertiaryPrice = (btcPrice * tertiaryExchangeRate)
secondaryPriceLabel.text = "\(secondaryUnit)\(String((round(1000 * secondaryPrice) / 1000)))"
mainPriceLabel.text = "\(currencyUnit)\(String((round(1000 * realPrice) /1000)))"
tertiaryPriceLabel.text = "\(tertiaryUnit)\(String((round(1000 * tertiaryPrice) / 1000)))"
}
//Image
func getIcon() {
let chosenImage = CGPrices.shared.coinData[dataIndex].image
let remoteImageUrl = URL(string: chosenImage)
guard let url = remoteImageUrl else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
DispatchQueue.main.async {
self.cryptoIconImage.image = UIImage(data: data)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// for family in UIFont.familyNames.sorted() {
// let names = UIFont.fontNames(forFamilyName: family)
// print("Family: \(family) Font names: \(names)")
// }
// Do any additional setup after loading the view, typically from a nib.
self.setupLayout()
self.getIndex()
self.setupLabels()
self.getIcon()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
view.backgroundColor = .lightGray
}
}
Edit: CGPrices Class with getData method:
import Foundation
class CGPrices {
struct Coins: Decodable {
let id: String
let name: String
let symbol: String
let image: String
let current_price: Double?
let low_24h: Double?
//let price_change_24h: Double?
}
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i + 1) < coins.count { coinsEncoded += "%2C" }
}
print("encoded")
}
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let coinsData = try JSONDecoder().decode([Coins].self, from: data)
self.coinData = coinsData
completion(arr)
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
print(data)
}
}.resume()
}
func refresh(completion: () -> ()) {
defaultCurrency = UserDefaults.standard.string(forKey: "DefaultCurrency")!
completion()
}
}
I figured it out.
The problem was inside my getData method I was not updated the coins array:
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i+1)<coins.count { coinsEncoded+="%2C" }
}
print("encoded")
}
I needed to add this line in getData:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
//Adding this line to update the array so that the URL is appended correctly.
coins = Manager.shared.coins
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
This would fix the finding nil in the DataViewController, but the app would still crash do to updating UI Elements on a background thread, as the segue was called inside the completion handler of the getData method. to fix this, I used DispatchQue.Main.Async on the segue inside the getData method in the addButton function, to ensure that everything is updated on the main thread, like so:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
print("starting")
CGPrices.shared.getData(arr: true) { (arr) in
print("complete")
print(CGPrices.shared.coinData)
//Here making sure it is updated on main thread.
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
}
}
searchBar.text = ""
}
Thanks for all the comments as they helped me to figure this out, and I learned a lot in doing so. Hopefully this can help someone else in their thought process when debugging, as one can get so caught up in one area of a problem, and forget to take a step back and look to other areas.

Swift Firebase sending data inside a closure

I am trying to send data to another view controller. However, the data cannot be reached at the second view controller. Here is my code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "tograddetail":
print("Going to Grad Detail")
guard let gradDetailViewController = segue.destination as? graduatedetailViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedgradCell = sender as? GradTableViewCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedgradCell) else {
fatalError("The selected cell is not being displayed by the table")
}
ref = FIRDatabase.database().reference().child("Database")
ref.observe(FIRDataEventType.value, with: { (snapshot) in
//print(snapshot.value)
if snapshot.exists() {
if let countdowntime = snapshot.value as? NSDictionary {
let selectedgrad = self.graduatename[indexPath.row]
if let graddata = countdowntime[selectedgrad] as? NSDictionary {
let theinstitution = graddata["Institution"] as! String
let thelocation = graddata["location"] as! String
let thetimeleft = graddata["timeleft"] as! Int
guard let firstgrad = graddetail(institution: theinstitution, location: thelocation, timeleft: thetimeleft) else {
fatalError("Unable to instantiate graddetail")
}
//print(firstgrad.institution)
//print(destinationgraddata.grad?.institution)
let destinationVC = segue.destination as! graduatedetailViewController
destinationVC.grad = firstgrad
}
}
}
})
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
And here is my code for the second view controller:
var grad: graddetail?
#IBOutlet weak var theinstitution: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let grad = grad {
theinstitution.text = grad.institution
}
}
However, the grad.institution value always return nil. Any idea?
The issue is observe(_:with:) is async and segue will called synchronously, so that when you get response in completion block of observe your segue is already performed.
To solved the issue what you need to do is call the observe before calling the performSegue and inside the completion block of observe when you get response call the perfromSegue with the value that you want to pass.

How do I save and show a Image with core data - swift 3

I am doing a project in Swift 3 - xcode 8, and I am trying to use core data to save and show some images in a data base table "users".
This image is the user photo in his profile.
Now I've managed to save strings and showing them from core data but I am having problems in working this out with images.
This is what I have so far:
Adding USERS into core data
func addUser() {
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
let newUser = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context)
if (firstName.text == "" && lastName.text == "" && contact.text == "" && email.text == "") { //if we have a user profile delete it
deleteUser()
} else { // add a new user profile
newUser.setValue(firstName.text, forKey: "firstName")
newUser.setValue(lastName.text, forKey: "lastName")
newUser.setValue(contact.text, forKey: "contact")
newUser.setValue(email.text, forKey: "email")
//newUser.setValue(imageView.image, forKey: "photo")
//let imgUrl = UIImagePickerControllerReferenceURL as! NSURL
let img = UIImage(named: "f.png")
let imgData = UIImageJPEGRepresentation(img!, 1)
newUser.setValue(imgData, forKey: "photo")
print ("Data added in Users")
}
do {
try context.save()
//print("saved!!!")
Alert.show(title: "Success", message: "Profile Saved", vc: self)
} catch {
// print ("Error")
Alert.show(title: "Error", message: "Profile not Saved", vc: self)
}
}
Showing Users from core data
func showUser() {
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
print("Profile: Data Found:")
for result in results as! [NSManagedObject] {
if let firstNameinData = result.value(forKey: "firstName") as? String{
firstName.text = firstNameinData
print(firstNameinData)
}
if let lastNameinData = result.value(forKey: "lastName") as? String{
lastName.text = lastNameinData
print(lastNameinData)
}
if let contactinData = result.value(forKey: "contact") as? String{
contact.text = contactinData
print(contactinData)
}
if let emailinData = result.value(forKey: "email") as? String{
email.text = emailinData
print(emailinData)
}
if let photoinData = result.value(forKey: "photo") as? UIImage{
imageView.image = photoinData
}
}
} else { // if there is not a user profile
firstName.text = ""
lastName.text = ""
contact.text = ""
email.text = ""
print("Profile : No data found")
}
//print("Loaded!!!")
} catch {
print ("Error Loading")
}
}
I cannot show the image I have saved.
Do you have any tips?
EDIT: Xcode gives me this message "Connection to assetsd was interrupted or assetsd died"
The property photo of Users is (NS)Data, as you do there, converting the
UIImage into NSData.
let img = UIImage(named: "f.png")
let imgData = UIImageJPEGRepresentation(img!, 1)
newUser.setValue(imgData, forKey: "photo")
While when you retrieve the info, you are doing like photo was a UIImage object:
if let photoinData = result.value(forKey: "photo") as? UIImage{
imageView.image = photoinData
}
This is not logical according to previous lines. It should be something like that:
if let imageData = result.value(forKey: "photo") as? NSData {
if let image = UIImage(data:imageData) as? UIImage {
imageView.image = image
}
}
Note: I don't speak Swift, so the proposed code may not compile, but you should get the idea of what's wrong and what's need to be done.
Larme has it almost spot on, but instead of this:
if let image = UIImage(data:imageData) as? UIImage
do this:
if let image = UIImage(data: imageData as Data)
Hope i helps you. work fine for me
var results :[Any] = []
let image = UIImage(named: "image.png")
//this is the line that appears to be wrong
let imageData = UIImagePNGRepresentation(image!) as NSData?
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
// 1
let managedContext =
appDelegate.persistentContainer.viewContext
// 2
let entity =
NSEntityDescription.entity(forEntityName: "Image",
in: managedContext)!
let person = NSManagedObject(entity: entity,
insertInto: managedContext)
// 3
person.setValue(imageData, forKeyPath: "name")
// 4
do {
try managedContext.save()
results.append(person)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
Hope this will help you. First I too had a great confusing of storing the image in core Data.
This is used to save the image in coreData
First create Nsmanaged Object Class
class Item: NSManagedObject {
}
Declare the image as NSData
import CoreData
extension Item {
#NSManaged var image: NSData?
#NSManaged var name: String?
#NSManaged var email: String?
}
Now go the View Controller you want to save the image .
class newViewController: UIViewController ,UIImagePickerControllerDelegate,UINavigationControllerDelegate{
var item : Item? = nil
var imagePicker = UIImagePickerController()
var PassImages = UIImage()
#IBOutlet var name: UITextField!
#IBOutlet var email: UITextField!
#IBOutlet var photoclick: UIButton!
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBAction func clickaction(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary){
print("Button capture")
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.sourceType = .photoLibrary
picker.delegate = self //Don't forget this line!
self.present(picker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var selectedImage: UIImage?
if let editedImage = info[.editedImage] as? UIImage {
selectedImage = editedImage
self.image.image = selectedImage!
picker.dismiss(animated: true, completion: nil)
} else if let originalImage = info[.originalImage] as? UIImage {
selectedImage = originalImage
self.image.image = selectedImage!
picker.dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(picker: UIImagePickerController!) {
self.dismiss(animated: true, completion: nil)
}
#IBOutlet var image: UIImageView!=nil
#IBAction func submit(_ sender: Any) {
if name.text != "" && email.text != ""
{
let entityDescription = NSEntityDescription.entity(forEntityName: "Table", in: context)
let item = Item(entity: entityDescription!, insertInto: context)
item.name = name.text
item.email = email.text
item.image = image.image!.pngData()! as NSData
do {
try context.save()
print("saved this moc")
} catch {
return
}
let UserDetailsVc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.navigationController?.pushViewController(UserDetailsVc, animated: true)
}
else
{
print("mail check")
let alertController1 = UIAlertController (title: "Fill Email id", message: "Enter valid email", preferredStyle: UIAlertController.Style.alert)
alertController1.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alertController1, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
if item != nil {
name.text = item?.name
email.text = item?.email
image.image = UIImage(data: (item?.image)! as Data)
}
}
This controller is used to fetch everything
class ViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate,NSFetchedResultsControllerDelegate{
var userarray: [Table] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userarray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
let name = userarray[indexPath.row]
cell.username.text = name.name
cell.showImage?.image = UIImage(data: (name.image)!)
return cell
}
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
override func viewWillAppear(_ animated: Bool) {
fetchData()
}
func fetchData(){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
userarray = try context.fetch(Table.fetchRequest())
print(userarray,"user")
}catch{
print("error")
}
}
}