In my app I am we are sending TimeStamp along with parameters for API calls to get data from. I could use any random string to pass with the values but I am using TimeStamp because it will be different everytime so that I will get fresh data everytime without cashing. Now our requirement is to update TimeStamp every hour so that everytime when I make an API call instead of showing fresh data everytime, data will be updated every hour.
My API looks like this:
let url = "https://myurl.api.getthisapidata?id=\(someID)&nocache=\(timeStamp)"
Now in the place of TimeStamp I want to send some random string like "\(arc4random())". But I want this random string to be changed only after an hour so that I go to some other view or closes the app and opens it before an hour this random string should remain same.
I hope you understand what I am trying to convey.
TimeStamp extenstion:
extension Date {
var ticks: UInt64 {
let timeStamp = UInt64((self.timeIntervalSince1970 + 62_135_596_800) * 10_000_000)
return timeStamp
}
}
Usage:
print(Date().ticks)
Get current hour value from current date using component(_:from:) method. Then append a random string with it.
class RandomStringGenerator: NSObject {
static let shared = RandomStringGenerator()
var oldString:String {
get {
return UserDefaults.standard.string(forKey: "oldString") ?? ""
}
set {
UserDefaults.standard.set(newValue, forKey: "oldString")
}
}
func getString() -> String {
let random = Int64.random(in: 10_000_00..<10_000_000)
let hour = Calendar.current.component(.hour, from: Date())
if !oldString.hasPrefix("\(hour)") {
oldString = "\(hour)" + "\(random)"
}
return oldString
}
}
Related
This might be a weird question because I don't fully understand how transient and the new derived properties work in Core Data.
So imagine I have a RegularClass entity, which stores any class that repeats over time. A class can repeat for example every third day or every one week. This is how it looks in the data model:
(a RegularClass belongs to a Schedule entity, which in turn belongs to a Course entity)
Now, if our class repeats every third day, we store the number 3 in the frequency property, and a string "days" in unit property, which is then converted to an enum in Swift. A Schedule, which every RegularClass belongs to, has a startDate property.
To check if a class happens at a given date, I came up with nothing better than calculating the difference in specified unit between the startDate and the given date, then taking a remainder between the difference and frequency, and if it's 0, than it's the date in which a class can occur.
var differenceComponent: Calendar.Component {
switch unitType {
case .weeks:
return .weekOfYear
case .days:
return .day
}
}
func getDifferenceFromDateComponents(_ dateComponents: DateComponents) -> Int? {
switch unitType {
case .weeks:
return dateComponents.weekOfYear
case .days:
return dateComponents.day
}
}
func dateIsInActiveState(_ date: Date) -> Bool {
if let startDate = schedule?.startDate {
let comps = Calendar.current.dateComponents([differenceComponent], from: startDate, to: date)
if let difference = getDifferenceFromDateComponents(comps) {
let remainder = Int64(difference) % frequency // that is the key!
return remainder == 0
}
}
return false
}
func containsDate(_ date: Date) -> Bool {
if dateIsInActiveState(date) {
if unitType == .days {
return true
}
let weekday = Calendar.current.component(.weekday, from: date)
return (weekdays?.allObjects as? [Weekday])?.contains(where: { $0.number == weekday }) ?? false
}
return false
}
Now, the thing is that this code works perfectly for courses that I've already got from a fetch request. But is there a way to pass a date parameter in a NSPredicate to calculate this while request happens? Or do I have to fetch all the courses and then filter them out manually?
To solve this issue you could store your data as scalar types and then do simple arithmetic in your predicate. Rather than dates, use integers with a days-from-day-zero figure (or whatever minimum unit of time is necessary for these calculations). Store your repeat cycle as number-of-days.
Then you can use the calculation ((searchDate - startDate) mod repeatCycle) == 0 in your predicate to find matching classes.
As you have suggested, it might be sensible to denormalise your data for different search cases.
Realm doesn't support DateInterval to be store into the database. For now our team do the following:
private let _intervalBegins = List<Date>()
private let _intervalEnds = List<Date>()
var dateIntervals: [DateInterval] {
get {
var intervals = [DateInterval]()
for (i, startDate) in _intervalBegins.enumerated() {
let endDate = _intervalEnds[i]
intervals.append(DateInterval(start: startDate, end: endDate))
}
return intervals
}
set {
_intervalBegins.removeAll()
_intervalBegins.append(objectsIn: newValue.compactMap{ $0.start })
_intervalEnds.removeAll()
_intervalEnds.append(objectsIn: newValue.compactMap{ $0.end })
}
}
Is there a more "proper" way to do this? Maybe to store both the start and end dates into one property/database column? And get those value directly without "parsing" them with another variable as we do now.
Thanks!
As you notice, Realm doesn't support DateInterval, but Realm is able to save your custom objects. In this case you can create your own RealmDateInterval (or so) and create initializer, that allows you to create object from DateInterval:
dynamic var start: Date = Date()
dynamic var end: Date = Date()
convenience init(dateInterval: DateInterval) {
self.init()
self.start = dateInterval.start
self.end = dateInterval.end
}
Next thing, when you retrieve RealmDateInterval from Realm you really want DateInterval instead. Here you can create a bridge function, that can convert RealmDateInterval to DateInterval or create a protocol with convert func and
adopt it to RealmDateInterval (i.e. clearly show everybody RealmDateInterval has specific functionality).
protocol DateIntervalConvertible {
func toDateInterval() -> DateInterval
}
extension RealmDateInterval: DateIntervalConvertible {
func toDateInterval() -> DateInterval {
return DateInterval(start: start, end: end)
}
}
Currently, there is an expiration date in the database. I would like the app to delete the post after the expiration date is passed, even while the user is currently using the app. Currently, the code checks the feed, collects the time, and if the time is less than the current time, the post is deleted from the feed. However, this does not continually compare the time; rather, it only compares the current time with the time in the database only when the value in the database updates or the app is restarted. How can I make it such that the app will continually compare the time interval since 1970 with the time in the database, even while the app is running? I.e even when the user is using the app, if the current time is greater than the expiration time it will delete it from the database?
func deletePosts() {
let userID = Auth.auth().currentUser?.uid
let deletionReference = Database.database().reference().child("feeds").child(userID!)
deletionReference.observe(.childAdded) { (snapshot) in
let feedPostID = snapshot.key
print(feedPostID)
let deletionPostTimeReference = Database.database().reference().child("postStatus").child(feedPostID)
deletionPostTimeReference.observe(.value, with: { (snapshot) in
print("damn")
if let value = snapshot.value as? NSDictionary {
print("shit")
let timeOfDeletion = value["timeOfDeletion"] as? Int ?? 0
print(timeOfDeletion)
if timeOfDeletion == 0 {
print("we aint deleting")
}
else {
print("time TO DELETE")
let date = Date()
let currentTime = UInt64(floor(date.timeIntervalSince1970 * 1000))
if currentTime >= timeOfDeletion {
deletionReference.child(feedPostID).removeValue()
if let index = self.postList.index(where: {$0.postID == feedPostID}) {
self.postList.remove(at: index)
self.orderedPostList = self.postList.sorted(by: { $0.revealDate! > $1.revealDate! })
self.tableView.reloadData()
}
}
}
}
})
}
}
I have a function that converts minutes to either a decimal or a HH:MM string based on a user preference in NSUserDefaults.
For example, 90 minutes would be either 1.5 or 1:30.
Here's my function:
func decimalOrHHMM(value:Int) -> String{
let totalMinutes = Double(value)
let defaults = UserDefaults.standard
if defaults.string(forKey: "displayTotalsAs") == "hhmm"{
//HH:MM
let hours = floor(totalMinutes/60)
let minutes = totalMinutes.truncatingRemainder(dividingBy: 60) //This gives us the remainder
let hrs = String(format: "%.0f", hours) //Remove tenths place
var mins = ""
if minutes < 10{
//Prepend 0
mins = String(format: "0%.0f", minutes)
}else{
mins = String(format: "%.0f", minutes)
}
return "\(hrs):\(mins)"
}else{
//Decimal
return String(format: "%.1f", totalMinutes/60)
}
}
This works great, but I'm wondering if this can be converted to an NSNumberFormatter Swift extension somehow. I'm having trouble knowing what I need to override in order to do all the converting.
Any idea how I can make this an extension?
This is the basic structure for a Swift extension, you could use a function instead of a computed property, but since your original function takes an Int value, a computed property makes sense.
extension Int {
var decimalOrHHMM: String {
return "\(self)" // do your actual conversion here
}
}
You can also choose to extend NSNumberFormatter.
Thanks to a variety of helpful posts in this forum, I have some code that works for obtaining the Creation Date of a single user-selected NSURL. However, I cannot get the code to work for either a hard-coded NSURL, nor within a loop through an NSFileManager enumerator.
I am not a professional programmer; I make apps that are tools for office. My ultimate goal is to simply sort an Array of NSURL objects based on Creation Date.
The code I am using is below, which functions just fine as is, but if I try to use the commented line to evaluate a specific PDF file, I get the following error:
I get the exact same error when I try to add this code to a loop of NSURL objects procured via the NSFileManager Enumerator.
I cannot figure out how to use the error instruction to solve the problem. If anyone can assist, that would be tremendous. Thank you.
let chosenURL = NSOpenPanel().selectFile
//let chosenURL = NSURL.fileURL(withPath: "/Users/craigsmith/Desktop/PDFRotator Introduction.pdf")
do
{
var cr:AnyObject?
try chosenURL?.getResourceValue(&cr, forKey: URLResourceKey.creationDateKey)
if (cr != nil)
{
if let createDate = cr as? NSDate
{
print("Seems to be a date: \(createDate)")
let theComparison = createDate.compare(NSDate() as Date)
print("Result of Comparison: \(theComparison)") // Useless
let interval = createDate.timeIntervalSinceNow
print("Interval: \(interval)")
if interval < (60*60*24*7*(-1))
{
print("More than a week ago")
}
else
{
print("Less than a week ago")
}
}
else
{
print("Not a Date")
}
}
}
catch
{
}
You can extend URL as follow:
extension URL {
/// The time at which the resource was created.
/// This key corresponds to an Date value, or nil if the volume doesn't support creation dates.
/// A resource’s creationDateKey value should be less than or equal to the resource’s contentModificationDateKey and contentAccessDateKey values. Otherwise, the file system may change the creationDateKey to the lesser of those values.
var creation: Date? {
get {
return (try? resourceValues(forKeys: [.creationDateKey]))?.creationDate
}
set {
var resourceValues = URLResourceValues()
resourceValues.creationDate = newValue
try? setResourceValues(resourceValues)
}
}
/// The time at which the resource was most recently modified.
/// This key corresponds to an Date value, or nil if the volume doesn't support modification dates.
var contentModification: Date? {
get {
return (try? resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate
}
set {
var resourceValues = URLResourceValues()
resourceValues.contentModificationDate = newValue
try? setResourceValues(resourceValues)
}
}
/// The time at which the resource was most recently accessed.
/// This key corresponds to an Date value, or nil if the volume doesn't support access dates.
/// When you set the contentAccessDateKey for a resource, also set contentModificationDateKey in the same call to the setResourceValues(_:) method. Otherwise, the file system may set the contentAccessDateKey value to the current contentModificationDateKey value.
var contentAccess: Date? {
get {
return (try? resourceValues(forKeys: [.contentAccessDateKey]))?.contentAccessDate
}
// Beginning in macOS 10.13, iOS 11, watchOS 4, tvOS 11, and later, contentAccessDateKey is read-write. Attempts to set a value for this file resource property on earlier systems are ignored.
set {
var resourceValues = URLResourceValues()
resourceValues.contentAccessDate = newValue
try? setResourceValues(resourceValues)
}
}
}
usage:
print(yourURL.creationDate)
According to the header doc of URL and URLResourceValues, you may need to write something like this:
(This code is assuming chosenURL is of type URL?.)
do {
if
let resValues = try chosenURL?.resourceValues(forKeys: [.creationDateKey]),
let createDate = resValues.creationDate
{
//Use createDate here...
}
} catch {
//...
}
(If your chosenURL is of type NSURL?, try this code.)
do {
if
let resValues = try (chosenURL as URL?)?.resourceValues(forKeys: [.creationDateKey]),
let createDate = resValues.creationDate
{
//Use createDate here...
print(createDate)
}
} catch {
//...
}
I recommend you to use URL rather than NSURL, as far as you can.
in swift 5 I use the following code:
let attributes = try! FileManager.default.attributesOfItem(atPath: item.path)
let creationDate = attributes[.creationDate] as! Date
sort array with the following code
fileArray = fileArray.sorted(by: {
$0.creationDate.compare($1.creationDate) == .orderedDescending
})
more about FileAttributeKey here: https://developer.apple.com/documentation/foundation/fileattributekey