Populating UIPickerview values from CoreData in Swift - swift

I am trying to populate a UIPicker with data that is stored in coreData. they way i have gone about this is i have set up a global variable array and when the screen loads a fetch request is sent to core data which populates the global array. The UIPicker then reads this global array and populates itself with its array of strings.
the problem is that the UIPicker populates fine the first time i load it, but then the second time i get the original entries repeated, and the 3rd time and 4th etc...
global array:
var savedWorkoutNamesPulled = [String]()
UIPicker set up:
//needed picker function (number of columns in picker view)
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
//needed picker function (number of rows in pickr view)
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
savedWorkoutNamesPulled.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
//setting exercisesInSelectedWorkout(exercise names within workout) to the elements of the uipicker
let row = savedWorkoutNamesPulled[row]
return row
}
Calling coredata function:
override func viewDidAppear(_ animated: Bool) {
//calling function that deals show picker
showUiPicker()
//going into core data swift file and calling a function (this function sets global array to fill workout names into picker view)
print("your workouts are as follows")
CoreDataManager.sharedInstance.retrieveWorkoutDataFromCoreData()
}
The core data function that appends values to global array:
func retrieveWorkoutDataFromCoreData() -> [Contact]{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ContactsData")
let result = try? context.fetch(fetchRequest)
var contacts = [Contact]()
for data in result as! [NSManagedObject]{
guard let id = data.value(forKey: "id") as? String else {continue}
guard let fullName = data.value(forKey: "fullName") as? String else {continue}
guard let excercise = data.value(forKey: "excersizes") as? [Exercise] else {continue}
var contact = Contact(id: id, fullname: fullName, exercises: excercise)
contact.id = id
contacts.append(contact)
print(fullName)
print(id)
savedWorkoutNamesPulled.append(fullName)
}
return contacts
}

I was able to fix it by just by setting the array to nothing when the view was dismissed.
override func viewDidDisappear(_ animated: Bool) {
savedWorkoutNamesPulled = []
}

Related

How can I get value of a Dictionary item from first item of an Array in Swift?

I have defined Dictionary in an Array object below:
var dataList : [[String:Any]]?
And then I have loaded some data in to it. Here is the output when i run this code below:
print("DATA LIST:\n \(myRecordList)")
Output:
Optional([["itemCode": 0, "itemText": please select city], ["itemCode": 1, "itemText": City A], ["itemCode": 2, "itemText": City B], ["itemCode": 3, "itemText": City C], ["itemCode": 4, "itemText": City D], ["itemCode": 5, "itemText": City E], ["itemCode": 6, "itemText": City F]])
I would like to reach value of a dictionary from first item in an Array:
As you know, here is the first item in an array:
["itemCode": 0, "itemText": please select city]
And here is the value in above dictionary:
"itemText": please select city
After that I would like to set above text ("please select city") as a button title if dataList count is not zero:
if dataList?.count != 0
{
**//need help in here!!!**
{
self.btnSelectMarkaOutlet.setTitle(tempBtnTitle, for: .normal)
}
}
I have tried below scope. But it crushed:
if let newTry = dataList![0]["itemText"] as? [String]
{
print("here is the output:: \(newTry[0])")
}
Here is the error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
let tempBtnTitle = must be "please select city"
I wanted to do this.
Let me share whole view controller file. Because dataList is nil.
import UIKit
class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var btnSelectMarkaOutlet: UIButton!
#IBOutlet weak var pickerViewOutlet: UIPickerView!
var dataList : [[String:Any]]?
override func viewDidLoad() {
print("viewDidLoad")
super.viewDidLoad()
self.pickerViewOutlet.delegate = self
self.pickerViewOutlet.dataSource = self
self.loadSayacRecordsForTheUIPickerView()
print("Is the data list nil? : \(self.dataList)")
if let newTry = self.dataList![0]["itemText"] as? String
{
print("here is the output:: \(newTry)")
}
//I WANTED TO SET BUTTON TITLE !!
let tempButtonTitle = ""
self.btnSelectMarkaOutlet.setTitle(tempButtonTitle, for: .normal)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let recordName = self.dataList![row]
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
//return self.cityList![row]
let info = self.dataList![row]
if let name = info["itemCode"] as? String,
let code = info["itemText"] as? String
{
let text = "\(name) - \(code)"
//print("am i here? \(text)")
return text
}
return nil
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.dataList == nil ? 0 : self.dataList!.count
}
func loadSayacRecordsForTheUIPickerView()
{
print("loadSayacRecordsForTheUIPickerView method is called")
ServiceManager.oServiceManager.loadSayacRecords()
{
(result) in
if let sayacRecords = try? JSONSerialization.jsonObject(with: result!, options: .mutableContainers) as? [String:Any]
{
if let resultCodeFromWebApi = sayacRecords?["resultCode"] as? String
{
DispatchQueue.main.async
{
print("loadSayacRecordsForTheUIPickerView resultCode: \(resultCodeFromWebApi)")
if resultCodeFromWebApi == "999"
{
if let myRecordList = sayacRecords?["recordList"] as? [[String:Any]]
{
//DispatchQueue.main.async
//{
self.dataList = myRecordList
print("DATA LIST:\n \(self.dataList)")
self.pickerViewOutlet.reloadAllComponents()
//}
}
} // resultCodeFromWebApi == 999 ENDS
else
{
if let resultMessageFromWebApi = sayacRecords?["resultMessage"] as? String
{
print("resultMessage: \(resultMessageFromWebApi)")
}
}
} // DispatchQueue.main.async ENDS
}
else
{
print("cant parse it")
} // if let resultCodeFromWebApi = loginResult?["resultCode"] as? Int ENDS
}
}
} // loadSayacRecordsForTheUIPickerView ENDS
} // class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource ENDS
The problem is in the logic you implemented: the dataList is loaded asynchronously, which means that you don't know when it will be available, but you are using it like it's always available.
Example of this error is in the viewDidLoad:
self.loadSayacRecordsForTheUIPickerView()
print("Is the data list nil? : \(self.dataList)")
if let newTry = self.dataList![0]["itemText"] as? String
{
print("here is the output:: \(newTry)")
}
With the first line you start to load the dataList, but in the for loop you are force unwrapping, which means you are saying "Ehi I'm 100% sure it will be available" - which is not true.
A possible solution for your problem is to convert the dataList! to dataList? everywhere, so if it's not loaded then nothing happens and your app won't crash.
A better and quicker solution is to have a default value for dataList, so in case is not loaded you will just react to an empty data model:
var dataList : [[String:Any]] = [[String:Any]]()
A reason would be array is nil and also you are unwrapping with [String] which is String, which may give nil.
Try below to avoid crashes.
if dataList?.isEmpty == false,let firstDic = dataList?.first, let neededString = firstDic["itemText"] as? String {
}

