Why does NSLocale.current.identifier include currency on macOS? - swift

If I request the current locales identifier on iOS, it returns just the identifier string:
let identifier = NSLocale.current.identifier // en_GB
However, on macOS 10.12.2 it also returns the currency:
let identifier = NSLocale.current.identifier // en_GB#currency=GBP
Is this a bug or expected behaviour?

I ran into this recently. I'm not sure why, but apparently as of 10.12, localeIdentifier can include a bunch of stuff besides country and language.
Unfortunately, the documentation doesn't elaborate on the cases in which other metadata is included.
However, also as of 10.12, there's another property languageCode, which, in conjunction with countryCode, you can use to generate en_US.
This is what iTerm2 does.

I think the best option for me here is to generate the code myself. To help with this I have created an extension on Locale:
extension Locale {
var iso3166code: String {
guard
let language = languageCode,
let region = regionCode
else { return "en-US" }
return "\(language)-\(region)"
}
}
While this is accurate enough for my purposes, you should probably ensure it returns expected values for your project.

Related

How does Self(...) works in swift?

I saw this piece of code but am not understanding what the syntax does. I've looked on web, but wasn't able to find anything.
Q1. What does it do?
Q2. How would you actually go about finding how a particular syntax works?
public extension AppleMusicClient {
static let denied = Self(
musicAuthorizationStatus: .denied,
playSong: { _ in .none },
requestMusicAuthorization: { .none },
searchRequest: { _ in .none }
)
static let noop = Self(
musicAuthorizationStatus: .authorized,
playSong: { _ in .none },
requestMusicAuthorization: { .none },
searchRequest: { _ in .none }
)
In simple terms Self is just a synonym of the current type name, in this case - AppleMusicClient. I.e. same static constants could be defined as
public extension AppleMusicClient {
static let denied = AppleMusicClient(...
static let noop = AppleMusicClient(
So why use Self, and not type name directly? Best it's described in the original SR:
Self is shorter and clearer in its intent. It mirrors self, which refers to the current instance.
It provides an easier way to access static members. As type names grow large, readability suffers. MyExtremelyLargeTypeName.staticMember is unwieldy to type and read.
Code using hardwired type names is less portable than code that automatically knows its type.
Renaming a type means updating any TypeName references in code.
See also: a more detailed explanation.
Regarding Q2 ("How would you actually go about finding how a particular syntax works?"): Swift language reference is there for you. Usually, every time you are upgrading your Xcode (which also updates Swift version), it's a good idea to check Revision History to see what changed for your current Swift version. So for instance the Self change is mentioned in Swift 5.1 changelog:
Updated the Self Type section, now that Self can be used to refer to the type introduced by the current class, structure, or enumeration declaration.

Updating Locale language swift

I get Locale.current.languageCode, but I would like also to set that value. How I can do it?
let currentLanguage = Locale.current.languageCode
I use:
Defaults[.language] = "En"
MOLH.setLanguageTo("en")
i do not think that you can change the current Locale language, just a user can change their language/locale in settings.
static var current: Locale { get }

Currency code returns nil

A simple request for currency code as done below should return the currency code but it returns nil. Do I need set the region before requesting the currency code or is there something I am missing. I am using Swift 3.1 and Xcode 8.3.3 iphone 6 and same result on my simulator.
if let currencyCode = Locale.current.currencyCode {
print(currencyCode)
}
Kindly select the system language from edit scheme and then check
At the moment I cannot think of a case when the currencyCode will not be returned, but as seen in the documentation, it is Optional. Could you please give more information about your specific locale?
A suggestion - print the currencySymbol instead and post the result here:
if let currencySymbol = Locale.current.currencySymbol {
print(currencySymbol)
}

Xcode 8.0 and Swift 3.0 conversion: Looking for explanation for a particular conversion error

I am a little confused about a conversion error.
I migrated my project form Swift 2.3 to Swift 3.0
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration
//due to: Argument labels do not match any available overloads
celsiusLabel.text = numberFormatter.string(from: NSNumber(value))
//This is my code trying to fix this issue and the project is now compiling
//and everything is fine
celsiusLabel.text = numberFormatter.string(from: value as NSNumber)
}
else { celsiusLabel.text = "???"
}
}
At first I thought that in Swift 3.0 the cast Type(value) was now forbidden, but I checked and I get absolutely no compiler warning. Can somebody tell me what the problem with NSNumber(value) is?
As far as I understand value as NSNumber and NSNumber(value) should be the same thing.
In Swift 3, NSNumber(value) won't work. Let's say that your value is an Int. In that case, you'd need NSNUmber(value: yourIntValue). In Swift 3, you must have the name of the first (and in this case the only) parameter in the function call. So, your usage of
value as NSNumber
works, but
NSNumber(value: yourNumberValue)
works too.
First of all I have taken some assumption here, I have assumed that -
numberFormatter = NSNumberFormatter() // Now it has been renamed to NumberFormatter
celsiusLabel.text I am taking text as optional string just for example you can use label.text for same.
After the above assumption please see below code which will work in Swift 3 -
var celsiusValue:Double?
var numberFormatter = NumberFormatter()
var text:String?
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration due to: Argument labels do not match any available overloads
text = numberFormatter.string(from: NSNumber(value: value))!
}
else {
text = "???"
}
}
Hope it help feel free to leave comment in case you have any doubt.

