I'm pulling data from CloudKit to put into an Array.
I have 3 items that can be pulled: Monday, Tuesday, Wednesday
I put them into an existingArray, but when I run the app I get: ["Monday", "Tuesday"], then as it runs thru a second time I get ["Monday", "Wednesday"], and I can't figure out why?
for days in results! {
let nD = DayClass()
nD.dayOfTheWeek = days[“D”] as! String
let defaults = NSUserDefaults.standardUserDefaults()
if var existingArr = defaults.arrayForKey("D") as? [String] {
if existingArr.contains(days["D"] as! String) == false {
existingArr.append(nd.dayOfTheWeek)
}
} else {
defaults.setObject([nD.dayOfTheWeek], forKey: "D")
}
}
Edit: If I add defaults.setObject([nD.dayOfTheWeek], forKey: "D") after I append, existingArray becomes ["Monday", "Tuesday"] then ["Tuesday", "Wednesday"]. I can't get it to just keep all 3 items in the existingArray.
You need to save the existingArr back to defaults after you append. Along the lines of:
for days in results! {
let nD = DayClass()
nD.dayOfTheWeek = days[“D”] as! String
let defaults = NSUserDefaults.standardUserDefaults()
if var existingArr = defaults.arrayForKey("D") as? [String] {
if existingArr.contains(days["D"] as! String) == false {
existingArr.append(nd.dayOfTheWeek)
defaults.setObject(existingArr, forKey: "D")
}
} else {
defaults.setObject([nD.dayOfTheWeek], forKey: "D")
}
}
Related
I have an array of Person. The Person object has many fields between them inscriptionDate (a timestamp). I have to create a collectionView from this array but using sections. Every section has a header that is inscriptionDate as a date having this format dd/mm/yyyy. I have to sort the array by inscriptionDate but without time (only the format dd/mm/yyyy) in order to load the data in the collectionView (taking into consideration the sections). I have found from another question this solution. But how can I sort the array before doing this? How can I use this:
order = NSCalendar.currentCalendar().compareDate(now, toDate: olderDate,
toUnitGranularity: .Day)
in my case?
I agree with #Vasilii Muravev, you will need to clean your timestamp object first, either using extensions or functions. Timestamp isn't a valid Swift object btw, unless that is a custom class you created.
Then you can create a dictionary for your dataSource. I will use #Vasilii Muravev's extension:
//var myKeys : [Date] = []
let sortedPeople = persons.sorted { $0.inscriptionDate.noTime() < $1.inscriptionDate.noTime() }
//break your array into a dictionary([Date : [Person]])
//personsSortedByDateInSections : [Date : [Person]] = [:]
for person in sortedPeople {
if personsSortedByDateInSections[person.inscriptionDate] != nil {
personsSortedByDateInSections[person.inscriptionDate]!.append(person)
} else {
personsSortedByDateInSections[person.inscriptionDate] = [person]
}
}
myKeys = setKeyArray(personSortedByDateInSections.keys)
that will give you a dictionary object with all of your Person objects grouped(sectioned) by their inscriptionDate. Then you will just need to fill out your collectionView delegate and datasource methods.
override func numberOfSections(in: UICollectionView) -> Int {
return myKeys.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return personsSortedByDateInSections[myKeys[section]].count
}
UPDATE:
As stated in your comment there is a issue with grabbing a array of the keys with swift dictionaries(I don't think swift dictionaries had this issue in earlier version? I could be wrong)....anyway to workaround this I have used this function to set a class variable for the 'keyArray'.:
fileprivate func setKeysArray(_ keys: LazyMapCollection<Dictionary<Date, [Person]>, String) -> [Date]{
var keysArray = [Date]()
for key in keys {
keysArray.append(key)
}
return keysArray
}
First, you'll need to clean timestamp before the sorting. You can do that by using Calendar and Date extension:
extension Date {
func noTime() -> Date! {
let components = Calendar.current.dateComponents([.day, .month, .year], from: self)
return Calendar.current.date(from: components)
}
}
Then you'll just need to sort your array by date without time:
let sortedByDate = persons.sorted { $0.inscriptionDate.noTime() < $1.inscriptionDate.noTime() }
Note. Be careful with compareDate function of Calendar, since it comparing only specific component. If in this example: NSCalendar.currentCalendar().compareDate(now, toDate: olderDate, toUnitGranularity: .Day) you'll have same days in different months, the comparing result will show that dates are equal.
Assuming your inscriptionDate is a Date, why can't you sort on that? Since Date conforms to Comparable all you need is
let sortedByDate = persons.sorted { $0.inscriptionDate < $1.inscriptionDate }
I wright a code for you I thing this code useful for you.
//
// ViewController.swift
// sortData
//
// Created by Bijender singh on 26/01/18.
// Copyright © 2018 Bijender singh. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
let myDataArray = NSMutableArray()
var myDataDic = NSMutableDictionary()
myDataDic.setValue("Bijender", forKey: "name")
myDataDic.setValue("1516965600", forKey: "date")
myDataArray.add(myDataDic)
myDataDic = NSMutableDictionary()
myDataDic.setValue("ben", forKey: "name")
myDataDic.setValue("1516965540", forKey: "date")
myDataArray.add(myDataDic)
myDataDic = NSMutableDictionary()
myDataDic.setValue("Deke", forKey: "name")
myDataDic.setValue("1516842180", forKey: "date")
myDataArray.add(myDataDic)
myDataDic = NSMutableDictionary()
myDataDic.setValue("Veer", forKey: "name")
myDataDic.setValue("1516842000", forKey: "date")
myDataArray.add(myDataDic)
myDataDic = NSMutableDictionary()
myDataDic.setValue("Ashfaq", forKey: "name")
myDataDic.setValue("1515981900", forKey: "date")
myDataArray.add(myDataDic)
print(myDataArray)
let sortedArray = self.sortDataWithDate(arrayData: myDataArray)
// sortedArray contane array which contan same date array
// you can use it according to you
// sortedArray.count is total diffrent dates in your array
// and (sortedArray.count[i] as! NSArray).count give you count of data of that date (0 < i < sortedArray.count)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sortDataWithDate(arrayData : NSArray) -> NSArray {
// var chat: [ChatMessage]!
let returnArray = NSMutableArray()
var subArray = NSMutableArray()
let arrayDate = NSMutableArray()
for i in 0 ..< arrayData.count {
let msgDate = self.timeStampToDate(_timestamp: ((arrayData[i] as! NSDictionary).object(forKey: "date") as! String), _dateFormat: "dd/MM/yyyy")
print("dddt \(msgDate)")
// print("date array \(arrayDate) $$ msgDate \(msgDate)")
if arrayDate.contains(msgDate) {
subArray.add(arrayData[i])
}
else{
arrayDate.add(msgDate)
if arrayDate.count > 1 {
returnArray.add(subArray)
}
subArray = NSMutableArray()
subArray.add(arrayData[i])
}
}
if subArray != nil {
returnArray.add(subArray)
}
print(returnArray)
return returnArray
}
func timeStampToDate(_timestamp : String, _dateFormat : String) -> String{
// _dateFormat = "yyyy-MM-dd HH:mm:ss.SSSS"
var date = Date(timeIntervalSince1970: TimeInterval(_timestamp)!)
date += TimeInterval(Int(TimeZone.current.secondsFromGMT()) as NSNumber)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "GMT") //Set timezone that you want
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = _dateFormat
let strDate = dateFormatter.string(from: date)
return strDate
}
}
In this code I insert array as hard coded
(
{
date = 1516965600;
name = Bijender;
},
{
date = 1516965540;
name = ben;
},
{
date = 1516842180;
name = Deke;
},
{
date = 1516842000;
name = Veer;
},
{
date = 1515981900;
name = Ashfaq;
}
)
And out put array is
(
(
{
date = 1516965600;
name = Bijender;
},
{
date = 1516965540;
name = ben;
}
),
(
{
date = 1516842180;
name = Deke;
},
{
date = 1516842000;
name = Veer;
}
),
(
{
date = 1515981900;
name = Ashfaq;
}
)
)
I am using xcode 8 swift 3. I have a mini App where I time myself how long I can answer the given questions, I have made it pass my time to the next VC and actually save it. I am wondering how do I save all the times I get in the App.
Button to save score
#IBAction func saveScore(_ sender: Any) {
label.text = label.text
UserDefaults.standard.set(label.text, forKey: "score")
}
User Defaults part
override func viewDidAppear(_ animated: Bool) {
if let x = UserDefaults.standard.object(forKey: "score") as? String {
label.text = x
}
}
You can store Array of Dictionary to UserDefaults which contains more values
let dict = ["score": "","userID":"1"]
let dict2 = ["score": "","userID":"2"]
let array = [dict,dict2]
UserDefaults.standard.set(array, forKey: "scoreCard")
And fetch like
if let x = UserDefaults.standard.array(forKey: "scoreCard") as? [[String:Any]] {
for dict in x {
//DO something
yourLabel.text = dict["score"] as! String
}
}
Hope it is helpful to you
I want the code to run once a day, but the way I want to accomplish this is by disabling the button after it is clicked and then reenabling when it has been more than 24 hours.
Would the code below be correct to just save the date the user pressed the button?
if distance < radius{
Total_Points += 10
pointsLabel.text = "Total Points: \(Total_Points)"
getPointsOutlet.isEnabled = false
let clickdate = UserDefaults.standard
if var timeList = clickdate.object(forKey: "timeList") as? [Date]{
timeList.append(Date())
clickdate.set(timeList, forKey: "timeList")
} else {
clickdate.set([Date()], forKey: "timeList")
}
clickdate.synchronize()
}
let PointsDefault = UserDefaults.standard
PointsDefault.setValue(Total_Points, forKey: "Total Points")
Your code
let clickdate = UserDefaults.standard
if var timeList = clickdate.object(forKey: "timeList") as? [Date]{
timeList.append(Date())
clickdate.set(timeList, forKey: "timeList")
} else {
clickdate.set([Date()], forKey: "timeList")
}
clickdate.synchronize()
Is fine for adding a date to an array of saved dates. You could pull that out into a separate function:
func addDateToDefaults(date: Date? = nil) {
let defaults = UserDefaults.standard
if date = nil {
date = Date()
}
if var timeList = defaults.object(forKey: "timeList") as? [Date]{
timeList.append(date)
defaults.set(timeList, forKey: "timeList")
} else {
defaults.set([date!], forKey: "timeList")
}
}
Then you could call that from a button action:
#IBAction func buttonClicked(_ sender: UIButton) {
addDateToDefaults()
//The rest of your button action code goes here
}
With the function addDateToDefaults() above, you can pass in a specific date, or leave off the date parameter and it will append the current date to your array of dates.
You can achieve in this way when you tapped on button save the date value using Userdefaults and then inside your ViewDidAppear, ViewDidload and UIApplicationWillEnterForeground notification method put the check to get dateValue from user defaults and then take the difference of current date and last stored date and accordingly enabled your button.
lazy var userDefaults: UserDefaults = {
return UserDefaults.standard
}()
func ViewDidLoad() {
UserDefaults.set(Date(), forKey:"date")
userDefaults.synchronize()
}
func someMethodWhereYouWantToGetValue() {
guard let value = userDefaults.object(forKey: "date") as? Date else
{return}
print(value)
}
I am on a Timer project that takes a value from a variable through unwind segue to pass it to another view controller, then append its value to an Array that should be used to insert a row with the task name whenever the user pressed save button I need to save the result permanently, but I am confused which data should I save the value of the variable itself, the row, or the array?
var taskList = [String]()
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
let name = newItemViewController.itemName
let date = newItemViewController.date
print("itemName passed is: \(name)")
if name == "" {
}
else {
print(date)
taskList.append(name!)
let indexToInsert = taskList.count == 0 ? 0 : taskList.count - 1
let indexPath = IndexPath(row: indexToInsert, section: 0)
tableView.insertRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
add task view controller
Timer / Task List
Ok, thanks to the hint of Matt Le Fleur
I solved the issue by using objects as below:
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
var name = newItemViewController.itemName
let date = newItemViewController.date
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(name!)
print(items)
} else {
items = [name!]
}
UserDefaults.standard.set(items, forKey: "items")
name = ""
}
Then added a ViewDidAppear as below:
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
tableView.reloadData()
}
Is there any way to save multiple values for the same key in NSUserDefaults? I seem to only be saving the last value, even though I have multiple values for the "Day" key.
Day:
for days in results! {
let nD = DayClass()
nD.dayOfTheWeek = days[“D”] as! String
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(nD.dayOfTheWeek, forKey: “Day”)
}
I've tried creating an array as a possible solution, but I'm still only getting the last value when I log it in my console:
var emptyArray = [String]()
emptyArray.append(dayOfTheWeek)
print("Array: \(emptyArray)")
print("ArrayCount: \(emptyArray.count)")
You can do this. First check if there is already an Day array in the prefs. If so then add the new dayOfTheWeek to that array and store it. Else create a new one and store it.
for days in results! {
let dayOfTheWeek = days["D"] as! String
//check if there is already an existing Day array in prefs
if var existingArr = NSUserDefaults.standardUserDefaults().arrayForKey("Day") as? [String] {
//if so append the new value to that array and store it
if existingArr.contains(dayOfTheWeek) == false {
existingArr.append(dayOfTheWeek)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(existingArr, forKey: "Day")
defaults.synchronize()
}
} else {
//create a new array and store it
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject([dayOfTheWeek], forKey: "Day")
defaults.synchronize()
}
}
let daysArray = ["Sat", "Sun", "Mon", "Tu", "Wed", "Th", "Fri"]
// Set Array Into NSUserDefaults
NSUserDefaults().setObject(daysArray, forKey: "day")
// Retrive Array from NSUserDefaults
if let daysOfWeek = NSUserDefaults().arrayForKey("day") as? [String] {
for item in daysOfWeek {
print(item)
}
}
var food: [String]
{
get {
if let returnValue =
NSUserDefaults.standardUserDefaults().objectForKey("food") as? [String] {
return returnValue
} else {
return ["muesli", "banana"] //Default value
}
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "food")
NSUserDefaults.standardUserDefaults().synchronize()
}
}
override func viewDidLoad() {
super.viewDidLoad()
print(food) // prints: ["muesli", "banana"] (at first launch)
food = ["cake"]
print(food) // prints: ["cake"]
food += ["spaghetti"]
print(food) // prints: ["cake", "spaghetti"]
food = []
print(food) // prints: []
NSUserDefaults.standardUserDefaults().setObject(nil, forKey: "food")
print(food) // prints: ["muesli", "banana"]
}