Swift: Get currency symbol from currency code is not working - ios

I want Currency symbol from Currency code.
for e.g) EUR -> €, USD -> $, SEK -> kr, DKK -> kr
I am using below code to get currency symbol.
func getSymbolForCurrencyCode(code: String) -> String? {
let locale = NSLocale(localeIdentifier: code)
return locale.displayName(forKey: NSLocale.Key.currencySymbol, value: code)
}
But it returns SEK for SEK and DKK for DKK, it should return kr.
For USD, GBP, EUR its working fine.
What could be the issue?

It works without using NSLocale class (Swift 3/4):
func getSymbolForCurrencyCode(code: String) -> String? {
let result = Locale.availableIdentifiers.map { Locale(identifier: $0) }.first { $0.currencyCode == code }
return result?.currencySymbol
}
getSymbolForCurrencyCode(code: "GBP")

I have created two extensions for strings adding cache to optimize the search.
extension String {
private static let currencyCode = NSCache<NSString, NSString>()
public var symbolForCurrencyCode: String? {
if let cached = Self.currencyCode.object(forKey: self as NSString) {
return cached as String
}
let identifiers = Locale.availableIdentifiers
guard let identifier = identifiers.first(where: { Locale(identifier: $0).currencyCode == self }) else {
return nil
}
guard let symbol = Locale(identifier: identifier).currencySymbol else {
return nil
}
Self.currencyCode.setObject(symbol as NSString, forKey: self as NSString)
return symbol
}
}
extension Optional where Wrapped == String {
public var symbolForCurrencyCode: String? {
guard let code = self else {
return nil
}
return code.symbolForCurrencyCode
}
}
"GBP".symbolForCurrencyCode // "£"
"EUR".symbolForCurrencyCode // "€"
"SEK".symbolForCurrencyCode // "kr"

Related

Foundation.Date turns to nil after returning from function