Winter 2015 / Lecture 10 - Broken Twitter Package

Trying to follow along and code the Smashtag project while watching the Lecture 10 iTunes video.
When I add the dowloaded Twitter package to my Smashtag project, XCode couldn't find the Tweet class when I made reference to it in the TweetTableViewController.
Because of the problem described above, I added the four classes belonging to the Twitter package individually to the project. XCode found the four classes but adding them in this manner generated 11 compile errors.
I'm using XCode Version 6.3 (6D570) which is subsequent to the iOS 8.3 release.
Has anyone else encountered this issue?
Thank you for reading my question.
~ Lee
Possibly not the most-correct (read: best practice) way to do this, but I'm going to chalk it up to doing what it takes to finish the course.
I just went through the list of compile errors and changed the relevant properties to var instead of let. Constants can't be changed and in the new version of Swift they can only be instantiated once. So for the sake of not rewriting too much code, I chose to make certain properties vars instead of lets.
Other bugs I found following the iTunes U course:
The named ‘handler:’ argument needs the name explicitly in a few places.
The simulator will show "TwitterRequest: Couldn\'t discover Twitter account type.” until you go to Settings (inside the simulator) and set the Twitter account. At this point I had to reboot the device, as the call is made in the ViewDidLoad, and thus is only called the first time the view loads. (Alternatively, you could close out the app from the app switcher in the simulator and relaunch that way.)
Here is a gist with corrected code that you can use as a Twitter package that will work with the course and has fixes for the aforementioned bugs, minus the Twitter account setting:
https://gist.github.com/mattpetters/ccf87678ccce0c354398
As Christian R. Jimenez said, "I went to Settings in the Simulated iphone and add my Twitter Account. And everything works perfect." in http://cs193p.m2m.at/cs193p-lecture-10-table-view-winter-2015/. I just added my Twitter Account and tested it, it works!
I had similar problems with the Twitter packages using Swift 2.0 and Xcode 7.2
I'm very new to Swift, so there is a good chance the changes I made are not best practices, but the updated files do work: https://gist.github.com/awaxman11/9c48c0b4c622bffb879f.
For the most part I used Xcode's suggested changes. The two larger changes I made were:
In Tweet.swift I updated the the IndexedKeyword struct's init method to use advanceBy() instead of advance()
In TwitterRequest.swift I updated the signature of NSJSONSerialization to conform to the new error handling system
I've just had a big session fixing the Twitter package files for this same version of Xcode.
It seems that what has broken is that in this version of Swift, constants ('let x...') may only be initialized once, so if a constant is given a value in the declaration ('let x = false'), it may not be changed in the init() function. The Twitter package gives some constants initial values, but then changes the values in the init() function.
My solution to this was to follow the styles suggested in the current version of the Apple Swift language book: declare (many of) the constants as implicitly unwrapped optionals, unconditionally assign a value to them in the init() function (which value may be nil), then test whether any of them are nil, and, if so, return nil from init().
See https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html, click "On This Page" and choose "Failable Initializers"
Also, in TwitterRequest.swift, I needed to add the parameter name 'handler:' in a couple of calls to performTwitterRequest(request, handler: handler).
As an example of constant initialization, in MediaItem.swift:
<< Original Code >>
...
public let aspectRatio: Double = 0
...
init?(data: NSDictionary?) {
var valid = false
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
valid = true
}
}
}
if !valid {
return nil
}
}
...
<< Updated code >>
...
public let aspectRatio: Double
...
init?(data: NSDictionary?) {
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString as String) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
return
}
}
}
return nil
}
...