Convert Kelvin into Celsius in Swift - ios

I'm trying to retrieve the temperature from the users current location.
I am using the API from OpenWeatherMap. The problem is, they provide the temperature in Kelvin as default, and I would like it in Celsius.
I understand that I just need to subtract 273.15 from the kelvin value....? But i'm struggling to figure out where to do that.
My code for setting my labels:
var jsonData: AnyObject?
func setLabels(weatherData: NSData) {
do {
self.jsonData = try NSJSONSerialization.JSONObjectWithData(weatherData, options: []) as! NSDictionary
} catch {
//handle error here
}
if let name = jsonData!["name"] as? String {
locationLabel.text = "using your current location, \(name)"
}
if let main = jsonData!["main"] as? NSDictionary {
if let temperature = main["temp"] as? Double {
self.tempLabel.text = String(format: "%.0f", temperature)
}
}
}
Can anyone help me get this right please, as I'm really not sure where to start, thanks.
Let me know if you need to see more of my code.

if let kelvinTemp = main["temp"] as? Double {
let celsiusTemp = kelvinTemp - 273.15
self.tempLabel.text = String(format: "%.0f", celsiusTemp)
}
or simply
self.tempLabel.text = String(format: "%.0f", temperature - 273.15)

For Swift 4.2:
Use a Measurement Formatter.
let mf = MeasurementFormatter()
This method converts one temperature type (Kelvin, Celsius, Fahrenheit) to another:
func convertTemp(temp: Double, from inputTempType: UnitTemperature, to outputTempType: UnitTemperature) -> String {
mf.numberFormatter.maximumFractionDigits = 0
mf.unitOptions = .providedUnit
let input = Measurement(value: temp, unit: inputTempType)
let output = input.converted(to: outputTempType)
return mf.string(from: output)
}
Usage:
let temperature = 291.0
let celsius = convertTemp(temp: temperature, from: .kelvin, to: .celsius) // 18°C
let fahrenheit = convertTemp(temp: temperature, from: .kelvin, to: .fahrenheit) // 64°F
To output the localized temperature format, remove the line mf.unitOptions = .providedUnit

From the code above, it seems to me the right place to do this would be right after you get the temperature
if let temperatureInKelvin = main["temp"] as? Double {
let temperatureInCelsius = temperatureInKelvin - 273.15
self.tempLabel.text = String(format: "%.0f", temperature)
}
In the future though, I would probably parse your JSON values in a separate class and store them in a model object which you can call later on.

A simpler example of the above handy function (updated for Swift 5.3) would be something like:
func convertTemperature(temp: Double, from inputTempType: UnitTemperature, to outputTempType: UnitTemperature) -> Double {
let input = Measurement(value: temp, unit: inputTempType)
let output = input.converted(to: outputTempType)
return output.value
}

