Use value of variable for property lookup - swift

I am trying to build a table of current locale properties in code, and have encountered issues with trying to pass the value of a variable to a function:
let currentLocale = Locale(identifier: "en_US")
let calendar1 = currentLocale.calendar // "gregorian (fixed)"
let propertyName = "calendar"
let calendar2 = currentLocale.propertyName // Error: Value of type 'Locale' has no member 'porpertyName'
In the last line of code above, the instance of Locale thinks I am passing it "propertyName" rather than the contents of the variable "calendar".
Is there any way to pass the value of propertyName ("calendar") to the instance of Locale? I know that in other languages, you can prepend the variable name like '$propertyName', and that tells it to read the value of the variable.
I want to keep this pure Swift if possible.

You are looking for some form of key-value coding.
It's a little tricky, in that this is a purely Objective-C feature of Cocoa, so it doesn't work with the Swift overlay class Locale; you will have to cast currentLocale to Objective-C NSLocale. Moreover, NSLocale exposes its attributes through special NSLocale.Key types. After a great deal of casting, I find that this works:
let calendar2 =
(currentLocale as NSLocale).object(forKey:NSLocale.Key(rawValue:propertyName))
calendar2 is typed as Any but you can cast it down to a String.

Related

What is the name of variable ending in = {} () Swift?

I have been digging through the Swift documentation trying to find the name of the Swift syntax below.
static let taskDateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}()
I do not know what to call this syntax. I am confused by the = {} () component. What is this called?
It's code that generates the default DateFormatter value for the taskDateFormat property.
As per the Swift manual (emphasis mine):
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
Setting a Default Property Value with a Closure or Function
If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.
Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
Basically the code pre-initializes the taskDateFormat property to a new DateFormatter object and sets the dateStyle property before the taskDateFormat property is set.
The syntax = {}() means a block (a.k.a closure) , it can return anything.
Since your variable is DateFormatter Type, it will expect a DateFormatter instance being returned from the block.
That Type on the variable is mandatory, you can have a hint on it if you remove the variable Type, you will get the error:
Unable to infer complex closure return type; add explicit type to disambiguate

How to understand the usage of 'let' or 'var' in Swift?

