Data Conversion in Swift
Back in 1936 the computer scientist Alan Turing invented a model of a computational device later known as The Turing Machine. This machine did simple operations and had a state. This state was actually a piece of data, and the machine was “running” algorithms to perform operations over that data.
“Algorithms + Data Structures = Programs”
— Niklaus Wirth, 1976
All that a computer program does is actually just data receiving, sending and processing. Data is that unit with which our code works. Even operations which don’t look like data processing, for example, showing the user interface or handling button clicks, are actually sending packets of data to an API and receiving responses.
Now, in 2020, we still work with algorithms and data. What became more complex are the data structures and, as a result, the conversions between them. Let’s have a look at the most popular Swift data types, structures and classes and find (or write) functions converting one into another.
A note about Swift types
Swift inherited its types from Objective-C, but Swift has a different naming convention for complex types. Classes from the Foundation framework typically start with the prefix NS (from NextStep). In Swift the NS is usually dropped and instead of NSString
you have String
and Data
instead of NSData
.
In some cases it’s kind of renaming, but not everywhere. TheString
class doesn’t have access to all NSString
methods. It’s not an alias, it’s a wrap around NSString
. But Swift allows to make a fast conversion if you need to access properties and methods of the original NSString
class:
let str: String = "Some String"
let nsstr = str as NSString
Such conversions never fail, that’s why you don’t need to use !
or ?
after as
.
Data to String and back
In the C programming language Data
and String
were the same data type, and they were the same as arrays of bytes. Modern String
s are not just buffers of data, they also have information about encoding and many useful methods.
At the same time, Data
(aka NSData
) can logically contain text, which means there should be a way to convert it to String
(aka NSString
) and back.
I use exclamation marks for the sake of simplicity. Thedata
method returns optional Data
objects. Read here how to deal with optionals in a proper way.
Numbers to String and back
Another popular conversion is numbers (Int
, Double
and others) to String
and back.
The easiest (but not always the best) way to convert number to String
is String interpolation. In other words, inserting variables (or constants) to strings using \()
constructions. The disadvantage of this method is that you can’t format it. For example, if you want to use a currency formatting, it’s better to use String.format
:
Format specifiers in Swift are inherited from Objective-C, and format specifiers in Objective-C are inherited from the legendary printf
function family in C. You can find a list of Swift format specifiers here.
Another important note: when you turn String
into Int
or Double
you get an optional value. It makes sense, because Int("a")
should return nil
. Int("1.5")
will also return nil
, not 1
or 2
.
String to Dictionary and Array and back
This is a big topic, because it depends on the String
format. The 3 most popular formats are JSON, XML and YAML.
- JSON — JavaScript Object Notation. A variation of JavaScript language, or more precisely of the part of JavaScript language responsible for describing objects and arrays. An interesting fact about JSON is that it can be pasted into JavaScript code and parsed by web browsers or another JavaScript engines.
- XML — eXtensible Markup Language. It’s a popular language for creating layouts of web pages and UIs. It’s the main format of Universal Windows Platform layouts, Android layouts and a close relative of HTML (HyperText Markup Language) and kind of a parent of PLIST (Property List) format actively used for iOS and macOS development.
- YAML — Yet Another Markup Language. Another popular language to represent data. Often used for configuration files, for example, in Flutter.
These 3 languages look very different, but they have some things in common:
- They are text strings.
- Swift dictionaries and arrays can be serialised into them and deserialised from them as long as each data type inside dictionaries and arrays is serialisable.
JSON
The easiest case is JSON. All modern languages support JSON as it’s the most common way of information exchange with server APIs.
This example shows how to convert a JSON String
to Dictionary
and back. In the end it prints this:
{
"key1" : "val1",
"arr" : [
1,
2,
3
],
"key2" : "val2"
}
It’s easy to see that it’s the same data structure as we provide, but formatted differently . If you need to prepare data for sending to the API, you’d better remove the .prettyPrinted
option to get a more optimised (but less human readable) output.
XML
The next is XML. Swift has the internal class XMLParser
, but it doesn’t make conversions. It’s very powerful, but very uncomfortable to use. For example, that’s how we can parse a simple XML String
:
The output of this code is:
Found element parent with attributes [:]Found element child with attributes ["attr": "attr"]
As you can see, it parses XML correctly, but it doesn’t create a Dictionary
or an Array
object.
To get a dictionary, you need an external library. For example, SWXMLHash
. This library is a wrapper around XMLParser
(aka NSXMLParser
), but it also makes conversion, which is exactly what we need.
You can add it with Cocoapods:
pod 'SWXMLHash'
If you’re not familiar with Cocoapods or you want to find another useful libraries for your project, you can read this article.
Then you parse XML document with just one line of code:
let xmlDict = SWXMLHash.parse(xml)
How to write XML? The easiest way is to generate String
with String.format
, but you may have problems with some symbols… you’ll need to escape them. Has anyone done it for us?
I didn’t find any modern solution. All the libraries are either deprecated or do something different. But I found a post here. It’s in Objective-C and doesn’t handle all the cases, so I wrote an updated version:
XML can have only one root element, that’s why we need to provide it specifically. See the example below:
let dict: [String: Any] = [
"child1": [1, 2, 3],
"child2": "Hello, world",
"child3": [
["a": "b"],
"cd",
5
]
]let xml = convertDictionaryToXML(dictionary: dict, startElement: "root", isFirstElement: true)
print(xml)
The output will be the following:
<?xml version="1.0" encoding="utf-8"?>
<root>
<child2>Hello, world</child2>
<child3>
<a>b</a>
</child3>
<child3>cd</child3>
<child3>5</child3>
<child1>1</child1>
<child1>2</child1>
<child1>3</child1>
</root>
This is a valid XML. As a side note I will mention that the elements of arrays are ordered, but the keys in the dictionary are not. This is a common rule for all programming languages.
YAML
Swift doesn’t provide an integrated way to create or parse YAML. But there are good libraries for it. For example, Yams.
Include it with Cocoapods:
pod 'Yams'
Import it:
import Yaml
And use:
let strYAML: String? = try? Yams.dump(object: dictionary)let loadedDictionary = try? Yams.load(yaml: mapYAML) as? [String: Any]
Numeric conversions
Swift makes it easy to convert different numeric types.
The problem is that conversion from Double
(and Float
) to Int
will always ignore the decimal part. To fix this issue and get a mathematically correct rounding, use the round
function on Double
before converting it to Int
:
Converting Bool
Bool
or Boolean is a logical type, which can have only two values: true
and false
. true
in Objective-C is also known as YES
and false
is NO
. Even if you program in Swift, you may see YES
and NO
in PLIST files and Objective-C libraries.
Usually true
is represented in memory as 1 and false
as 0. In C, for example, you can write if (1) { ... }
and while (1) { ... }
makes an infinite loop unless you terminate it with break
or return
. JavaScript offers the conception of truth-y and false-y values. For example, 1 is truth-y and nil is false-y.
Swift needs an explicit conversion to Bool
for these cases. Constructions like let b = Bool(1)
will cause a warning. And in my opinion it’s the right thing. Because I don’t know if -1 is truth-y or false-y. Different programmers will give different answers. And different programming languages will interpret it differently.
Many APIs return 0 for success and non-0 value for errors. The WIN32 API defines a constant S_OK
, which is 0. And if you write:
if (some_call()) {
// ...
}
the inner code will never be executed if some_call returns S_OK
.
I’m writing all this to say that all conversions between boolean values and numeric values should be forbidden to avoid all this mess.
On the other hand, conversions between Bool
and String
make sense.
As you can see from the example above, Swift considers only “true” and “false” as valid boolean strings. That’s why, working in Swift, you should serialise Bool
this way.
If you use external APIs, for example, REST API, you should check how boolean values are presented in the documentation and make explicit conversions. For example:
Date and Time
For both date and time Swift offers the data type Date
. It’s easy to confuse with Data
, but it’s totally different.
Technically, Date
is a combination of two values:
- Timestamp
- Time zone
When we talk about dates, it makes sense to convert it to (from) two data types — Double
and String
. Double
(aka TimeInterval
) is the way Swift represents timestamps (amount of seconds since Epoch, 1 January 1970). And String
has many purposes, starting with showing date and/or time to the user and ending with sending them to the server in ISO-8601 format.
The example above shows how Date
can be converted into timestamp or one of possible strings.
The opposite conversion is also possible. For example:
As you can probably guess, objects date1
, date2
and date3
refer to the same date and time (except milliseconds, they will be different).
Conclusion
Swift is a strictly typed programming language, that’s why even simple data conversions need to be made explicitly. We reviewed conversions between the most common data types and data formats used in modern programming.
Happy coding and see you next time!