Here:
self.tempLabel.text = String(format: "%.0f", temperature - 273.15)
or you can do it here (pseudo syntax as I don't know Swift that well):
if let temperature = (main["temp"] as? Double) - 273.15 {
self.tempLabel.text = String(format: "%.0f", temperature)
}

Related

Swift, converting a number to string, number gets rounded down

I'm having a bit of issue with my code...right now, I am passing a string containing a bunch of numbers, to get converted to a number, comma separators added, then converted back to a string for output. When I add a decimal to my string and pass it in, a number like 996.3658 get truncated to 996.366...
"currentNumber" is my input value, "textOutputToScreen" is my output...
func formatNumber() {
let charset = CharacterSet(charactersIn: ".")
if let _ = currentNumber.rangeOfCharacter(from: charset) {
if let number = Float(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
else {
if let number = Int(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
}
Thank you in advance for your help!
The issue there is that you have to set your NumberFormatter minimumFractionDigits to 4. Btw there is no need to initialize a NSNumber object. You can use Formatters string(for: Any) method and pass your Float. Btw I would use a Double (64-bit) instead of a Float (32-bit) and there is no need to initialize a new string g from your formattedNumber object. It is already a String.
Another thing is that you don't need to know the location of the period you can simply use contains instead of rangeOfCharacter method. Your code should look something like this:
extension Formatter {
static let number: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
}
func formatNumber(from string: String) -> String? {
if string.contains(".") {
guard let value = Double(string) else { return nil }
Formatter.number.minimumFractionDigits = 4
return Formatter.number.string(for: value)
} else {
guard let value = Int(string) else { return nil }
Formatter.number.minimumFractionDigits = 0
return Formatter.number.string(for: value)
}
}
let label = UILabel()
let currentNumber = "996.3658"
label.text = formatNumber(from: currentNumber) // "996.3658\n"
If you would like to assign the result to your var instead of a label
if let formatted = formatNumber(from: currentNumber) {
textOutputToScreen = formatted
}

How to know data coming from JSON is a Float or an Integer in Swift 3?

I am getting data from Json and displaying it in table view how to check whether the number is float or double or integer in swift 3 if it is float how to get the no.of digits after decimal can anyone help me how to implement this in swift 3 ?
if specialLoop.attributeCode == "special_price" {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "$ \((arr.price))")
attributeString.addAttribute(NSStrikethroughStyleAttributeName, value: 1, range: NSMakeRange(0, attributeString.length))
let specialPrice = specialLoop.value.replacingOccurrences(of: ".0000", with: "0")
print(specialPrice)
cell.productPrice.text = "$ \(specialPrice)"
cell.specialPriceLabel.isHidden = false
cell.specialPriceLabel.attributedText = attributeString
break
}
else {
cell.specialPriceLabel.isHidden = true
let price = arr.price
print(price)
cell.productPrice.text = "$ \( (price))0"
}
You can use (if let)
let data = [String: Any]()
if let value = data["key"] as? Int {
} else if let value = data["key"] as? Float {
} else if let value = data["key"] as? Double {
}
as describe below, you can find a type of any object (whether custom class or built-in class like - String, Int, etc.).
class demo {
let a: String = ""
}
let demoObj = demo()
print(type(of: demoObj))
--> Output: "demo.Type"

How to trim a String using Swift 3

My code snippet is:
unwanted = " £€₹jetztabfromnow"
let favouritesPriceLabel = priceDropsCollectionView.cells.element(boundBy: UInt(index)).staticTexts[IPCUIAHighlightsPriceDropsCollectionViewCellPriceLabel].label
let favouritesPriceLabelTrimmed = favouritesPriceLabel.components(separatedBy: "jetzt").flatMap { String($0.trimmingCharacters(in: .whitespaces)) }.last
favouritesHighlightsDictionary[favouritesTitleLabel] = favouritesPriceLabelTrimmed
My problem is, this didn't work:
let favouritesPriceLabelTrimmed = favouritesPriceLabel.components(separatedBy: unwanted).flatMap { String($0.trimmingCharacters(in: .whitespaces)) }.last
I have a price like "from 3,95 €" - I want to cut all currencies "£€₹" and words like "from" or "ab"
Do you have a solution for me, what I can use here?
Rather than mess around with trying to replace or remove the right characters or using regular expressions, I'd go with Foundation's built-in linguistic tagging support. It will do a lexical analysis of the string and return tokens of various types. Use it on this kind of string and it should reliably find any numbers in the string.
Something like:
var str = "from 3,95 €"
let range = Range(uncheckedBounds: (lower: str.startIndex, upper: str.endIndex))
var tokenRanges = [Range<String.Index>]()
let scheme = NSLinguisticTagSchemeLexicalClass
let option = NSLinguisticTagger.Options()
let tags = str.linguisticTags(in: range, scheme: scheme, options: option, orthography: nil, tokenRanges: &tokenRanges)
let tokens = tokenRanges.map { str.substring(with:$0) }
if let numberTagIndex = tags.index(where: { $0 == "Number" }) {
let number = tokens[numberTagIndex]
print("Found number: \(number)")
}
In this example the code prints "3,95". If you change str to "from £28.50", it prints "28.50".
One way is to place the unwanted strings into an array, and use String's replacingOccurrences(of:with:) method.
let stringToScan = "£28.50"
let toBeRemoved = ["£", "€", "₹", "ab", "from"]
var result = stringToScan
toBeRemoved.forEach { result = result.replacingOccurrences(of: $0, with: "") }
print(result)
...yields "28.50".
If you just want to extract the numeric value use regular expression, it considers comma or dot decimal separators.
let string = "from 3,95 €"
let pattern = "\\d+[.,]\\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
if let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) {
let range = match.range
let start = string.index(string.startIndex, offsetBy: range.location)
let end = string.index(start, offsetBy: range.length)
print(string.substring(with: start..<end)) // 3,95
} else {
print("Not found")
}
} catch {
print("Regex Error:", error)
}
I asked if you had a fixed locale for this string, because then you can use the locale to determine what the decimal separator is: For example, try this in a storyboard.
let string = "some initial text 3,95 €" // define the string to scan
// Add a convenience extension to Scanner so you don't have to deal with pointers directly.
extension Scanner {
func scanDouble() -> Double? {
var value = Double(0)
guard scanDouble(&value) else { return nil }
return value
}
// Convenience method to advance the location of the scanner up to the first digit. Returning the scanner itself or nil, which allows for optional chaining
func scanUpToNumber() -> Scanner? {
var value: NSString?
guard scanUpToCharacters(from: CharacterSet.decimalDigits, into: &value) else { return nil }
return self
}
}
let scanner = Scanner(string: string)
scanner.locale = Locale(identifier: "fr_FR")
let double = scanner.scanUpToNumber()?.scanDouble() // -> double = 3.95 (note the type is Double?)
Scanners are a lot easier to use than NSRegularExpressions in these cases.
You can filter by special character by removing alphanumerics.
extension String {
func removeCharacters(from forbiddenChars: CharacterSet) -> String {
let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
return String(String.UnicodeScalarView(passed))
}
}
let str = "£€₹jetztabfromnow12"
let t1 = str.removeCharacters(from: CharacterSet.alphanumerics)
print(t1) // will print: £€₹
let t2 = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(t2) // will print: 12
Updated 1:
var str = "£3,95SS"
str = str.replacingOccurrences(of: ",", with: "")
let digit = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(digit) // will print: 395
let currency = str.removeCharacters(from: CharacterSet.alphanumerics)
print(currency) // will print: £
let amount = currency + digit
print(amount) // will print: £3,95
Update 2:
let string = "£3,95SS"
let pattern = "-?\\d+(,\\d+)*?\\.?\\d+?"
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
if let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) {
let range = match.range
let start = string.index(string.startIndex, offsetBy: range.location)
let end = string.index(start, offsetBy: range.length)
let digit = string.substring(with: start..<end)
print(digit) //3,95
let symbol = string.removeCharacters(from: CharacterSet.symbols.inverted)
print(symbol) // £
print(symbol + digit) //£3,95
} else {
print("Not found")
}
} catch {
print("Regex Error:", error)
}

