if let ISO8601DateFormatter from String - swift3

I have a response that returns the date of an object and Sometimes it returns the year and sometimes returns the date with the ISO8601DateFormatter style (1995-01-01T00:00:00Z)
The question is how can i get a if let that can identify when the value is a year as String or a ISO8601Date from a string.

I ended up doing this:
let pulledDate = "1995-01-01T00:00:00Z"
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime]
if let date = dateFormatter.date(from: pulledDate ?? ""){
let units: Set<Calendar.Component> = [.year]
let comps = Calendar.current.dateComponents(units, from: date)
print("It is a date year = \(comps.year)")
}
else{
print("It is not a date")
}

Related

Run CKQueryOperation with results from previous CKQueryOperation

I have an app that is a shopping list. I can store prices per product and vendor in my app, the model is
Product
Vendor
Price
One product can have multiple prices from different vendors.
I store the price information with references to the product and vendor (CKRecord.Reference).
Now I am using the below to fetch all the prices related to a product:
public func fetchDataByProduct(product: Product, completionHandler: #escaping (Bool) -> Void){
self.pricesBuffer = []
let cloudContainer = CKContainer.init(identifier: "iCloud.XYZ")
let publicDatabase = cloudContainer.publicCloudDatabase
let reference = CKRecord.Reference(recordID: product.recordID, action: .deleteSelf)
let predicate = NSPredicate(format: "priceToProduct == %#", reference)
let query = CKQuery(recordType: "Price", predicate: predicate)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { record in
let price = Price()
price.recordID = record.recordID
price.grossPrice = record.object(forKey: "grossPrice") as? Double
let dummy = record.object(forKey: "priceToVendor") as! CKRecord.Reference
price.vendorRecordID = dummy.recordID
self.pricesBuffer.append(price)
}
operation.queryCompletionBlock = { [unowned self] (cursor, error) in
self.pricesBuffer.forEach({price in
price.retrieveVendor()
})
DispatchQueue.main.async {
if error == nil {
self.prices = self.pricesBuffer
completionHandler(true)
} else {
}
}
}
publicDatabase.add(operation)
}
My problem is now that I cannot retrieve the vendor name which is part of the Vendor object (Vendor.name).
I have tried to loop over the pricesBuffer and run this one per price but the problem seems to be that CloudKit first completes the initial request to fetchDataByProduct() and then afterwards fetches the vendor data but then its too late because that updated data does not get pushed to my View (SwiftUI).
publicDatabase.fetch(withRecordID: self.vendorRecordID, completionHandler:  {[unowned self] record, error in
if let record = record {
print(record)
self.vendor.recordID = record.recordID
self.vendor.name = record["name"] as! String
print(self.vendor.name)
}
})
Any ideas how to solve this? I believe I have to add a second CKQueryOperation to the mix and use the .addDependency() but I cannot wrap my head around how that should look like in the end.
Let's say you use the operation to fetch prices like above.
let predicate = NSPredicate(format: "priceToProduct == %#", reference)
let query = CKQuery(recordType: "Price", predicate: predicate)
let pricesOperation = CKQueryOperation(query: query)
pricesOperation.database = publicDatabase // not required if you don't use OperationQueue
Then you can construct operation to fetch vendors, I will create simple operation for demo purposes.
let vendorQuery = CKQuery(recordType: "Vendor", predicate: predicate)
let vendorsOperation = CKQueryOperation(query: vendorQuery)
vendorsOperation.database = publicDatabase // not required if you don't use OperationQueue
Then you can set dependency, first fetch prices than vendors.
vendorsOperation.addDependency(pricesOperation)
And lastly submit those operations to OperationQueue
let operationQueue = OperationQueue()
operationQueue.addOperations([pricesOperation, vendorsOperation], waitUntilFinished: false)
Edit: If you don't want to use OperationQueue, simply submit those operations to database, but first set dependency before submitting the operations to be executed.
vendorsOperation.addDependency(pricesOperation)
publicDatabase.add(pricesOperation)
publicDatabase.add(vendorsOperation)

how to remove countryCode from fetched Contacts in swift4?

func fetchContacts(){
let key = [CNContactGivenNameKey, CNContactPhoneNumbersKey] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: key)
try! contactStore.enumerateContacts(with: request) { (contact, stoppingPointer) in
let name = contact.givenName
let number = contact.phoneNumbers.first?.value.stringValue
let contactToAppend = ContactStruct(name: name, number: number)
self.contacts.append(contactToAppend)
self.queryPhoneNumber()
}
tableView.reloadData()
}
In this way I'm getting contact numbers(with country code) and names of the contacts. How to get the contacts without country code.please help me in solving this problem. Thank you.

Set global TimeZone for DateFormatter