I have some decoders for different types.
In this function decoders for date are creating.
private static var __once: () = {
//possible formats
let formatters = [
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd HH:mm:ss"
].map { (format: String) -> DateFormatter in
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = format
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}
// Decoder for Date
Decoders.addDecoder(clazz: Date.self) { (source: AnyObject, instance: AnyObject?) -> Decoded<Date> in
if let sourceString = source as? String {
for formatter in formatters {
if let date = formatter.date(from: sourceString) {
return .success(date) //in this state date = 2022-03-26 05:45:00 +0000 (not nil)
}
}
}
...some code
}
Decoder calls here. It work right way for another types, but "date" turns to nil after returning from decoder function.
static func decodeOptional<T>(clazz: T.Type, source: AnyObject?) -> Decoded<T?> {
if let source = source, !(source is NSNull) {
switch Decoders.decode(clazz: clazz, source: source, instance: nil) { //this function call decoder for date from above.
case let .success(value): return .success(value) // but date in this statement turns no nil
case let .failure(error): return .failure(error)
}
} else {
return .success(nil)
}
}
static func decode<T>(clazz: T.Type, source: AnyObject, instance: AnyObject?) -> Decoded<T> {
initialize()
...somecode...//decoder with key "Date"
let key = "\(T.self)"
if let decoder = decoders[key], let value = decoder(source, instance) as? Decoded<T> {
return value
} else {
return .failure(.typeMismatch(expected: String(describing: clazz), actual: String(describing: source)))
}
}
Returning type:
public enum Decoded<ValueType> { ////ValueType = Date
case success(ValueType)
case failure(DecodeError)
}
Why Date object turns from real value to nil after returning from function?

save arrays of json in firebase with my swift 4 app

I have an app that shows 4 options. Everyday you click on one or more of the options. Right now, it's storing in the firebase database like an array of string, where every string is one of the options. Like this
override func addSelection(selection: String) {
self.quitPlan.medications.append(selection)
}
var medications: [String] {
get {
return document[Properties.medications.rawValue] as? [String] ?? []
}
set {
document[Properties.medications.rawValue] = newValue
}
}
But I actually want an array of jsons, with the option and the option. I have try:
override func addSelection(selection: String) {
let medicationSelected = Medication(medication: selection, date: Date())
self.quitPlan.medications.append(medicationSelected)
}
var medications: [Medication] {
get {
return document[Properties.medications.rawValue] as? [Medication] ?? []
}
set {
document[Properties.medications.rawValue] = newValue
}
}
struct Medication {
let medication: String
let date: Date
}
But it's not working, I'm getting 'FIRInvalidArgumentException', reason: 'Unsupported type: __SwiftValue'
You can do something like this:
struct Medication {
let medication: String
let date: Date
private let divider = "|"
func toString() -> String {
return midication + divider + date.toString()
}
func from(_ string: String) -> Medication {
let arr = string.split(divider)
let medication = arr[0]
let date = // TODO: Date from string arr[1]
return Medication(medication: medication, date: date)
}}
and
self.quitPlan.medications.append(medicationSelected.toString())
Firebase cannot save custom Swift structs.
A possible solution is to encode the array of Medication to a JSON string.
struct Medication : Codable {
let medication: String
let date: Date
}
In the database change the type from an array of string to single string
var medications: [Medication] {
get {
guard let medicationJSON = document[Properties.medications.rawValue] as? String,
let data = medicationJSON.data(using: .utf8),
let medi = try? JSONDecoder().decode([Medication].self, from: data) else { return [] }
return medi
}
set {
let medicationData = try! JSONEncoder().encode(newValue)
document[Properties.medications.rawValue] = String(data: medicationData, encoding: .utf8)!
}
}
Firestore can save a Swift struct to a collection, there is a module for this.
First, you should include the module:
import FirebaseFirestoreSwift
Then, just do:
db.collection("yourCollectionName").document(from: yourSwiftObject)
It will be converted to be saved in your Firestore collection.

How to remove characters from a String in swift

I am looking for a simple way to remove the 4 characters in the tilesColored String "ment" from the shuffledWord1.
var word1: String = "employment"
var shuffledWord1: String = "melpyoemtn"
var tilesColored: String = "ment"
var characters = Array(tilesColored) // gives ["m","e","n","t"]
let newWord1 = word1.StringByReplacingOccurencesOfString("\(characters[0])", withString:"") // gives "elpyoetn"
stringByReplacingOccurencesOfString only allows 1 character to be checked and removes BOTH m's, so how can I check against all 4 and only remove ONE instance of each to return "melpyo"?
Thanks in advance for any help possible
Swift 3+ version with better performance than the previous top answers. (Because we don't separate into arrays with substrings, which all would need seperate allocations.)
This here just works on the unicode scalar level. You can paste it right into a playground.
import Foundation
extension String {
func removeCharacters(from forbiddenChars: CharacterSet) -> String {
let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
return String(String.UnicodeScalarView(passed))
}
func removeCharacters(from: String) -> String {
return removeCharacters(from: CharacterSet(charactersIn: from))
}
}
let str = "n1o d2i3g4i5t6s!!!789"
let t1 = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(t1) // will print: 123456789
let t2 = str.removeCharacters(from: "0123456789")
print(t2) // will print: no digits!!!
Swift 3 version of Macondo2Seattle's answer, which, I think, is the most elegant solution.
extension String {
func removing(charactersOf string: String) -> String {
let characterSet = CharacterSet(charactersIn: string)
let components = self.components(separatedBy: characterSet)
return components.joined(separator: "")
}
}
Swift 5:
var phrase = "The rain in Spain stays mainly in the plain."
let vowels: Set<Character> = ["a", "e", "i", "o", "u"]
phrase.removeAll(where: { vowels.contains($0) })
// phrase == "Th rn n Spn stys mnly n th pln."
extension String {
func removeCharacters(characters: String) -> String {
let characterSet = NSCharacterSet(charactersInString: characters)
let components = self.componentsSeparatedByCharactersInSet(characterSet)
let result = components.joinWithSeparator("")
return result
}
}
Swift 2.0:
extension String {
func stringByRemovingOnce(chars: String) -> String {
var cs = Set(chars.characters)
let fd = characters.filter { c in
cs.remove(c).map { _ in false } ?? true
}
return String(fd)
}
}
"melpyoemtn".stringByRemovingOnce("ment") // "lpyoem"
Swift 1.2:
extension String {
func stringByRemovingOnce(chars: String) -> String {
var cs = Set(chars)
let fd = filter(self) { c in
cs.remove(c).map { _ in false } ?? true
}
return String(fd)
}
}
"melpyoemtn".stringByRemovingOnce("ment") // "lpyoem"

How can I get a Country Code from the Country Name in iOS? [duplicate]

Im retrieving a country name from a server in english. For example, "Spain"
What I want to do is, assuming the country name is going to be written in english, get the country code.
What should I do? I´ve found getting the country name from the country code quite easy, but I´ve got no idea of how to do the opposite operation.
Thanks a lot.
Jef's answer helped here, with slight additions.
NSArray *countryCodes = [NSLocale ISOCountryCodes];
NSMutableArray *countries = [NSMutableArray arrayWithCapacity:[countryCodes count]];
for (NSString *countryCode in countryCodes)
{
NSString *identifier = [NSLocale localeIdentifierFromComponents: [NSDictionary dictionaryWithObject: countryCode forKey: NSLocaleCountryCode]];
NSString *country = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_UK"] displayNameForKey: NSLocaleIdentifier value: identifier];
[countries addObject: country];
}
NSDictionary *codeForCountryDictionary = [[NSDictionary alloc] initWithObjects:countryCodes forKeys:countries];
Now go through the 'codeForCountryDictionary' with the name of the country for which you require the code, for example,
NSLog(#"%#",[codeForCountryDictionary objectForKey:#"Spain"]);
would yield the result as 'ES', which is the 2 letter country code for spain.
A Swift 4 updated version of #Daxesh Nagar solution:
private func locale(for fullCountryName : String) -> String {
var locales : String = ""
for localeCode in NSLocale.isoCountryCodes {
let identifier = NSLocale(localeIdentifier: localeCode)
let countryName = identifier.displayName(forKey: NSLocale.Key.countryCode, value: localeCode)
if fullCountryName.lowercased() == countryName?.lowercased() {
return localeCode as! String
}
}
return locales
}
For this Input as fullCountryName
"United Kingdom"
It will return the country code as follows
"GB"
Hope it helps you guys!
In the Swift you will use this code
extension NSLocale {
class func locales1(countryName1 : String) -> String {
var locales : String = ""
for localeCode in NSLocale.ISOCountryCodes() {
let countryName = NSLocale.systemLocale().displayNameForKey(NSLocaleCountryCode, value: localeCode)!
if countryName1.lowercaseString == countryName.lowercaseString {
return localeCode as! String
}
}
return locales
}
}
Get the data:
strP2countrycode = NSLocale.locales1("PASS_COUNTRYNAME")
Using swift 3 (some of the answers above are missing a piece)
extension NSLocale {
class func locales1(countryName1 : String) -> String {
let locales : String = ""
for localeCode in NSLocale.isoCountryCodes {
let countryName = (Locale.current as NSLocale).displayName(forKey: .countryCode, value: localeCode)
if countryName1.lowercased() == countryName?.lowercased() {
return localeCode
}
}
return locales
}
}
My compact version including the fix for matching only against english country names version.
extension NSLocale
{
class func localeForCountry(countryName : String) -> String?
{
return NSLocale.ISOCountryCodes().first{self.countryNameFromLocaleCode($0 as! String) == countryName} as? String
}
private class func countryNameFromLocaleCode(localeCode : String) -> String
{
return NSLocale(localeIdentifier: "en_UK").displayNameForKey(NSLocaleCountryCode, value: localeCode) ?? ""
}
}
Here is a simple solution that works in Swift 3 or later. Depending on your OS version, this supports roughly 256 country names and codes.
extension Locale {
func isoCode(for countryName: String) -> String? {
return Locale.isoRegionCodes.first(where: { (code) -> Bool in
localizedString(forRegionCode: code)?.compare(countryName, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
})
}
}
The trick to using this correctly is knowing the language of the country names you wish to convert to the standard 2-letter ISO country code.
If you know the country names are in English, you must use this on a locale set to English. It would be best to use the special locale of en_US_POSIX in such a case.
let locale = Locale(identifier: "en_US_POSIX")
print(locale.isoCode(for: "Spain")) // result is `ES`
If you have country names in Spanish, then be sure to use a Spanish locale:
let locale = Locale(identifier: "es")
print(locale.isoCode(for: "España")) // result is `ES`
Swift 3.0 :
Getting the country code and country name as NS Dictionary-
let countryCodes: [AnyObject] = NSLocale.isoCountryCodes as [AnyObject]
let countries: NSMutableArray = NSMutableArray(capacity: countryCodes.count)
for countryCode in countryCodes {
let identifier: String = NSLocale.localeIdentifier(fromComponents: NSDictionary(object: countryCode, forKey: NSLocale.Key.countryCode as NSCopying) as! [String : String])
let country: String = NSLocale(localeIdentifier: "en_US").displayName(forKey: NSLocale.Key.identifier, value: identifier)!
countries.add(country)
}
let codeForCountryDictionary: [NSObject : AnyObject] = NSDictionary(objects: countryCodes, forKeys: countries as! [NSCopying]) as [NSObject : AnyObject]
print(codeForCountryDictionary)
So this solution works well in swift 4 taken from comments above...
extension NSLocale {
struct Locale {
let countryCode: String
let countryName: String
}
class func locales() -> [Locale] {
var locales = [Locale]()
for localeCode in NSLocale.isoCountryCodes {
let identifier = NSLocale(localeIdentifier: localeCode)
let countryName = identifier.displayName(forKey: NSLocale.Key.countryCode, value: localeCode)!
let countryCode = localeCode
let locale = Locale(countryCode: countryCode, countryName: countryName)
locales.append(locale)
}
return locales.sorted{$0.countryName.localizedCaseInsensitiveCompare($1.countryName) == ComparisonResult.orderedAscending}
}
}
enjoy!
Swift 5
extension Locale {
func countryCode(by countryName: String) -> String? {
return Locale.isoRegionCodes.first(where: { code -> Bool in
guard let localizedCountryName = localizedString(forRegionCode: code) else {
return false
}
return localizedCountryName.lowercased() == countryName.lowercased()
})
}
}
To use:
let locale = Locale(identifier: "en_US")
let countryCode = locale.countryCode(by: "Russia")

iOS: Convert ISO Alpha 2 to Alpha 3 country code

Is it possible to convert ISO 3166 alpha-2 country code to alpha 3 country code in iOS, for instance DE to DEU?
Base on the answer Franck here is the code swift4 for loading the plist and then to convert 3 letters Country ISO code
The plist: Full conversion ISO 3166-1-Alpha2 to Alpha3
//
// CountryUtility.swift
//
import Foundation
struct CountryUtility {
static private func loadCountryListISO() -> Dictionary<String, String>? {
let pListFileURL = Bundle.main.url(forResource: "iso3166_2_to_iso3166_3", withExtension: "plist", subdirectory: "")
if let pListPath = pListFileURL?.path,
let pListData = FileManager.default.contents(atPath: pListPath) {
do {
let pListObject = try PropertyListSerialization.propertyList(from: pListData, options:PropertyListSerialization.ReadOptions(), format:nil)
guard let pListDict = pListObject as? Dictionary<String, String> else {
return nil
}
return pListDict
} catch {
print("Error reading regions plist file: \(error)")
return nil
}
}
return nil
}
/// Convertion ISO 3166-1-Alpha2 to Alpha3
/// Country code of 2 letters to 3 letters code
/// E.g: PT to PRT
static func getCountryCodeAlpha3(countryCodeAlpha2: String) -> String? {
guard let countryList = CountryUtility.loadCountryListISO() else {
return nil
}
if let countryCodeAlpha3 = countryList[countryCodeAlpha2]{
return countryCodeAlpha3
}
return nil
}
static func getLocalCountryCode() -> String?{
guard let countryCode = NSLocale.current.regionCode else { return nil }
return countryCode
}
/// This function will get full country name based on the phone Locale
/// E.g. Portugal
static func getLocalCountry() -> String?{
let countryLocale = NSLocale.current
guard let countryCode = countryLocale.regionCode else { return nil }
let country = (countryLocale as NSLocale).displayName(forKey: NSLocale.Key.countryCode, value: countryCode)
return country
}
}
To use you just need to:
if let countryCode = CountryUtility.getLocalCountryCode() {
if let alpha3 = CountryUtility.getCountryCodeAlpha3(countryCodeAlpha2: countryCode){
print(alpha3) ///result: PRT
}
}

Resources