Choosing units with MeasurementFormatter

This is similar to a question I asked yesterday but the answer I got doesn't seem to work in this case.
I'm getting altitude values in meters from Core Location. I want to display these in a localized form. As an example, the altitude where I am right now is 1839m above sea level. This should be displayed as 6033 feet. The best I can do with MeasurementFormatter is "1.143 mi".
let meters : Double = 1839
let metersMeasurement = Measurement(value: meters, unit: UnitLength.meters)
let measurementFormatter = MeasurementFormatter()
measurementFormatter.locale = Locale(identifier: "en_US")
let localizedString = measurementFormatter.string(from: metersMeasurement)
The .naturalScale option that answered my previous question doesn't help here. I think this is a limitation of the framework, but I wonder if anyone has a workaround for now.
You just need to convert your UnitLength from meters to feet. You can also create a custom US measurement formatter to display it as needed:
extension Measurement where UnitType == UnitLength {
private static let usFormatted: MeasurementFormatter = {
let formatter = MeasurementFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 0
formatter.unitStyle = .long
return formatter
}()
var usFormatted: String { Measurement.usFormatted.string(from: self) }
}
Playground
let value: Double = 1839
let meters: Measurement<UnitLength> = .init(value: value, unit: .meters)
let feet = meters.converted(to: .feet)
let formatted = feet.usFormatted
print(formatted) // "6,033 feet"\n
I think you are correct there's no way to specify this kind of context. You could do something like:
extension MeasurementFormatter
{
func altitudeString(from measurement: Measurement<UnitLength>) -> String
{
var measurement = measurement
let unitOptions = self.unitOptions
let unitStyle = self.unitStyle
self.unitOptions = .naturalScale
self.unitStyle = .long
var string = self.string(from: measurement)
if string.contains(self.string(from: UnitLength.miles))
{
self.unitStyle = unitStyle
measurement.convert(to: UnitLength.feet)
self.unitOptions = .providedUnit
string = self.string(from: measurement)
}
else if string.contains(self.string(from: UnitLength.kilometers))
{
self.unitStyle = unitStyle
measurement.convert(to: UnitLength.meters)
self.unitOptions = .providedUnit
string = self.string(from: measurement)
}
else
{
self.unitStyle = unitStyle
string = self.string(from: measurement)
}
self.unitOptions = unitOptions
return string
}
}
Maybe there are other culturally specific ways of measuring elevation, but this would seem better than miles and kilometers.

