I have a property #NSManaged public var sectionKeyDate: Date? I am trying to use for the sectionKey. I'm having an issue getting the resulting string from the NSFetchedResultsSectionInfo to format into a Date, to then be converted back into a formatted string.
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let sectionInfo = fetchedResultsController.sections?[section] else {
return nil
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-DD"
print("sectionInfo.name: ", sectionInfo.name)
print("Date: ", formatter.date(from: sectionInfo.name))
guard let date = formatter.date(from: sectionInfo.name) else { return "Error" }
let string = formatter.string(from: date)
return string
}
The conversion from a string to date is failing and return nil.
sectionInfo.name: 2021-03-25 05:00:00 +0000
Date: nil
(1) I'm not sure how to format this correctly.
(2) I'm not entirely sure if using a Date for the sectionKeyPath is an ok thing to do.
The issue was in getting the correct dateFormat.
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
let date = formatter.date(from: sectionInfo.name)
Related
I need to create a common method for many of the views in my project to export data as .csv. For that purpose, I need to pass an "entity" parameter, in order to reuse this function for all of them.
The example code for "Alumno" entity is as follows:
func exportData(???) {
let container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
//*
var exportData: [Alumno] = []
let exportRequest: NSFetchRequest<Alumno> = Alumno.fetchRequest()
//*
let context = container?.viewContext
do {
exportData = try context!.fetch(exportRequest)
} catch {
print("Error: \(error)")
}
...
Now, how can I pass an "entity" parameter in the method definition (marked with ???), and what should be "exportData" array and "exportRequest" definition (marked with *), in order to reuse this method to be called from other views to export its data?
Thanks.
If you want this to work for any of your entities, then it doesn't need to know which entity it's working with. It can use NSManagedObject, and ignore the subclasses.
This would be something like
func exportData(class name: String) -> Void {
// ...
var exportData: [NSManagedObject] = []
let exportRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: name)
// ...
}
The name is all you need for the fetch request. You'll get back a collection of managed objects.
Going on with Tom suggestions, he is right: I have defined three csv() methods to get csv strings for each of my entities. For example, "alumno" one:
extension Alumno {
func csv() -> String {
let coalescedNombre = nombre ?? ""
let coalescedEspecialidad = especialidadRelacionada?.nombre ?? ""
let coalescedCurso = cursoRelacionado?.nivel ?? ""
let coalescedTutor = tutorRelacionado?.nombre ?? ""
let coalescedMovil = movil ?? ""
let coalescedCorreo = correo ?? ""
let formatter = DateFormatter()
formatter.dateFormat = "d MMM y, E HH:mm"
let coalescedNacimiento = formatter.string(from: nacimiento!)
return "\(coalescedNombre), \(coalescedEspecialidad), \(coalescedCurso), \(coalescedTutor), \(coalescedMovil), \(coalescedCorreo), \(coalescedNacimiento)\r\n"
}
}
But now the problem is: how can I match this coalesced values to populate csv file for its correspondent entity? I mean: I can write en equivalent method for all of them, but, how can I define its values? This doesn't work:
extension NSManagedObject {
func csv() -> String {
var entity = NSManagedObject.entity
if entity == Alumno {
let coalescedNombre = nombre ?? ""
let coalescedEspecialidad = especialidadRelacionada?.nombre ?? ""
let coalescedCurso = cursoRelacionado?.nivel ?? ""
let coalescedTutor = tutorRelacionado?.nombre ?? ""
let coalescedMovil = movil ?? ""
let coalescedCorreo = correo ?? ""
let formatter = DateFormatter()
formatter.dateFormat = "d MMM y, E HH:mm"
let coalescedNacimiento = formatter.string(from: nacimiento!)
return "\(coalescedNombre), \(coalescedEspecialidad), \(coalescedCurso), \(coalescedTutor), \(coalescedMovil), \(coalescedCorreo), \(coalescedNacimiento)\r\n"
}
if entity == ...
}
}
I've came across a problem to convert a Day of Week string into a time.Weekday value. I couldn't find anything built into the time package.
Then I've written this simple function (that covers my needs):
var daysOfWeek = [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
func parseWeekday(v string) (time.Weekday, error) {
for i := range daysOfWeek {
if daysOfWeek[i] == v {
return time.Weekday(i), nil
}
}
return time.Sunday, fmt.Errorf("invalid weekday '%s'", v)
}
Is there any other recommended or more idiomatic way to perform this conversion in Golang?
Thanks!
Foreword: I released this utility in github.com/icza/gox, see timex.ParseWeekday().
Yes, use a map instead of an array, so lookups are faster and more straight-forward:
var daysOfWeek = map[string]time.Weekday{
"Sunday": time.Sunday,
"Monday": time.Monday,
"Tuesday": time.Tuesday,
"Wednesday": time.Wednesday,
"Thursday": time.Thursday,
"Friday": time.Friday,
"Saturday": time.Saturday,
}
func parseWeekday(v string) (time.Weekday, error) {
if d, ok := daysOfWeek[v]; ok {
return d, nil
}
return time.Sunday, fmt.Errorf("invalid weekday '%s'", v)
}
Testing it:
fmt.Println(parseWeekday("Monday"))
fmt.Println(parseWeekday("Friday"))
fmt.Println(parseWeekday("invalid"))
Output (try it on the Go Playgorund):
Monday <nil>
Friday <nil>
Sunday invalid weekday 'invalid'
Tip:
You can even use a for loop to initialize safely the daysOfWeek map like this:
var daysOfWeek = map[string]time.Weekday{}
func init() {
for d := time.Sunday; d <= time.Saturday; d++ {
daysOfWeek[d.String()] = d
}
}
Testing and output is the same. Try this one on the Go Playground.
Another nice property of this map-solution (compared to your array-solution) is that you may list additional valid values in the same map that may be parsed into time.Weekday without additional parsing code.
For example, let's also parse the 3-letter short weekday names into their time.Weekday equivalent, e.g. "Mon" to time.Monday.
This extension can be added with a simple loop:
var daysOfWeek = map[string]time.Weekday{}
func init() {
for d := time.Sunday; d <= time.Saturday; d++ {
name := d.String()
daysOfWeek[name] = d
daysOfWeek[name[:3]] = d
}
}
Testing it:
fmt.Println(parseWeekday("Monday"))
fmt.Println(parseWeekday("Friday"))
fmt.Println(parseWeekday("Mon"))
fmt.Println(parseWeekday("Fri"))
fmt.Println(parseWeekday("invalid"))
Output (try it on the Go Playground):
Monday <nil>
Friday <nil>
Monday <nil>
Friday <nil>
Sunday invalid weekday 'invalid'
See similar question: Get integer month value from string
This looks to do it:
package main
import "time"
func parseWeekday(v string) (time.Weekday, error) {
t, e := time.Parse("Monday 2", v + " 2")
if e != nil { return 0, e }
return t.Weekday(), nil
}
func main() {
n, e := parseWeekday("Sunday")
if e != nil {
panic(e)
}
println(n == time.Sunday)
}
https://golang.org/pkg/time#Parse
I've code to show clock time with the timeLabel:UILabel in my app whit Swift3.
And that is:
override func viewWillAppear(_ animated: Bool) {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.currentTime), userInfo: nil, repeats: true)
}
func currentTime(){
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
let second = calendar.component(.second, from: date)
if second % 2 == 0{
timeLabel.text = "\(hour):\(minutes)"
}else {
timeLabel.text = "\(hour) \(minutes)"
}
But when two points disappear between hour and minutes numbers, they are a little bit movement. How to fix that?
With a trick 😏 can be fix that.
By adding another label with same background color in else condition, Yup😉
For Swift 3:
var attributes = [String: AnyObject]()
attributes[NSForegroundColorAttributeName] = self.view.backgroundColor
let attributedString = NSAttributedString(string: ":", attributes: attributes)
label.attributedText = attributedString
I need to know if a string contains an Int to be sure that a name the user entered is a valid full name,
for that I need to either make the user type only chars, or valid that there are no ints in the string the user entered.
Thanks for all the help.
You can use Foundation methods with Swift strings, and that's what you should do here. NSString has built in methods that use NSCharacterSet to check if certain types of characters are present. This translates nicely to Swift:
var str = "Hello, playground1"
let decimalCharacters = CharacterSet.decimalDigits
let decimalRange = str.rangeOfCharacter(from: decimalCharacters)
if decimalRange != nil {
print("Numbers found")
}
If you're interested in restricting what can be typed, you should implement UITextFieldDelegate and the method textField(_:shouldChangeCharactersIn:replacementString:) to prevent people from typing those characters in the first place.
Simple Swift 4 version using rangeOfCharacter method from String class:
let numbersRange = stringValue.rangeOfCharacter(from: .decimalDigits)
let hasNumbers = (numbersRange != nil)
This method is what i use now for checking if a string contains a number
func doStringContainsNumber( _string : String) -> Bool{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
let containsNumber = testCase.evaluateWithObject(_string)
return containsNumber
}
If your string Contains a number it will return true else false. Hope it helps
//Swift 3.0 to check if String contains numbers (decimal digits):
let someString = "string 1"
let numberCharacters = NSCharacterSet.decimalDigits
if someString.rangeOfCharacter(from: numberCharacters) != nil
{ print("String contains numbers")}
else if someString.rangeOfCharacter(from: numberCharacters) == nil
{ print("String doesn't contains numbers")}
//A function that checks if a string has any numbers
func stringHasNumber(_ string:String) -> Bool {
for character in string{
if character.isNumber{
return true
}
}
return false
}
/// Check stringHasNumber function
stringHasNumber("mhhhldiddld")
stringHasNumber("kjkdjd99900")
if (ContainsNumbers(str).count > 0)
{
// Your string contains at least one number 0-9
}
func ContainsNumbers(s: String) -> [Character]
{
return s.characters.filter { ("0"..."9").contains($0)}
}
Swift 2.3. version working.
extension String
{
func containsNumbers() -> Bool
{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
return testCase.evaluateWithObject(self)
}
}
Usage:
//guard let firstname = textField.text else { return }
let testStr1 = "lalalala"
let testStr2 = "1lalalala"
let testStr3 = "lal2lsd2l"
print("Test 1 = \(testStr1.containsNumbers())\nTest 2 = \(testStr2.containsNumbers())\nTest 3 = \(testStr3.containsNumbers())\n")
You need to trick Swift into using Regex by wrapping up its nsRegularExpression
class Regex {
let internalExpression: NSRegularExpression
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
var error: NSError?
self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)
}
func test(input: String) -> Bool {
let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, countElements(input)))
return matches.count > 0
}
}
if Regex("\\d/").test("John 2 Smith") {
println("has a number in the name")
}
I got these from http://benscheirman.com/2014/06/regex-in-swift/
let numericCharSet = CharacterSet.init(charactersIn: "1234567890")
let newCharSet = CharacterSet.init(charactersIn: "~`##$%^&*(){}[]<>?")
let sentence = "Tes#ting4 #Charact2er1Seqt"
if sentence.rangeOfCharacter(from: numericCharSet) != nil {
print("Yes It,Have a Numeric")
let removedSpl = sentence.components(separatedBy: newCharSet).joined()
print(sentence.components(separatedBy: newCharSet).joined())
print(removedSpl.components(separatedBy: numericCharSet).joined())
}
else {
print("No")
}
I got this Error while try to convert a string to datetime "String was not recognized as a valid DateTime"
My string value : "09/25/2012 11:00:36:156"
Code :
capture = Convert.ToDateTime(newRecord.CaptureTime),
I suggest to You use Regular Expressions. You will have validation input string and robust mechanism of DateTime convert:
void Main()
{
string datestring = "09/25/2012 11:00:36:156";
string regexpr = #"(?x)(?i)
(\d{1,4}) [./-]
(\d{1,2}) [./-]
(\d{1,4}) [\sT] (\d+):(\d+):(\d+) \s? (A\.?M\.?|P\.?M\.?)?";
DateTime capture = new DateTime(); // set to default value in case datestring isn't valid
if (Regex.IsMatch(datestring, regexpr)
&& DateTime.TryParse(Regex.Match(datestring, regexpr).Groups[0].Value, out capture))
{
// convert is succeded
}
else
{
// Handle invalid dateString
}
Console.WriteLine(capture);
}
Or DateTime.TryParseExact(). Read more here.
the problem is with the last colon sign (':') replace with . 09/25/2012 11:00:36:156
var dateString = "09/25/2012 11:00:36:156";
var modifiedString = dateString.Substring(0, dateString.Length - 4) + "." + dateString.Substring(20);
//var modifiedString = dateString.Substring(0, dateString.Length - 4);
var dateValue = Convert.ToDateTime(modifiedString);