Realm Array and child relationships

I am trying to make a List in relation to my realm array. I don't know if it is possible to take a hard coded realm array and give each string its own list. Currently I have my array in a table view and when a row is selected it segues to its own viewController. I am trying to get each selected row to contain its own list. Here's the code
Data Model 1
import Foundation
import RealmSwift
class DateChange: Object {
#objc dynamic var itemId : String = UUID().uuidString
override static func primaryKey() -> String? {
return "itemId"
}
let dates = List<String>()
let selection = List<Home>()
convenience init(tag: String) {
self.init()
}
}
Data Model 2
class Home: Object {
#objc dynamic var itemId : String = UUID().uuidString
override static func primaryKey() -> String? {
return "itemId"
}
var parentCategory = LinkingObjects(fromType: Home.self, property: "selection")
View Controller 1
class WeekOfViewController: NSViewController {
let post = DateChange(tag: "")
post.dates.append("December 30th - January 5th")
post.dates.append("January 13th - January 19th")
}
func numberOfRows(in tableView: NSTableView) -> Int {
return 2
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "dateCell") , owner: self) as! NSTableCellView?
cell?.textField?.stringValue = post.dates[row]
return cell
}
func tableViewSelectionDidChange(_ notification: Notification) {
self.performSegue(withIdentifier: "selectedDate", sender: self)
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
// Unwrap the segue's identifier.
guard let identifier = segue.identifier else { return }
// Make sure this is the segue we care about.
if identifier == "selectedDate" {
let secondVC = segue.destinationController as! ViewController2
// Figure out which row was selected.
if let selectedRow = dateTableView?.selectedRow {
secondVC.selectedDate = post.dates[selectedRow]
}
View Controller 2
class ViewController2: NSViewController {
#IBAction func saveData(_ sender: NSButton) {
if let appendDate = selectedDate {
do {
try realm?.write {
let homeData = Home()
homeData.done = false
appendDate.dates.append()
}
} catch {
print("There was an error saving")
}
}
}
Yes. You can make multiple dimensional arrays.
Example:
1. Year object has a list of months
2. Month object has a list of days
3. Day object has a list of hours
4. etc, etc
When you app launches, you create a loop which initializes a Year, then initializes lots of months and appends them into the Year.Month_Array. Do the same for days_array in each month.
To save in Realm, call:
try! realm.write {
realm.add(Year)
}
Now you can read out you multi-level Realm object anytime you wish.
If my answer isn't clear, please let me know
The exact use case is a bit unclear but it seems that the app is a Master->Detail configuration where the master page contains a list of dates and then when a date is tapped, it segues to a detail page with further info.
Here's an example of code to handle the objects. You know how to populate a tableView with it's delegate methods so I'm omitting that part.
Suppose we want a list of events on the master page and then the activities of each event on the detail page. Start with two managed Realm objects; the event object which has a start and end date, the event title and the activities list within that event. Then there's the activity object.
class EventClass: Object {
#objc dynamic var start_date: Date?
#objc dynamic var end_date: Date?
#objc dynamic var event_title = ""
let activities = List<ActivityClass>()
}
class ActivityClass: Object {
#objc dynamic var title = ""
}
write an event with some activities to Realm
let formatter = DateFormatter()
formatter.dateFormat = "yyyymmdd"
let e0 = EventClass()
e0.event_title = "Workshop"
e0.start_date = formatter.date(from: "20180113")
e0.end_date = formatter.date(from: "20180119")
let a0e0 = ActivityClass()
a0e0.title = "Some activity"
let a0e1 = ActivityClass()
a0e1.title = "Another activity"
let a0e2 = ActivityClass()
a0e2.title = "Activity continues"
e0.activities.append(a0e0)
e0.activities.append(a0e1)
e0.activities.append(a0e2)
// write event 0 (e0) to realm which will create the event and activities
We are assuming both the master and detail views have tableViews, so load the events into the master tableView dataSource which is a Realm results class - it behaves like an array.
class ViewController: NSViewController {
var eventResults: Results<EventClass>? = nil
and then whenever it's time to populate the dataSource:
self.eventResults = realm.objects(EventClass.self)
and then the tableView delegate methods can get each row for display. It would look something like this on the master page
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "dateCell") , owner: self) as! NSTableCellView?
//cell?.textField?.stringValue = post.dates[row]
let event = self.eventResults[row]
cell.textField?.stringValue = "Event: \(title) from: \(start) - \(end)"
return cell
}
so then the tableView would look something like this
Event: Workshop from: 2018-12-30 - 2018-01-05
Event: Workshop from: 2018-01-13 - 2018-01-19
etc
when a row is tapped, get that event object from the master tableView datasource, self.eventResults
let tappedEvent = self.eventResults[tappedRowIndex]
and then get the activity list
let activityList = tappedEvent.activities
that activityList can be passed via the segue (or 1000 other ways) to the detail viewController and it becomes the dataSource for the detail tableView. The detail page follows the same pattern except the dataSource is the activities.
Does that help?