I'm following tutorial for Swift 4 and have found the usages of 'let' or 'var' in Swift quite inconsistent.
//in try catch : "let ... as" to match a error ?
catch let(or var) printerError as PrinterError
//in switch 1: "let .." to match a case pattern ?
case let(or var) .result(sunrise, sunset):
//in switch 2: "let ... where" pointless for me, why not just use someVar.hasSuffix ?
switch: someVar {
case let x(or var) where x.hasSuffix("pepper"):
Could anyone kindly give a summary of the usage in Swift?
It seems everyone is answering about the difference between 'let' and 'var' and mark the question as a duplication. But, I didn't even mention anything about 'var' in the original post at the first place!
let is keyword that is used to declare a constant value of any data type, using let you can declare a value but you can't change its value again through out the project and if you try to change its value, it will gives you an error stating that this is a let constant. If you want to modify its value kindly change it to var, where var is a keyword used for variables.
let x: Int = 5
let string : String = "Hello! World"
The above values are constant and you can never change these values.
var x: Int = 5
var string: String = "Hello! World"
The above values are variables. You can change their value anywhere in the code.
let is used for constants while var is for variables
let is also used for optional binding like in your examples. You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable.
Let is a keyword to declare a constant.
Think of constant as a box that stores information.
let name = "Bob"
"let" is the keyword to declare the constant.
"name" is the name you assign your constant. This is the box you store your information in. You can name it whatever you want doesn't have to be "name"
"=" assigns the value(your information) from the right side to the "name" constant.
"Bob" is the value aka the information you want to store. It can be anything you want and it's assigned to your constant.
Something you have to remember for constant is that constants are immutable. Meaning the values once declared cannot be changed. That's why it's called constant, because the values are always constant and does not change.

Swift 3: How to read from "Optional(Optional(stringValue))" without optional?

I got the string value from server like this.
let filename = "\(eventList[index]["filename"])"
But I got the value with Optional(Optional(stringValue)).
So I changed that like this.
let filename = "\(eventList[index]["filename"]!)"
Then I got the value with Optional(stringValue).
I can't do any more for this error.
How can I read the filename without any optional?
Use nil-coalescing operator aka double question mark operation. It is used to provide a default value when unwrapping an optional type.
let filename = eventList[index]["filename"] ?? ""
R̶e̶f̶:̶ ̶h̶t̶t̶p̶:̶/̶/̶w̶w̶w̶.̶j̶e̶e̶n̶a̶l̶i̶n̶f̶o̶t̶e̶c̶h̶.̶c̶o̶m̶/̶b̶l̶o̶g̶s̶/̶i̶o̶s̶/̶h̶o̶w̶-̶t̶o̶-̶d̶o̶-̶o̶p̶t̶i̶o̶n̶a̶l̶-̶v̶a̶r̶i̶a̶b̶l̶e̶-̶a̶s̶s̶i̶g̶n̶m̶e̶n̶t̶-̶w̶i̶t̶h̶-̶d̶e̶f̶a̶u̶l̶t̶-̶v̶a̶l̶u̶e̶-̶d̶o̶u̶b̶l̶e̶-̶q̶u̶e̶s̶t̶i̶o̶n̶-̶m̶a̶r̶k̶/̶
https://medium.com/#milanpanchal24/
Use if-let syntax to unwrap optional:
if let fileName = eventList[index]["filename"] {
// use fileName
}
eventList[index] accesses an array item at the given index. The item you are referring seems to be an optional dictionary so before accessing the dictionary item it needs to be unwrapped: eventLists[index]! (assuming it exists and valid of course otherwise it will crash)
then you can access the dictionary require value which is an optional as well:
eventLists[index]!["fileName"]!
assuming your list is valid you will get the desired String object.
I recommend using the safety checks (if-let or other variants) for preventing crashes

Can I create a Date object from a predefined string (typescript)?

I have a value returned in a string (numbers separated by commas) and I'd like to make a Date object out of it. It looks like this is not possible, can someone confirm and/or suggest me a solution.
This does not work :
let dateString='2017,3,22,0';
let dateFromString = new Date(dateString);
This works though (when I pass a list of numbers) :
let dateFromString = new Date(2017,3,22,0);
And this works also :
let dateString = '2008/05/10 12:08:20';
let dateFromString = new Date(dateString);
The goal would be to create a Date object from a uniform string. Is that possible ?
Can I create a Date object from a predefined string, which has only one type of separator (comma, colon, slash or whatever) ?
If your environment is compatible with ES6 (eg. Babel, TypeScript, modern Chrome/Firefox etc), you can use the string's .split(',') and decompose the array into arguments like the following:
const dateString = '2017,3,22,0';
const date = new Date(...dateString.split(',')); // date object for 2017/03/22
ES5 compatible version:
var dateString = '2017,1,2,0';
var date = new (Function.prototype.bind.apply(Date, [null].concat(dateString.split(','))));
As for how the .bind.apply method works with new, you can take a look at Use of .apply() with 'new' operator. Is this possible?
Note: Thanks to the two comments below for spotting my errors 👍

How do I make a CFLocale for a target language?

In Objective C, one can create a CFLocale as follows (taken from this post in 2012):
CFLocaleRef myLocale = CFLocaleCopyCurrent() for the current locale; or:
CFLocaleRef myLocale = CFLocaleCreate(kCFAllocatorDefault, CFSTR("ja")), for a target locale. The locale name comes from the rightmost column of the ISO 639-1/639-2 table, which Apple specifies as their standard for language codes here.*
*Note: very old code examples refer to long language codes like 'Japanese', as may be expected by versions of Mac OS X older than 10.4.
How does one create a CFLocale in Swift 3, as the API appears to have changed in several ways?
CFLocale is toll-free bridged to NSLocale, so you can simply call
let myLocale = NSLocale(localeIdentifier: "ja")
// or
let myLocale = NSLocale(localeIdentifier: NSLocale.canonicalLocaleIdentifier(from: "Japanese"))
depending on whether you have a ISO 639-1 language code or not.
The corresponding Swift 3 "overlay value type" Locale
(which is used by Calendar, DateFormatter, ...,
compare SE-0069 Mutability and Foundation Value Types)
can similarly be created with
let myLocale = Locale(identifier: "ja")
// or
let myLocale = Locale(identifier: Locale.canonicalIdentifier(from: "Japanese"))
Here are the API changes to Swift since the Objective-C example given from 2012:
CFLocaleRef has been replaced by CFLocale.
CFStringRef has been replaced by CFString, which may be created by a regular String cast to CFString type: "ja" as CFString!.
CFLocaleCreate() now expects a CFLocaleIdentifier rather than just a CFString, so we must provide one using CFLocaleCreateCanonicalLanguageIdentifierFromString().
This can be done with the following two lines:
let localeIdentifier: CFLocaleIdentifier = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, "ja" as CFString!)
let locale: CFLocale = CFLocaleCreate(kCFAllocatorDefault, localeIdentifier)