How to create a String with format?

I need to create a String with format which can convert Int, Int64, Double, etc types into String. Using Objective-C, I can do it by:
NSString *str = [NSString stringWithFormat:#"%d , %f, %ld, %#", INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, STRING_VALUE];
How to do same but in Swift?
I think this could help you:
import Foundation
let timeNow = time(nil)
let aStr = String(format: "%#%x", "timeNow in hex: ", timeNow)
print(aStr)
Example result:
timeNow in hex: 5cdc9c8d
nothing special
let str = NSString(format:"%d , %f, %ld, %#", INT_VALUE, FLOAT_VALUE, LONG_VALUE, STRING_VALUE)
let str = "\(INT_VALUE), \(FLOAT_VALUE), \(DOUBLE_VALUE), \(STRING_VALUE)"
Update: I wrote this answer before Swift had String(format:) added to it's API. Use the method given by the top answer.
No NSString required!
String(format: "Value: %3.2f\tResult: %3.2f", arguments: [2.7, 99.8])
or
String(format:"Value: %3.2f\tResult: %3.2f", 2.7, 99.8)
I would argue that both
let str = String(format:"%d, %f, %ld", INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE)
and
let str = "\(INT_VALUE), \(FLOAT_VALUE), \(DOUBLE_VALUE)"
are both acceptable since the user asked about formatting and both cases fit what they are asking for:
I need to create a string with format which can convert int, long, double etc. types into string.
Obviously the former allows finer control over the formatting than the latter, but that does not mean the latter is not an acceptable answer.
First read Official documentation for Swift language.
Answer should be
var str = "\(INT_VALUE) , \(FLOAT_VALUE) , \(DOUBLE_VALUE), \(STRING_VALUE)"
println(str)
Here
1) Any floating point value by default double
EX.
var myVal = 5.2 // its double by default;
-> If you want to display floating point value then you need to explicitly define such like a
EX.
var myVal:Float = 5.2 // now its float value;
This is far more clear.
let INT_VALUE=80
let FLOAT_VALUE:Double= 80.9999
let doubleValue=65.0
let DOUBLE_VALUE:Double= 65.56
let STRING_VALUE="Hello"
let str = NSString(format:"%d , %f, %ld, %#", INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, STRING_VALUE);
println(str);
The accepted answer is definitely the best general solution for this (i.e., just use the String(format:_:) method from Foundation) but...
If you are running Swift ≥ 5, you can leverage the new StringInterpolationProtocol protocol to give yourself some very nice syntax sugar for common string formatting use cases in your app.
Here is how the official documentation summarizes this new protocol:
Represents the contents of a string literal with interpolations while it’s being built up.
Some quick examples:
extension String.StringInterpolation {
/// Quick formatting for *floating point* values.
mutating func appendInterpolation(float: Double, decimals: UInt = 2) {
let floatDescription = String(format: "%.\(decimals)f%", float)
appendLiteral(floatDescription)
}
/// Quick formatting for *hexadecimal* values.
mutating func appendInterpolation(hex: Int) {
let hexDescription = String(format: "0x%X", hex)
appendLiteral(hexDescription)
}
/// Quick formatting for *percents*.
mutating func appendInterpolation(percent: Double, decimals: UInt = 2) {
let percentDescription = String(format: "%.\(decimals)f%%", percent * 100)
appendLiteral(percentDescription)
}
/// Formats the *elapsed time* since the specified start time.
mutating func appendInterpolation(timeSince startTime: TimeInterval, decimals: UInt = 2) {
let elapsedTime = CACurrentMediaTime() - startTime
let elapsedTimeDescription = String(format: "%.\(decimals)fs", elapsedTime)
appendLiteral(elapsedTimeDescription)
}
}
which could be used as:
let number = 1.2345
"Float: \(float: number)" // "Float: 1.23"
"Float: \(float: number, decimals: 1)" // "Float: 1.2"
let integer = 255
"Hex: \(hex: integer)" // "Hex: 0xFF"
let rate = 0.15
"Percent: \(percent: rate)" // "Percent: 15.00%"
"Percent: \(percent: rate, decimals: 0)" // "Percent: 15%"
let startTime = CACurrentMediaTime()
Thread.sleep(forTimeInterval: 2.8)
"∆t was \(timeSince: startTime)" // "∆t was 2.80s"
"∆t was \(timeSince: startTime, decimals: 0)" // "∆t was 3s"
This was introduced by SE-0228, so please be sure to read the original proposal for a deeper understanding of this new feature. Finally, the protocol documentation is helpful as well.
I know a lot's of time has passed since this publish, but I've fallen in a similar situation and create a simples class to simplify my life.
public struct StringMaskFormatter {
public var pattern : String = ""
public var replecementChar : Character = "*"
public var allowNumbers : Bool = true
public var allowText : Bool = false
public init(pattern:String, replecementChar:Character="*", allowNumbers:Bool=true, allowText:Bool=true)
{
self.pattern = pattern
self.replecementChar = replecementChar
self.allowNumbers = allowNumbers
self.allowText = allowText
}
private func prepareString(string:String) -> String {
var charSet : NSCharacterSet!
if allowText && allowNumbers {
charSet = NSCharacterSet.alphanumericCharacterSet().invertedSet
}
else if allowText {
charSet = NSCharacterSet.letterCharacterSet().invertedSet
}
else if allowNumbers {
charSet = NSCharacterSet.decimalDigitCharacterSet().invertedSet
}
let result = string.componentsSeparatedByCharactersInSet(charSet)
return result.joinWithSeparator("")
}
public func createFormattedStringFrom(text:String) -> String
{
var resultString = ""
if text.characters.count > 0 && pattern.characters.count > 0
{
var finalText = ""
var stop = false
let tempString = prepareString(text)
var formatIndex = pattern.startIndex
var tempIndex = tempString.startIndex
while !stop
{
let formattingPatternRange = formatIndex ..< formatIndex.advancedBy(1)
if pattern.substringWithRange(formattingPatternRange) != String(replecementChar) {
finalText = finalText.stringByAppendingString(pattern.substringWithRange(formattingPatternRange))
}
else if tempString.characters.count > 0 {
let pureStringRange = tempIndex ..< tempIndex.advancedBy(1)
finalText = finalText.stringByAppendingString(tempString.substringWithRange(pureStringRange))
tempIndex = tempIndex.advancedBy(1)
}
formatIndex = formatIndex.advancedBy(1)
if formatIndex >= pattern.endIndex || tempIndex >= tempString.endIndex {
stop = true
}
resultString = finalText
}
}
return resultString
}
}
The follow link send to the complete source code:
https://gist.github.com/dedeexe/d9a43894081317e7c418b96d1d081b25
This solution was base on this article:
http://vojtastavik.com/2015/03/29/real-time-formatting-in-uitextfield-swift-basics/
There is a simple solution I learned with "We <3 Swift" if you can't either import Foundation, use round() and/or does not want a String:
var number = 31.726354765
var intNumber = Int(number * 1000.0)
var roundedNumber = Double(intNumber) / 1000.0
result: 31.726
Use this following code:
let intVal=56
let floatval:Double=56.897898
let doubleValue=89.0
let explicitDaouble:Double=89.56
let stringValue:"Hello"
let stringValue="String:\(stringValue) Integer:\(intVal) Float:\(floatval) Double:\(doubleValue) ExplicitDouble:\(explicitDaouble) "
The beauty of String(format:) is that you can save a formatting string and then reuse it later in dozen of places. It also can be localized in this single place. Where as in case of the interpolation approach you must write it again and again.
Simple functionality is not included in Swift, expected because it's included in other languages, can often be quickly coded for reuse. Pro tip for programmers to create a bag of tricks file that contains all this reuse code.
So from my bag of tricks we first need string multiplication for use in indentation.
#inlinable func * (string: String, scalar: Int) -> String {
let array = [String](repeating: string, count: scalar)
return array.joined(separator: "")
}
and then the code to add commas.
extension Int {
#inlinable var withCommas:String {
var i = self
var retValue:[String] = []
while i >= 1000 {
retValue.append(String(format:"%03d",i%1000))
i /= 1000
}
retValue.append("\(i)")
return retValue.reversed().joined(separator: ",")
}
#inlinable func withCommas(_ count:Int = 0) -> String {
let retValue = self.withCommas
let indentation = count - retValue.count
let indent:String = indentation >= 0 ? " " * indentation : ""
return indent + retValue
}
}
I just wrote this last function so I could get the columns to line up.
The #inlinable is great because it takes small functions and reduces their functionality so they run faster.
You can use either the variable version or, to get a fixed column, use the function version. Lengths set less than the needed columns will just expand the field.
Now you have something that is pure Swift and does not rely on some old objective C routine for NSString.
Since String(format: "%s" ...) is crashing at run time, here is code to allow write something like "hello".center(42); "world".alignLeft(42):
extension String {
// note: symbol names match to nim std/strutils lib:
func align (_ boxsz: UInt) -> String {
self.withCString { String(format: "%\(boxsz)s", $0) }
}
func alignLeft (_ boxsz: UInt) -> String {
self.withCString { String(format: "%-\(boxsz)s", $0) }
}
func center (_ boxsz: UInt) -> String {
let n = self.count
guard boxsz > n else { return self }
let padding = boxsz - UInt(n)
let R = padding / 2
guard R > 0 else { return " " + self }
let L = (padding%2 == 0) ? R : (R+1)
return " ".withCString { String(format: "%\(L)s\(self)%\(R)s", $0,$0) }
}
}
Success to try it:
var letters:NSString = "abcdefghijkl"
var strRendom = NSMutableString.stringWithCapacity(strlength)
for var i=0; i<strlength; i++ {
let rndString = Int(arc4random() % 12)
//let strlk = NSString(format: <#NSString#>, <#CVarArg[]#>)
let strlk = NSString(format: "%c", letters.characterAtIndex(rndString))
strRendom.appendString(String(strlk))
}

Resources