Accessing information from another viewController

I am new to swift and I am trying to repopulate a pickerview using an array of names from another class. I have this class Notes that creates notes and each note has a name/title, I am trying to access those names/title and put them in pickerview, can anyone help me.
import UIKit
var allNotes: [Note] = []
var currentNoteindex: Int = -1
var noteTable: UITableView?
let kAllNotes:String = "notes"
class Note: NSObject
{
var date:String
var note:String
var name:String
override init()
{
date = NSDate().description
name = ""
note = ""
}
func dictionary() -> NSDictionary
{
return ["note": note, "date": date, "name": name]
}
I have two different pickerview and one works it's just the one with the names doesn't work.
override func viewDidLoad()
{
super.viewDidLoad()
fileNamePicker.dataSource = self
fileNamePicker.delegate = self
gradePicker.dataSource = self
gradePicker.delegate = self
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
if pickerView == gradePicker
{
return grades.count
}
else
{
return allNotes.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
if pickerView == gradePicker
{
return grades[row]
}
else
{
//This is where the Problem is, but I don't know how to fix it.
let nameFiles = allNotes[currentNoteindex].name
return nameFiles[row]
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int
{
return 1
}
nameFiles is accessing a name from a note.
nameFiles[row] doesn't work because nameFiles is not an array.
If you want to return a string, simply do return nameFiles. Check first to make sure that nameFiles actually has a value, though!
Looks like your allNotes array is empty. You have initilaize but does not seems to append the object. Create initiliaze method inside Note class
init(_ date: String, note: String, name: String) {
self.date = date
self.note = note
self.name = name
}
In your viewcontroller class try appending like this. Now below you can access inside picker view method:-
//You can append by doinf iteration based on your data
allNotes.append(Note(date: "value1", note: "Value", name: "name"))

Json parsing into swift 3 currency exchange values from bank accounts

Is it possible to fetch currency exchange rates from some banks websites in my country, and is it legal ?
making an app specified to my country and have been trying for a week now but couldn't find an answer this is the code could you tell a solution?
import UIKit
class ViewController: UIViewController,UIPickerViewDelegate,UIPickerViewDataSource {
#IBOutlet weak var lblDisplay: UILabel!
#IBOutlet weak var txtField: UITextField!
#IBOutlet weak var pkrView: UIPickerView!
var myCurrency:[String] = []
var myValue:[Double] = []
var activeCurrency:Double = 0
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://ca-egypt.com")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error)in
if error != nil{
print("error is here!")
}else{
if let content = data{
do{
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let rates = myJson["rates"] as? NSDictionary{
var firstOne = true
for (key,value)in rates{
self.myCurrency.append((key as? String)!)
self.myValue.append((value as? Double)!)
}
}
}catch{
}
}
}
self.pkrView.reloadAllComponents()
}
task.resume()
}
#IBAction func btnCalculateAction(_ sender: UIButton) {
lblDisplay.text = String(Double(txtField.text!)! * activeCurrency)
}
func numberOfComponents(in pickerView: UIPickerView,umberOfRowsInComponent component: Int) -> Int {
return myCurrency.count
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return myCurrency.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return myCurrency[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
activeCurrency = myValue[row]
}
}
Image of the Error:
Hello #Ali instead of fetching currency exchange rates from some banks websites, you can make use of some API, which are freely available or you can make use of paid API's. And off-course fetching currency exchange rates is illegal. They won't allow some-one to get values.
To get various countries currency-exchange rates use Fixer.io , which are updating on daily basis, and yes it is freely available.
Fixer.io is a free JSON API for current and historical foreign exchange rates, published by the European Central Bank. The rates are updated daily around 4PM CET.
Example :
API Link : http://api.fixer.io/latest?base=USD
Alamofire.request("http://api.fixer.io/latest?base=USD") .responseJSON { response in
if let arr = response.result.value as? [String:AnyObject]
{
let usd_val = (arr["rates"]?["INR"] as? NSNumber)!
print(usd_val)
}
}
By this way you can parse JSON API and use this currency exchange values, and if you want to get your country currency exchange value, then pass value.
example : To get current currency exchange value of Japan, then pass value of it. Like this. http://api.fixer.io/latest?base=JPY
Hope it helps you.

Issue saving PFobjects into an array when using pickerView

i'm relatively new to Swift. I've encountered an issue when using PFQuery in pickerView.
I'm trying to implement a 2 component pickerView as demonstrated below:
Component 0: Component 1
"A": "Altivo", "Altrez"
"B": "Beverly", "Bijou"
Choosing "A" from Component 0 will show "Altivo", "Altrez" in Component 1
The values from Component 1 are queried from Parse by using the value from Component 0
Here's my code:
var condoNameList: [String] = [String]()
var condoPicker = UIPickerView()
var condoAlphabet: [String] = [String]()
private let prefixComponent = 0
private let condoNameComponent = 1
override func viewDidLoad() {
super.viewDidLoad()
condoAlphabet = ["A", "B"]
}
//Start: For condoName picker
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == prefixComponent{
return condoAlphabet.count
}else {
return self.condoNameList.count
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == prefixComponent {
condoNameList.removeAll()
let query = PFQuery(className: "condodirectory")
let selectedalphabet: String = condoAlphabet[row] as String!
query.whereKey("condoName", hasPrefix: selectedalphabet)
query.findObjectsInBackgroundWithBlock{
(objects:[PFObject]?, error:NSError?)->Void in
if error == nil{
for object in objects!{
//print(object)
let condoName:String = object["condoName"] as! String
self.condoNameList.append(condoName)
}
}
//print("condo Name List is \(self.condoNameList)") //Position 1
}
print("condo Name List is \(self.condoNameList)") //Position 2
condoPicker.reloadComponent(condoNameComponent)
condoPicker.selectRow(0, inComponent: condoNameComponent, animated: true)
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == prefixComponent{
return condoAlphabet[row]
}else {
return self.condoNameList[row]
}
}
The issue is at Position 1, the print function is able to print out the condoNameList but at Position 2 it prints an empty array. As a result, the list for Component 1 is not showing up in the PickerView.
Anyone knows why?
Thanks in advance for the help.
I managed to solve the problem from another thread by using the didset{} method.
var condoNameList: [String] = [] {
didSet {
self.condoNameListTemp = condoNameList
condoName.inputView = condoPicker
condoPicker.reloadAllComponents()
}
}