I searched here in SO but can't find useful thread to accomplish what I want. I'm new with Swift so I don't know much of this. So the scenario is I want to set a specific timezone which can be change by user with the app's settings. What I want is when the user changed the said timezone it should reflect through out the app. Yes I can set the timezone in each "DateFormatter" but I don't want to write it every time I create a DateFormatter.
Is there a way so when I create an instance of the DateFormatter the timezone is already set to the timezone the user selected? Is it possible to do this using an Extension of the DateFormatter?
What you can do is make extension of Date as of you are setting this TimeZone to DateFormatter to formate your date, So make two extension one for Date and one for String.
extension Date {
struct CustomDateFormatter {
static var currentTimeZone = TimeZone.current //Set default timeZone that you want
static func dateFormatter(withFormat format: String) -> DateFormatter {
let formatter = DateFormatter()
formatter.timeZone = CustomDateFormatter.currentTimeZone
formatter.dateFormat = format
return formatter
}
}
func toDateString(withFormat format: String) -> String {
return CustomDateFormatter.dateFormatter(withFormat: format).string(from: self)
}
}
extension String {
func toDate(withFormat format: String) -> Date? {
return Date.CustomDateFormatter.dateFormatter(withFormat: format).date(from: self)
}
}
Now when ever you need to set TimeZone or want to convert Date to String or vice versa use this extension like this.
//Set timezone
Date.CustomDateFormatter.currentTimeZone = TimeZone(abbreviation: "GMT")!
//get date from String
let date = "31-05-2017 07:30:05".toDate(withFormat: "dd-MM-yyyy HH:mm:ss")
//get string from date with specific format
let dateString = Date().toDateString(withFormat: "dd-MM-yyyy HH:mm:ss")

Trigger notification weekly Swift 3

I am trying to make a schedule, in which I need to remember all the weeks I have class such as Monday at certain time. The problem is that if I assign weekday = 1 (Sunday) when I print the variable triggerWeekly it tells me that weekday = 2, so by performing the tests I do not receive such notification. I need to know why this happens
let weekday = 1 //Sunday 19 Mar
let calendar = NSCalendar.current
var date = DateComponents()
date.weekday = weekday
date.hour = 1
date.minute = 5
let ultimateDate = calendar.date(from: date)
let triggerWeekly = Calendar.current.dateComponents([.weekday, .hour, .minute], from:
ultimateDate!)
print(triggerWeekly) // hour: 1 minute: 5 second: 0 weekday: 2 isLeapMonth: false
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)
let identifier = "curso\(String(Index))"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
You can set your trigger to repeat every monday at 1:05am as follow:
import UserNotifications
let trigger = UNCalendarNotificationTrigger(dateMatching: DateComponents(hour: 1, minute: 5, weekday: 2), repeats: true)
print(trigger.nextTriggerDate() ?? "nil")
let content = UNMutableNotificationContent()
content.title = "title"
content.body = "body"
// make sure you give each request a unique identifier. (nextTriggerDate description)
let request = UNNotificationRequest(identifier: "identify", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print(error)
return
}
print("scheduled")
}
Don't forget to ask the user permission to schedule notifications before trying to schedule your notification:
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]) { granted, error in
if granted {
print("authorized")
}
}

swift 3 how to subclass NSDate or Date

I'm trying to add a special JSONSerializable method to a dateTaken field so I wanted to extend Date. but I discovered that Date is a struct and cannot be extended, so NSDate. but I can't figure out how to get a DateTaken from a Date
class DateTaken : NSDate, JSONRepresentable {
static var formatter = DateFormatter()
var JSONRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" // ISO8601
DateTaken.formatter.timeZone = NSTimeZone(abbreviation: "UTC") as TimeZone!
return DateTaken.formatter.string(from:self as Date) as AnyObject
}
var JSONLocalTimeRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return DateTaken.formatter.string(from:self as Date) as AnyObject
}
}
let d = Date()
let dateTaken : DateTaken = DateTaken.init(timeInterval: 0, since: d)
// exception
(lldb) p NSDate.init(timeInterval: 0, since: dateTaken)
(NSDate) $R0 = 0x0000000156e6fdb0 2014-10-13 08:49:18 UTC
(lldb) p dateTaken.init(timeInterval: 0, since: dateTaken)
error: <EXPR>:3:1: error: 'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type
dateTaken.init(timeInterval: 0, since: dateTaken)
^
type(of: )
This isn't Java. We have extensions for this.
extension NSDate: JSONRepresentable {
static var formatter = DateFormatter()
var JSONRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" // ISO8601
DateTaken.formatter.timeZone = TimeZone(abbreviation: "UTC")
return DateTaken.formatter.string(from: self as Date) as AnyObject
}
var JSONLocalTimeRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return DateTaken.formatter.string(from: self as Date) as AnyObject
}
}
I also suspect there's a bug in this caused by the sharing of formatter. After the timeZone is set for the first time in JSONRepresentation, JSONLocalTimeRepresentation will act like JSONRepresentation. I may be wrong, but I can't test it now. It's worth checking out.