<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Kirby" -->
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">

  <channel>
    <title>Anerma &#8211; Blog</title>
    <link>https://tantal.uber.space/blog</link>
    <generator>Kirby</generator>
    <lastBuildDate>Sun, 02 Feb 2020 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://tantal.uber.space" rel="self" type="application/rss+xml" />

        <description>iOS, Objective-C, Swift, Design and Whatever Comes in Mind &#8211; Andreas Neusuess</description>
    
        <item>
      <title>Open-Source-Project: Audiograph</title>
      <link>https://tantal.uber.space/blog/open-source-project-audiograph</link>
      <guid>blog/open-source-project-audiograph</guid>
      <pubDate>Sun, 02 Feb 2020 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Shortly after iOS 13 was released I <a href="https://twitter.com/minofifa/status/1169435380950528000">discovered</a>  a new accessibility feature of the native stocks app:<br />
The app offers to use to rotor to select <em>&quot;data comprehension&quot;</em>. After doing so the user can select between various options to get a verbal description of the chart, for example the trend or minimum and maximum values. One option that stands out is <em>&quot;play audiograph&quot;</em>.</p>
<p>If you haven't tried it yourself, here is a short video demonstrating it:</p>
<video controls poster="https://anerma.de/uploads/iOS_13_preview.png">
  <source src="https://anerma.de/uploads/iOS_13_Audiograph.mp4" type="video/mp4">
<figure><img src="https://anerma.de/uploads/iOS_13_Audiograph.mp4" alt=""></figure>
</video>
<p>Using sound to describe a chart for accessibility reasons seems to be a great idea and because Apple has put so much effort into it, it seems like people really benefit from that!<br />
Charts most often describe a time-value relationship that is otherwise really hard to put into words. Why should disabled people be excluded from that?</p>
<p>Unfortunately there is no public API from Apple that enables developers to implement it in their apps (yet). What's more, there seems to be no open source library that fills the gap <strong>(yet)</strong>.  </p>
<h2>This is where my small new side project comes in:</h2>
<figure><img src="https://tantal.uber.space/content/4-blog/57-open-source-project-audiograph/logo.png" alt="Logo of Audiograph project"></figure>
<p>The usage is rather simple:</p>
<pre><code class="language-swift">private func playAudiograph() {
    audiograph.play(graphContent: points)
}</code></pre>
<p><code>Audiograph</code> can play the content that is visually displayed by the chart. The developer just needs to invoke the <code>play</code> method, passing an array of <code>CGPoint</code> that is already used to draw the UI.<br />
All logic that's left is to decide when to play the Audiograph.</p>
<p>In order to help for that, Audiograph provides a pre-configured <code>UIAccessibilityCustomAction</code> for the developer to add to the chart view.</p>
<pre><code class="language-swift">accessibilityCustomActions = [audiograph.createCustomAccessibilityAction(for: self)]</code></pre>
<p>This is what it looks like in action:</p>
<video controls poster="https://anerma.de/uploads/Audiograph_preview.png">
  <source src="https://anerma.de/uploads/Audiograph_Demo_small.mp4" type="video/mp4">
<figure><img src="https://anerma.de/uploads/Audiograph_Demo_small.mp4" alt=""></figure>
</video>
<p>You can read more on that project at <a href="https://github.com/Tantalum73/Audiograph">GitHub</a>.</p>
<blockquote>
<p>When your app draws a chart in any way, <strong>please</strong> give it a try. Charts should not be a limited experience to those without impairments.</p>
</blockquote>
<p>If you have any ideas for improvement please <a href="https://twitter.com/Klaarname">contact me</a> 🙂</p>]]></description>
    </item>
        <item>
      <title>SwiftUI: Loading Persistent Data</title>
      <link>https://tantal.uber.space/blog/swiftui-loading-persistant-data</link>
      <guid>blog/swiftui-loading-persistant-data</guid>
      <pubDate>Sun, 28 Jul 2019 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>In my early attempt in learning SwiftUI I stumbled upon a fairly common problem: <strong>loading data from disk and saving it after updates were made</strong>.<br><br />
It seems simple at first but when thinking about I can come up with a number of hurdles one has to take:</p>
<ol>
<li>Loading data should be performed in the background. Thus the data is not immediately available to us. It can even come from a database request or via network.</li>
<li>The UI should reload as soon as data is available.</li>
<li>Changes the user has made must be saved so that the model layer is always persisted.</li>
<li>However, the file system should not be asked to write a file <em>every time</em> the user interacts. Give them a little time until writes are made. That's called <strong>debouncing</strong>.</li>
</ol>
<h2>Let's Go!</h2>
<p>In order to create a sample project we start by defining our very <strong>basic model</strong>:</p>
<pre><code class="language-swift ">struct Model: Codable {
    var content: [Element]
}

struct Element: Codable, Identifiable {
    let id = UUID()
    let name: String
}</code></pre>
<p>Now it is a great time to care about a class that saves an object to disk and is able to read it from there. Let's call it <em>PersistanceController</em>. When loading data, it calls a completion handler to deliver that data:</p>
<pre><code class="language-swift">class PersistanceController&lt;T: Codable&gt; {
...

func loadData(for filename: String, fileExtension: String, defaultValue: T, completion: @escaping ((T) -&gt; Void)) {
        let fileManager = FileManager()
        let dataURL = pathToSavedDataInSharedContainer(forName: filename, fileExtension: fileExtension)
        if fileManager.fileExists(atPath: dataURL.path) {
            let decoder = PropertyListDecoder()

            queue.async(flags: .barrier) {
                guard let data = try? Data(contentsOf: dataURL), let decoded = try? decoder.decode(T.self, from: data) else {
                    DispatchQueue.main.async {
                        completion(defaultValue)
                    }
                    return
                }
                DispatchQueue.main.async {
                    completion(decoded)
                }
            }
        }
        else {
            completion(defaultValue)
        }
    }
}</code></pre>
<p>For <em>writing data</em> we define our function as follows:</p>
<pre><code class="language-swift">private func save(_ object: T, fileName: String, fileExtension: String = "plist", completionHandler: (() -&gt; Void)? ) {
        let fileManager = FileManager()
        let fileURL = pathToSavedDataInSharedContainer(forName: fileName, fileExtension: fileExtension)
        if !fileManager.fileExists(atPath: fileURL.path) {
            // Directory needs to be created first.
            try? fileManager.createDirectory(atPath: applicationDataDirectory.path, withIntermediateDirectories: true, attributes: nil)
        }

        let encoder = PropertyListEncoder()
        queue.async(flags: .barrier) {
            defer {
                DispatchQueue.main.async(execute: completionHandler ?? {})
            }
            guard let data = try? encoder.encode(object) else {
                return
            }
            try? data.write(to: fileURL, options: [.atomic])
        }
    }
</code></pre>
<p>Those functions can potentially do whetever they want them to do as long as they save and restore the data. <br><br />
To make our lives easier when it comes to storing updated data we introduce a <code>Subscriber</code>. New values are passed in through whatever publisher this subscriber is connected to <em>(we'll cover that later)</em>. Errors are not handled in this simple example.</p>
<pre><code>// Should be used to trigger a write request.
 var writeSubscriber: Subscribers.Sink&lt;T, Never&gt;!

private func setupSubscriber() {
        writeSubscriber = Subscribers.Sink(receiveCompletion: nil, receiveValue: { (value) in
            self.save(value, fileName: self.filename) {
            // No error handling here.
                print("Saved")
            }
        })
    }</code></pre>
<p>Now we have </p>
<ul>
<li>a way to load data from disk asynchronously. The data is received in a completion block.</li>
<li>a way to save data by passing it to a subscriber.</li>
</ul>
<p>We continue making our lives easier by introducing a PropertyWrapper that encapsulates all of the logic above:</p>
<pre><code class="language-swift">@propertyWrapper
class Storing&lt;Value: Codable&gt;: ObservableObject {
    private var value: Value?
    private let persistanceController: PersistanceController&lt;Value&gt;
    private let filename: String
    private let defaultValue: Value

    let objectWillChange = PassthroughSubject&lt;Value, Never&gt;()
    private let writePublisher = PassthroughSubject&lt;Value, Never&gt;()

    init(filename: String, defaultValue: Value) {
        self.filename = filename
        self.defaultValue = defaultValue
        persistanceController = PersistanceController(filename: filename)
        setupPublishers()

        persistanceController.loadData(for: filename, fileExtension: "plist", defaultValue: defaultValue) { loaded in
            print("Loaded")
            // Foreward the new data into the publisher
            self.objectWillChange.send(loaded)
            self.value = loaded
        }
    }

    private func setupPublishers() {

        // Connect write publisher to persistance controller's receiving subscriber that will write the data for us
        writePublisher.debounce(for: .seconds(1.5), scheduler: RunLoop.main).receive(subscriber: persistanceController.writeSubscriber)
    }

    var wrappedValue: Value {
        get {
            value ?? defaultValue
        }
        set {
            objectWillChange.send(newValue)
            value = newValue

            writePublisher.send(newValue)
        }
    }
}
</code></pre>
<p>The property wrapper is initialized with a filename and a default value. As soon as loading the file is completed the publisher will pass the new value along to its subscribers. When the wrapped value is mutated, the <code>writePublisher</code> is asked to send the new value.<br><br />
The function <code>setupPublishers</code> connects the publisher with the <code>writeSubscriber</code> of the persistance controller. We also include a debounce so that not every mutation is written to disk immediately. Upon mutation it waits 1.5 seconds until the file system should process the new data.</p>
<p><br><br />
Now we can</p>
<ul>
<li>use the property wrapper to store and load any <code>Codable</code>.</li>
<li>use the wrapped value either directly or use the publisher to add a subscriber to it. Then we'll be notified about events such as mutations or the initial loading.</li>
</ul>
<h3>Now to the view</h3>
<p>In this simple example we just want display the content in a list. Before we get to the declaration of the view we need to instantiate our model.<br><br />
SwiftUI is still in beta so unfortunately we can not use the property wrapper directly as source for our list like this</p>
<pre><code class="language-swift">// Does not compile for unkown reason:
@Storing(filename: "TrackList", defaultValue: Model(content: [])) var model: Model</code></pre>
<p>also wrapping it inside another wrapper does not work:</p>
<pre><code class="language-swift">@ObjectBinding @Storing(filename: "TrackList", defaultValue: Model(content: [])) var model: Model</code></pre>
<p>Therefore we fall back to creating a model wrapper class <em>(it needs to be a class since @ObjectBinding demands so)</em>. That wrapper receives changes from <code>@Storing</code>'s publisher and forewards it to its own publisher:</p>
<pre><code class="language-swift">class ModelWrapper: ObservableObject {
    @Storing(filename: "Animals", defaultValue: Model(content: [])) var model: Model
    let objectWillChange = PassthroughSubject&lt;Model, Never&gt;()
        init() {
            let intermediateSubsriber = Subscribers.Sink&lt;Model, Never&gt;(receiveCompletion: { (completion) in
            }) { (value) in
                self.objectWillChange.send(value)
            }
            _model.objectWillChange.receive(subscriber: intermediateSubsriber)
        }
}</code></pre>
<p>In doing so we can finally wire up the model layer with our view:</p>
<pre><code class="language-swift">struct ContentView : View {
    @ObservedObject var modelWrapper: ModelWrapper = .init()

    var body: some View {
        NavigationView {
            List(modelWrapper.model.content) { element in
                Text("\(element.name)")
            }
            .navigationBarTitle(Text("Animals"))
                .navigationBarItems(leading: Button(action: {

                    // We can access the property directly, everything else is done for us automatically.
                    self.modelWrapper.model.content.append( Element.generateMock())

                }, label: {
                    Text("Add more")
                })
                    , trailing: Button(action: {
                        self.modelWrapper.model.content.removeAll()
                    }, label: {
                        Text("Remove All")
                    }))
        }
    }
}</code></pre>
<p>Now our list is populated by data that is loaded from disk asynchronously. When we press a button the model gets updated <strong>and</strong> the new data is written do disk automatically. When the user presses one buttons frequently it debounces the writes until there is a short brake.</p>
<figure><img src="https://tantal.uber.space/content/4-blog/56-swiftui-loading-persistant-data/animalssample.mp4" alt="Video of the sample project"></figure>
<p>The project still has a lot room for improvements but I wanted to make it simple.<br><br />
If you want to take a look at the very basic project we build, you can <a href="https://tantal.uber.space/uploads/PropertyWrapperStoringTest.zip">download it here</a>.</p>
<p>If you have any feedback or know how to avoid that extra <code>ModelWrapper</code> <a href="https://twitter.com/Klaarname">please let me know</a>! 😊</p>]]></description>
    </item>
        <item>
      <title>Tear-Down: Trade Republic Charts</title>
      <link>https://tantal.uber.space/blog/tear-down-trade-republic-charts</link>
      <guid>blog/tear-down-trade-republic-charts</guid>
      <pubDate>Sat, 18 May 2019 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Recently I stumbled upon a very nice representation of data over time: the charts from <a href="https://www.traderepublic.com">Trade Republic</a>. They really stand out by their minimalism and smooth animation. Because I liked it so much I wanted to find out how they did it and chose to replicate the interface. <br><br />
I came real close and in this post I want to share how I archieved it.</p>
<h2>Analysis 🧐</h2>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/traderepublicsmaller.mp4" alt="Video of trade republic interface with smooth animation between graphs."></figure>
<p>The chart smoothly transitions between data from one day <em>(1T)</em> to five days <em>(5T)</em> to one month <em>(1M)</em> to half a year <em>(6M)</em> to a year <em>(1J)</em> to five years <em>(5Y)</em>. Whilst doing so, a dashed baseline is updated to always mark the first entry of the selected range. When the rightmost point is below that baseline the chart is colored in red. It becomes green otherwise.<br><br />
When you follow a point on the graph you see that some points just move up or down whilst other also move horizontally.<br><br />
Also please notice the timing of the animation. It moves not linearly but has a quite extensive easing on both ends.</p>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/traderepublicfocusonmonthyear.mp4" alt="Video that focuses on the transition between one month and a year."></figure>
<p>Here, I zoomed to the transition from a month to a year. At first I assumed that this transition would move the current month to the right end of the year while scaling it down to become a twelth of the width. To my surprise I recognized that the transform moves to the opposite direction and that still confuses me. Let's see if I can fix that in my replicated version 🙃</p>
<p>My intuition says that a <code>CABAsicAnimation</code> between two <code>CGPath</code>s combined with a custom timing function might be the key to this transition.</p>
<h2>Getting mock data 💾</h2>
<p>A nice demo can be done by using random data and that is where I actually started. Soon I found out that it becomes much more appealing when real data is involved so I downloaded historically data about the <a href="https://de.finance.yahoo.com/quote/%5EGDAXI/history/">DAX from Yahoo</a>. The CSV can then be parsed like:</p>
<pre><code class="language-swift">
struct DataStore {
    private let rawData: [StockData]

    init(contentsOfCSV input: String) {
            let lines = input.components(separatedBy: .newlines)
            var resut = [StockData]()
            for line in lines {
                let components = line.components(separatedBy: ",")
                // StockData simply contains the fields defined by the CSV. We actually only need `Close`.
                if let data = StockData(from: components) {
                    resut.append(data)
                }
            }
            // Reverse the input, more on that later.
            rawData = resut.reversed()
        }
}</code></pre>
<p>After reading the historical data we need to split it in order to come up with defined ranges of time <em>(last five days, last month, last six months, last year and last five years)</em>:</p>
<pre><code class="language-swift">lazy var month: [StockData] = {
        var result = [StockData]()

        guard let firstDate = rawData.first?.date else { return result }
        guard let deadline = Calendar.current.date(byAdding: .month, value: -1, to: firstDate)?.endOfDay else { return result }
        // Get every element whose date is before or on the same date as the deadline.
        result = rawData.filter( { $0.date &gt; deadline } )
        return result
}()
// Make use of a handy extension:
extension Date {
    var startOfDay: Date {
        return Calendar.current.startOfDay(for: self)
    }
    var endOfDay: Date {
        var components = DateComponents()
        components.day = 1
        components.second = -1
        return Calendar.current.date(byAdding: components, to: startOfDay)!
    }
}</code></pre>
<h2>Drawing a chart 📈</h2>
<p>Drawing a chart is mainly calculating points in respect to the frame size of a view. Once we have scaled the points so that the minimum and maximum all fit, we can combine the points with a path and a chart will evolve. So let's start with a <code>CAShapeLayer</code> where we are going to draw the chart on:</p>
<pre><code class="language-swift">class ChartView: UIView {
private let graphLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.name = "GraphLayer"
        layer.strokeColor = UIColor.red.cgColor
        layer.fillColor = UIColor.clear.cgColor
        layer.lineWidth = 1
        layer.lineJoin = .bevel
        layer.isGeometryFlipped = true

        return layer
    }()

override func layoutSubviews() {
        super.layoutSubviews()
        graphLayer.frame = bounds
}
}</code></pre>
<p>The shape layer is added as sublayer during initialization. <code>isGeometryFlipped</code> sets the origin of the layers coordinate system in the bottom left corner – exactly where the Cartesian  Coordinate System has its origin located at.</p>
<p>Now the ViewController must chose what data to display. The view that will contain the chart, called<code>ChartView</code>, only knows how to handle points but the chart should show dates on the x-axsis. Since the axis is not labled in any way it is sufficient to convert the dates into a number, as long as their relative distance to one another is preserved. Using the <code>timeIntervalSince1970</code> makes a great candidate for that:</p>
<pre><code class="language-swift ">        let newData: [StockData] = model.month
        // Store the currently selected data so it can be used later.
        model.currentDataSet = newData
        let newPoints: [CGPoint] = newData.map { (data) -&gt; CGPoint in
            let xComponent = data.date.timeIntervalSince1970
            return CGPoint(x: xComponent, y: data.close)
        }
        chartView.transform(to: newPoints)</code></pre>
<p>Now we have our <code>ChartView</code> in place, added it to the ViewController's view and derived points to draw a line between. The actual drawing consists of two steps:</p>
<ol>
<li>Scale the points so that the minimum x-value is on the left origin, the maximum x value is on the right edge of the view and the corresponding y values are also scaled accordingly to fit the view's bounds.</li>
<li>Draw a line between the scaled points.</li>
</ol>
<p>Step one is done like:</p>
<pre><code class="language-swift">private func scale(_ points: [CGPoint], for size: CGSize) -&gt; [CGPoint] {
        let xValues = points.map( { $0.x } )
        let yValues = points.map( { $0.y } )

        let max = (x: xValues.max() ?? 0, y: yValues.max() ?? 0)
        let min = (x: xValues.min() ?? 0, y: yValues.min() ?? 0)

        let scaleFactorX: CGFloat
        if max.x - min.x == 0 {
            scaleFactorX = 0
        } else {
            scaleFactorX = size.width / (max.x - min.x)
        }

        let scaleFactorY: CGFloat
        if max.y - min.y == 0 {
            scaleFactorY = 0
        } else {
            scaleFactorY = size.height / (max.y - min.y)
        }

        let scaledPoints = points.map { point -&gt; CGPoint in
            let scaledX = scaleFactorX * (point.x - min.x)
            let scaledY = scaleFactorY * (point.y - min.y)

            return CGPoint(x: scaledX, y: scaledY)
        }
        return scaledPoints
    }</code></pre>
<p>And thus the entire drawing function emerges to:</p>
<pre><code class="language-swift">func transform(to newPoints: [CGPoint]) {
        guard newPoints.count &gt; 0 else { return }

        // Step 1: Scale according to current frame:
        let newPath = CGMutablePath()
        let scaledPoints = scale(newPoints, for: frame.size)

        // Step 2: connect scaled points
        // Notice the the first point correlates to the most recent point which is drawn on the right!
        newPath.move(to: scaledPoints.first!)
        for point in scaledPoints.dropFirst() {
            newPath.addLine(to: point)
        }

        graphLayer.path = newPath

        // Store points for later usage:
        points = newPoints
        self.scaledPoints = scaledPoints
}</code></pre>
<p>With that in place we get a nice chart that scales in respect to the minimum and maximum data points:</p>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/basicchart.mp4" alt="Video of the basic charts."></figure>
<h3>Baseline</h3>
<p>We need another layer for the baseline. In order to achieve a dashed line, we use a <code>CAShapeLayer</code> and set a path on it:</p>
<pre><code class="language-swift">private let baselineLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.backgroundColor = UIColor.clear.cgColor
        layer.name = "BaselineOfGraph"
        layer.opacity = 1
        layer.strokeColor = UIColor.lightGray.cgColor
        layer.lineWidth = 1.0
        layer.lineJoin = .round
        layer.lineDashPattern = [2, 3]
        return layer
}()</code></pre>
<p>Now the baseline is configured but a frame is not set, yet. We create a new method that is called on <code>layoutSubviews()</code> to do exactly that. Additionally, it creates the path that the <code>baselineLayer</code> is about to draw based on the current frame size and the leftmost datapoint (which defines the position of the baseline).</p>
<pre><code class="language-swift">private func updateBaselineLayer() {
       baselineLayer.removeFromSuperlayer()
        if let leftmostPoint = scaledPoints.last {
            baselineLayer.frame = CGRect(x: 0, y: leftmostPoint.y, width: bounds.width, height: 2.0)
        } else {
            baselineLayer.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 2.0)
        }
        let baselinePath = CGMutablePath()
        baselinePath.move(to: CGPoint(x: 0, y: baselineLayer.bounds.midY))
        baselinePath.addLine(to: CGPoint(x: baselineLayer.bounds.maxX, y: baselineLayer.bounds.midY))
        baselineLayer.path = baselinePath
        graphLayer.addSublayer(baselineLayer)
}</code></pre>
<h2>Animate transitions 📉🔜📈</h2>
<h3>Timing function</h3>
<p>As we can see in the videos, the animation does not use a linear timing curve. It rather makes use of heavy easing on both start and end. To come close to that we <a href="https://cubic-bezier.com/?#.17,.67,.83,.67">play around with timing curves until we find something</a> that fits.</p>
<pre><code class="language-Swift">private let timingFunction = CAMediaTimingFunction(controlPoints: 0.64, 0, 0, 1)</code></pre>
<p>Two animations need to take place now: the path animation that transforms the <strong>graph</strong> and an animation that moves the <strong>baseline</strong>. Both share the same timing function so that they perform in sync.</p>
<pre><code class="language-swift">func transform(to newPoints: [CGPoint]) {
// The same as above:
        guard newPoints.count &gt; 0 else { return }
        let newPath = CGMutablePath()
        let scaledPoints = scale(newPoints, for: frame.size)

        newPath.move(to: scaledPoints.first!)
        for point in scaledPoints.dropFirst() {
            newPath.addLine(to: point)
        }
        let newBaselinePosition = CGPoint(x: baselineLayer.position.x, y: scaledPoints.last!.y)
// New part:    
        let pathAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.path))
        pathAnimation.fromValue = graphLayer.presentation()?.path
        pathAnimation.toValue = newPath
        pathAnimation.duration = 1.2
        pathAnimation.timingFunction = timingFunction

        let baselineAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.position))
        baselineAnimation.fromValue = baselineLayer.presentation()?.position
        baselineAnimation.toValue = newBaselinePosition
        baselineAnimation.timingFunction = timingFunction
        baselineAnimation.duration = 1.2

        graphLayer.add(pathAnimation, forKey: "PathAnimation")
        baselineLayer.add(baselineAnimation, forKey: "BaselineAnimation")
        // Set the new properties without implicit animations:
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        baselineLayer.position = newBaselinePosition
        graphLayer.path = newPath
        CATransaction.commit()
        // Bookkeeping:
        points = newPoints
        self.scaledPoints = scaledPoints
}</code></pre>
<p>On both layers we use the presentation layer in order to accomodate for a user who changes mind while an animation is ongoing. After both animations are added to their layers, the final values need to be set. We do so by disabling CoreAnimation's implicit animations since they would interfere with the desired ones.</p>
<p>The right feeling of this animation depends on two things:</p>
<ol>
<li>A smaller segment must transition to a larger segment when the displayed interval is increased.</li>
<li>The larger segment must zoom in when the time interval is reduced.</li>
</ol>
<p>This is where the reversed elements from above come into play! We draw the points <strong>from right to left</strong>. When the number of points increases the new points are added on the <strong>left</strong> <em>(e.g. the user selected a larger interval like from one month to a year)</em>. The points that are alredy contained will morph to their new positions <em>(on the right side)</em> whilst new points come in <strong>from the left</strong>. At any time the user wants to see the most recent data which happened to be positioned on the <strong>right</strong> <em>(e.g. the last five days, the last month, the last year)</em>.<br><br />
When the graph is about to contain less elements than it did before, elements on the <strong>left</strong> are removed which results in a zoom-effect.</p>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/myversionanimation.mp4" alt="Video of my version of animated charts."></figure>
<h2>Making it interactive 👉👆</h2>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/traderepublicinteraction.mp4" alt="Video of the trade republic app whilst the user interacts with it."></figure>
<p>In this video you can see how the user is able to interact with the chart view. When the thumb contacts the screen a little highlight indicator appears. It can be dragged around and snapps to the next available data point. That snapping is supported by the TapticEngine which lets the user feel haptic feedback.</p>
<p>This interaction feature basically consists of three parts:</p>
<ol>
<li>Showing a selection indicator when user begins the gesture and remove it when the it ends.</li>
<li>Moving the indicator corresponding to the thumb's movement, snapping to the nearest data point.</li>
<li>Giving haptic feedback at every snap and update the UI with the price at the given point.</li>
</ol>
<p>In order to implement this we introduce a new sublayer: </p>
<pre><code class="language-swift">private var touchindicatorLayer: CALayer = {
        let layer = CALayer()
        layer.backgroundColor = UIColor.lightGray.cgColor
        layer.opacity = 0
        return layer
}()</code></pre>
<p>To play along with the users gesture we can override the following methods to get a good starting point:</p>
<pre><code class="language-swift">func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {...}
func touchesMoved(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {...}
func touchesEnded(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {...}</code></pre>
<p>In <code>touchesBegan</code> we first get the location of the touch and then use a helper function that calculates the point next to that location. </p>
<pre><code class="language-swift">override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        guard !points.isEmpty, !scaledPoints.isEmpty else { return }
        guard let location = touches.first?.location(in: self) else { return }

        // Store where the finger hit the screen to calculate relative movement
        lastTouchLocation = location

        guard let selected = pointNextTo(touch: location) else { return }
        self.selectedElement = selected

        showSelectedPointHandle()
        moveSelectedPointHandle()
        // The delegate updates the UI.
        delegate?.highlightDidChanged(to: selected.indexInData)
}</code></pre>
<p>Other helper methods are responsible for showing and moving the selection indicator. Showing and hiding make also use of implicit animations. Moving should not be animated so they are turned off:</p>
<pre><code class="language-swift">private func showSelectedPointHandle() {
        touchindicatorLayer.opacity = 1
}

private func moveSelectedPointHandle() {
        guard let selectedPoint = selectedElement else { return }
        // Disable implicit animations because moving should exactly follow the users thumb.
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        touchindicatorLayer.position.x = selectedPoint.point.x
        CATransaction.commit()
}

private func hideSelectedPointHandle() {
       touchindicatorLayer.opacity = 0
}</code></pre>
<p><br><br />
The biggest helper function is the one that calculates the <strong>nearest point to a given location</strong>.  It returns not only the nearest point but also its index. A simple iteration over the contained points is enough for that:</p>
<pre><code class="language-swift">private func pointNextTo(touch: CGPoint) -&gt; (point: CGPoint, indexInData: Int)? {
        guard !points.isEmpty, !scaledPoints.isEmpty else { return nil }
        var pointNextToTouchLocation: CGPoint = .zero
        var bestDistancePointToLocationOfTouch: CGFloat = .greatestFiniteMagnitude
        var indexOfFoundPoint: Int = 0

        for (index, displayedPoint) in scaledPoints.enumerated() {
            let distance = abs(displayedPoint.x - touch.x)
            if distance &lt; bestDistancePointToLocationOfTouch {
                bestDistancePointToLocationOfTouch = distance
                pointNextToTouchLocation = displayedPoint
                indexOfFoundPoint = index
            }
        }
        return (point: pointNextToTouchLocation, indexInData: indexOfFoundPoint)
}</code></pre>
<p>Even though this function is simple, it leaves out the fact that our array of scaled points is already sorted. Finding a minimum is way faster in this cases. Therefore we leverage the fact that once a minimum  is found <em>(a.k.a. local minimum)</em> and the next point is further apart than the <strong>local</strong> minimum, we have found the <strong>global</strong> minimum. The improved version looks like this:</p>
<pre><code class="language-swift">private func pointNextTo(touch: CGPoint) -&gt; (point: CGPoint, indexInData: Int)? {
        guard !points.isEmpty, !scaledPoints.isEmpty else { return nil }
        var pointNextToTouchLocation: CGPoint = .zero
        var bestDistancePointToLocationOfTouch: CGFloat?
        var indexOfFoundPoint: Int = 0

        for (index, displayedPoint) in scaledPoints.enumerated() {
            let distance = abs(displayedPoint.x - touch.x)

            if bestDistancePointToLocationOfTouch != nil &amp;&amp; distance &gt; bestDistancePointToLocationOfTouch! {
                // scaledPoints is sorted regarding their x value. So a local minimum is also a global minimum.
                // We can break the loop when a minimum was found (bestDistancePointToLocationOfTouch != nil) and the next distance is greater than this.
                break
            }
            if distance &lt; bestDistancePointToLocationOfTouch ?? .greatestFiniteMagnitude {
                bestDistancePointToLocationOfTouch = distance
                pointNextToTouchLocation = displayedPoint
                indexOfFoundPoint = index
            }
        }

        return (point: pointNextToTouchLocation, indexInData: indexOfFoundPoint)
}</code></pre>
<p><br><br />
At this point we show the highlight indicator and have helper methods in place to move and hide it. Let's put them into action:</p>
<pre><code class="language-swift">override func touchesMoved(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let location = touches.first?.location(in: self) else { return }
        guard let selected = pointNextTo(touch: location) else { return }

        // Move the handle, store new data and call delegate functions if changed.
        if selectedElement?.indexInData != selected.indexInData {
            selectedElement = selected

            moveSelectedPointHandle()
            delegate?.highlightDidChanged(to: selected.indexInData)
        }
}

override func touchesEnded(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)

        selectedElement = nil
        hideSelectedPointHandle()
        delegate?.hightlightDidEnd()
}</code></pre>
<p>Another possible optimization is to only calculate the nearest point once the touch gesture has moved a significant amount of points. You'll find an implementation for that in the corresponding GitHub project!</p>
<p>We just saw that the <code>ChartView</code> calls its delegate to inform it about highlighted points. The <code>ViewController</code> sits on the receiving side of the calls. It updates the label above the chart view to match the selected element. In this prototype, the <code>ChartView</code> and the <code>ViewController</code> communicate by passing the index of the highlighted element. That is not the most elegant solution and might also be improved.<br><br />
Here you can see the implementation of said delegate methods:</p>
<pre><code class="language-swift">extension ViewController: ChartViewDelegate {
    func highlightDidChanged(to elementAtIndex: Int) {
        guard elementAtIndex &lt; model.currentDataSet.count else { return }
        let selectedElement = model.currentDataSet[elementAtIndex]
        let leftElement = model.currentDataSet.last!

        // Color is chosen relative to the first element which sets the baseline.
        let colorOfPriceLabel: UIColor = leftElement.close &gt; selectedElement.close ? colorMinus : colorPlus

        updatePriceLabel(to: String.init(format: "%.2f", selectedElement.close), in: colorOfPriceLabel)

        // Only give feedback when not too many points are displayed. That is the case in the 1Y and 5Y state.
        if segmentedControl.selectedSegmentIndex != 4 &amp;&amp; segmentedControl.selectedSegmentIndex != 5 {
            feedbackGenerator.selectionChanged()
        }
    }
    private func updatePriceLabel(to newText: String, in color: UIColor) {
        UIView.transition(with: priceLabel, duration: 0.2, options: [.beginFromCurrentState, .transitionCrossDissolve], animations: {
            self.priceLabel.text = newText
            self.priceLabel.textColor = color
        }, completion: nil)

    }
}</code></pre>
<p><strong>Aaaaaand with that we have finished our re-creation of that nice chart view!</strong> 🥳</p>
<figure><img src="https://tantal.uber.space/content/4-blog/55-tear-down-trade-republic-charts/myversioncomplete.mp4" alt="My re-created version of the chart containing user interaction and animations."></figure>
<h3>The main takeaways are:</h3>
<ul>
<li>A chart is nothing more than scaled points connected by a line.</li>
<li>Using a <code>CABasicAnimation</code> to morph a <code>CGPath</code> is <strong>very</strong> smooth. By doing so almost the entire animation comes for free.</li>
<li>The animation benefits <strong>a lot</strong>  from start drawing the chart from the right combined with proportional numbers of points.</li>
<li>Interaction can be broken down into three steps. The most complicated part is to get the point next to the users finger and even that is straight foreward.</li>
<li>Accessibility is nothing that takes care of itself! This entire demo is missing accessibility adjustments in order to focus on the main visuals.</li>
<li><code>UIKit</code> and <code>CoreAnimation</code> are just awsome frameworks 💯</li>
</ul>
<p><br><br />
The entire project is available on my site at <a href="https://github.com/Tantalum73/ChartsTeardown">GitHub</a>!<br />
As always I would be happy to <a href="https://twitter.com/Klaarname">hear what you think</a> 🙃</p>]]></description>
    </item>
        <item>
      <title>On the Limitations of CharacterSet</title>
      <link>https://tantal.uber.space/blog/on-the-limitations-of-characterset</link>
      <guid>blog/on-the-limitations-of-characterset</guid>
      <pubDate>Mon, 22 Apr 2019 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Recently, I wanted to build a sanitizer for a text input. The text input had rules that excluded come characters. When an invalid character is typed the user should be notified why it does not appear on screen. However, pasting a text from clipboard is also an option so the sanitizer needs to handle multiple invalid characters at once, too.</p>
<h3>“Great, there is the <code>CaracterSet</code> API, let’s do this!”</h3>
<p>By stating the problem we start by defining a function that has a string as input and needs to produce a sanitized string and the invalid characters that were removed:</p>
<p><code>func sanitize(input: String) -&gt; (sanitized: String, removed: String?)</code></p>
<p>Additionally, the set of invalid characters needs to be defined:</p>
<p><code>let forbiddenCharacters = CharacterSet(charactersIn: "&lt;&gt;.;*")</code></p>
<p>The input will not be a gigantic string but we want to keep an eye on performance as the function needs to be called on every character the user types. The best we can hope for is a time complexity of <code>O(n)</code>, <code>n</code> being the length of the input string.</p>
<p>Let’s start with the most simple approach: filter through the characters of the input string and figure out which characters not belong in there. Those parts need to be memorized in order to tell the user later why the input differs from their expectation. The individual characters need to be concatenated into a single string, which is done in line 9.</p>
<pre><code class="language-swift">func sanitize1(input: String) -&gt; (removed: String?, sanitized: String) {
    var removed: String?
    let sanitized = input.filter { (character) -&gt; Bool in //O(n)
        if forbiddenCharacterSet.contains(character) { //O(1) (Set containment is constant)

            // Store the removed character
            let forbiddenCharacterAsString = String(character)
            removed = (removed ?? "") // Inizializing it only when needed
            removed?.append(forbiddenCharacterAsString)
            return false
        }
        return true
    }
    return (removed, sanitized)
    // O(n)
}</code></pre>
<p>The time complexity is <code>O(n)</code> since it loops through all the characters and checks if thy are contained in the set. The set containment operation is constant <code>O(1)</code> which gives us the desired complexity of <code>O(n)</code>. <em>Yay! <strong>🎉</strong></em></p>
<p>We even can do better in some scenarios by using set operations! Create a set form the input string and calculate the intersection with the set of forbidden characters. It would be neat if it was possible to get characters <em>out</em> of a character set. </p>
<blockquote>
<p>Unfortunately, it is a one way street so there is no way to figure out what characters are contained in the intersection. Another filtering is needed. That might break our performance win as we see.</p>
</blockquote>
<pre><code class="language-swift">func sanitize2(input: String) -&gt; (removed: String?, sanitized: String) {
    var removed: String?
    var sanitized = input

    let inputSet = CharacterSet(charactersIn: input) // O(n)
    let intersection = inputSet.intersection(forbiddenCharacterSet) // O(min(n,k))
    if !intersection.isEmpty {
        // Input contains invalid characters, only perform the filter now
        sanitized = input.filter { (character) -&gt; Bool in //O(n)
            if intersection.contains(character) { //O(1) (Set containment is constant)
                ...
            }
            return true
        }
    }
    return (removed, sanitized)
    // Filter only performed when needed, but checking for containment is not possible.
    // ALSO: Getting characters OUT of a CharacterSet is not possible
    // O(n) + O(min(n,k)), when it contains invalid characters, O(min(n,k)) if not
}</code></pre>
<p>Time complexity wise, creating a <code>CharacterSet</code> from a string takes approximately <code>O(n)</code> and may be even cheaper. Calculating the intersection of a set is <code>O(min(k, n))</code> <em>(k being the amount of forbidden characters)</em>. In the most cases <code>k</code> is smaller than <code>n</code>. </p>
<p>A CharacterSet is not a normal set so it is unfortunately not possible to create an array from it.<br />
The only way to figure out what elements causes the intersection to be not empty is to filter again. Compared to function #1 it is only done when we have a reason to do so.</p>
<p><strong>Except</strong>: </p>
<p><code>Cannot convert value of type 'String.Element' (aka 'Character') to expected argument type ’Unicode.Scalar'.</code> As it turns out, a <code>CharacterSet</code> is not simply a set of characters <code>Set&lt;Character&gt;</code>! 😖</p>
<blockquote>
<p>It is not possible to <strong><em>check</em></strong> if a given character belongs to the <code>CharacterSet</code>! It is also not possible to <strong><em>retrieve</em></strong> any character out of a <code>CharacterSet</code> or even to convert it back to a string!</p>
</blockquote>
<h3>Backup strategy:</h3>
<p>If <code>CharacterSet</code> does not provide the right APIs for us, we could drop it entirely. We store the forbidden characters in a string <code>let forbiddenCharacters = "&lt;&gt;.;*"</code>. Following approach #1 we use the filter function to check if any characters need to be removed form the input. Instead of having the nice constant <code>O(1)</code> lookup operation of a set we need to drop down to a string search operation. That takes <code>O(k)</code> time.</p>
<pre><code class="language-swift">let forbiddenCharacters = "&lt;&gt;.;*"
func sanitize3(input: String) -&gt; (removed: String?, sanitized: String) {
    var removed: String?
    // Using string containment now.
    let sanitized = input.filter { (character) -&gt; Bool in //O(n)

        if forbiddenString.contains(character) { // O(k), not a set operation
            // Store the removed character
            let forbiddenCharacterAsString = String(character)
            removed = (removed ?? "") // Inizializing it only when needed
            removed?.append(forbiddenCharacterAsString)
            return false
        }
        return true
    }

    return (removed, sanitized)
    // O(k*n) in total
}</code></pre>
<p>After filtering we still need to combine the removed characters to a string. Thus, the third approach is <code>O(kn)</code> since for every character in the filter operation the forbidden characters need to be scanned.</p>
<p>But wait, there’s more. We can improve our small algorithm! The String-API gives us one anchor to use our not really beloved <code>CharacterSet</code>: <code>string.rangeOfCharacter(from: CharacterSet)</code>. If any character of the <code>CharacterSet</code> is found in the string, its range is returned. However, it only finds the <strong>first</strong> match. So we need to build a loop to find <strong>every</strong> occurrence:</p>
<pre><code class="language-swift">func sanitize4(input: String) -&gt; (removed: String?, sanitized: String) {
    var removed: String?
    var sanitized = input

    // Documentation: "This method does not perform any Unicode normalization."
    while let range = sanitized.rangeOfCharacter(from: forbiddenCharacterSet) { // O(n)*k
        let wrongPart = String(sanitized[range])
        removed = (removed ?? "") // Inizializing it only when needed
        removed?.append(wrongPart)
        sanitized.removeSubrange(range)
    }

    return (removed, sanitized)
    // O(k*n) in total but saves a little because .rangeOfCharacter() will find elements more quickly.
}</code></pre>
<p>Apple sadly does not provide any information about the time complexity of this method so we assume that it is <code>O(n)</code>. Doing it for every invalid character brings us to <code>O(nk)</code> in total.</p>
<p>That is quite close to our desired <code>O(n)</code>. However, it does not produce correct results when unicode is involved. If we waive unicode, we can also modify function #1:</p>
<pre><code class="language-swift">func sanitize1_1(input: String) -&gt; (removed: String?, sanitized: String) {
    var removed: String?
    let sanitized = input.filter { (character) -&gt; Bool in //O(n)

        for scalar in character.unicodeScalars { //O(|unicode scalars in character|)
            if forbiddenCharacterSet.contains(scalar) { //O(1) (Set containment is constant)
                // Store the removed character
                let forbiddenCharacterAsString = String(character)
                removed = (removed ?? "") // Inizializing it only when needed
                removed?.append(forbiddenCharacterAsString)
                return false
            }
        }
        return true
    }
    return (removed, sanitized)
    // O(n) * O(|unicode scalars in character|)
}</code></pre>
<p>In the filter function every character’s <code>unicodeScalar</code> is examined and checked if the <code>CharacterSet</code> contains it. The rest is the same logic as before. Time complexity wise an additional loop with <code>O(|unicode scalars of character|)</code> is introduced. Thus giving us the combined complexity of <code>O(n * |unicode scalars of character|)</code>​.</p>
<h3>Wait a minute.</h3>
<blockquote>
<p>We can not check if a <strong>CharacterSet</strong> contains a <strong>Character</strong> but it’s possible to test if it contains a <strong>UnicodeScalar</strong>! Why isn’t it called “UnicodeScalarSet” then? </p>
</blockquote>
<p>🧐</p>
<h2>Conclusion</h2>
<p>A <code>CharacterSet</code> is really not a set of <code>Characters</code>. We can not check if a string’s character is part of it. It is more a “UnicodeScalarSet”. Since a character can consist of multiple unicode scalars, it is not possible to build a unicode aware API around <code>CharacterSet</code>. It is also impossible to get elements out of a <code>CharacterSet</code>.</p>
<p>If we need unicode support, we'll go with function #3. <code>O(k*n)</code> is quite ok if the invalid characters aren’t too many. Otherwise, function #4 or #1_1 are the best options. With that we come pretty close to <code>O(n)</code>.</p>
<blockquote>
<p>It wonder why the <code>CharacterSet</code> API is so limited. It even misses the point of its name. Working around its limitations is possible but may be confusing in the first time. </p>
</blockquote>
<p>If you want to, you can download the playground <a href="https://anerma.de/uploads/CharacterSet.playground.zip">here</a>. I would love to <a href="https://twitter.com/Klaarname">hear from you</a>, as well!</p>
<h3>Additional information</h3>
<p>Using <a href="https://github.com/attaswift/Attabench">Attabench</a> for performance investigations shows that all of the introduced ways are nearly constant and there is no clear winner. However if you need unicode support, function #3 may be the best fit.</p>
<figure><img src="https://tantal.uber.space/content/4-blog/54-on-the-limitations-of-characterset/sanitizer-3-tasks.png" alt="Performance investigation results showing that every algorithm has On time complexity."></figure>]]></description>
    </item>
        <item>
      <title>Overlooked API of the Day: NSCache</title>
      <link>https://tantal.uber.space/blog/overlooked-api-of-the-day-nscache</link>
      <guid>blog/overlooked-api-of-the-day-nscache</guid>
      <pubDate>Tue, 11 Dec 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>I am almost sure that you have stumbled upon the method <code>didReceiveMemoryWarning</code>. It gets added to every <code>UIViewController</code> that you create usind a template from Xcode.</p>
<figure><img src="https://tantal.uber.space/content/4-blog/53-overlooked-api-of-the-day-nscache/code1.png" alt="Image of the default code in didReceiveMemoryWarning"></figure>
<p>Depending on the memory consumption of your ViewController, it's the systems way of telling you <em>&quot;Hey, please use less memory or I am forced to kill your process&quot;</em>. This might be a good time to drop references to cached images that can be re-loaded from disk or via network.<br><br />
Your app also gets a <a href="https://developer.apple.com/documentation/uikit/uiapplicationdidreceivememorywarningnotification?language=objc">notification</a> called <code>UIApplicationDidReceiveMemoryWarningNotification</code> when the device will run our of memory. Every object can listen for this notification and act accordingly.</p>
<p>If you do not want do deal with this kind of situation by yourself <code>NSCache</code> assist you. <code>NSCache</code> works like a regular dictionary but will drop elements once the memory pressure gets too high.<br><br />
You can, for example, put downloaded images into a <code>NSCache</code>. They may get purged during usage of your app. In this case you need to re-download them, which is not great but certainly better than crashing.</p>
<p>You can find the documentation of <code>NSCache</code> <a href="https://developer.apple.com/documentation/foundation/nscache">over here</a>. And if you need an image cache that writes the cache to disk and manages all of the downloading and storing things for you, please take a look at <a href="https://github.com/onevcat/Kingfisher">Kingfisher</a></p>]]></description>
    </item>
        <item>
      <title>Overlooked API of the Day: NSOrderedSet</title>
      <link>https://tantal.uber.space/blog/overlooked-api-of-the-day-nsorderedset</link>
      <guid>blog/overlooked-api-of-the-day-nsorderedset</guid>
      <pubDate>Tue, 11 Dec 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>As you know, an <code>Array</code> stores elements while preserving the order of their addition. You also may have learned that <code>Sets</code> are perfect for storing a collection of data where every element is unique. Because of its implementation it is also easy to perform boolean operations on two <code>Sets</code> <em>(like computing the intersection or difference of two Sets)</em>. However, Sets will not preserve the order of their elements. What is more, they gain their efficiency only from <strong>not remembering</strong> information about the order.</p>
<p>This is where <code>NSOrderedSet</code>  comes into play. According to <a href="https://developer.apple.com/documentation/foundation/nsorderedset">Apples documentation</a>, it acts as a Set <em>(containing an element only once)</em> whilst preserving the order of their additions.<br><br />
Its counterpart <code>NSMutableOrderedSet</code> is the mutable addition to the datatype.</p>]]></description>
    </item>
        <item>
      <title>Overlooked API of the Day: NSCountedSet</title>
      <link>https://tantal.uber.space/blog/overlooked-api-of-the-day</link>
      <guid>blog/overlooked-api-of-the-day</guid>
      <pubDate>Mon, 10 Dec 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Have you ever encountered the issue where you had a sequence of things, you needed the elements only once and must know what elements are duplicates? And how often they occure?<br><br />
Take a look as <code>NSCountedSet</code>! For example when combining multiple collections (<code>Set</code>s), you can determine which elements are contained multiple times. That technique can be used to calculate the intersection of collections in a straight foreward way 🙃</p>
<p>Apples documentation can be found <a href="https://developer.apple.com/documentation/foundation/nscountedset">here</a>.</p>]]></description>
    </item>
        <item>
      <title>Advanced Debugging with Xcode and LLDB &#8211; Sharing my Notes</title>
      <link>https://tantal.uber.space/blog/advanced-debugging-with-xcode-and-lldb-sharing-my-notes</link>
      <guid>blog/advanced-debugging-with-xcode-and-lldb-sharing-my-notes</guid>
      <pubDate>Sat, 30 Jun 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>I watched the  WWDC talk and I would like to share my notes with you. You can find the recording as well as the mentioned script over  <a href="https://developer.apple.com/videos/play/wwdc2018/412">here</a>.</p>
<ul>
<li>Xcode debug tab can be opened when a breaktpoint is hit via settings > Running > Pauses > check “Show tab named: Debug”</li>
<li><code>po &lt;variable&gt;</code> to get the value of a variable</li>
<li><code>expression &lt;expression&gt;</code> (eg. <code>expression didReachSelectedHeight = true</code>) to execute code at the breaktpoint, useful to always take one branch of an if-else-statement</li>
<li>Breakpoints can execute a LLDB command and then continue running. When double-clicked, one can enter the command via “Add Action”. Enabling “Automatically continue after Evaluation” tells the debugger to execute the command and continue to run the program. Combined with the aforementioned point it is possible to (always) take one branch of an if-else-statement whilst debugging.</li>
<li>Shortcut the &quot;change>compile>run&quot; workflow by injecting the change directly through LLDB to your running code. Just add another breakpoint and add the code you just inserted to your source file here, too. <code>expression &lt;code you just updated&gt;</code> does the trick that helps to tighten the workflow.</li>
<li>Debug navigator > add symbol > &quot;add symbolic breakpoint&quot; to add breakpoints to framework's code. For example <code>[UILabel setText:]</code> to add a breakpoint that hits every time a labels text is changed. Resolving the methods name succeeded when a text like “in UIKitCore” appears beside your new breakpoint. When this breakpoint hits, one can
<ul>
<li><code>po $arg1</code> to print the receiver of the Objective-C message (the <code>UILabel</code> in this case).</li>
<li><code>po (SEL)$arg2</code> to print the selector that is sent to the object.</li>
<li><code>po $arg3</code> to print the first argument passed into the method.</li>
</ul></li>
<li>In a breakpoint, another breakpoint can be set dynamically with <code>breakpoint set --one-shot true -name [UILabel setText:]</code> (one shot breakpoint: only exists until it is triggered and then removed). Useful to “enable” a breakpoint only after a given part of code is executed, for example when the text of a label is updated in the following code.</li>
<li>Change the instruction pointer while the execution is paused by grabbing the grab-handle left besides “Thread 1: breakpoint…” in the green bar.</li>
<li><code>thread jump --by 1</code> tells the debugger to jump one code line ahead when the breakpoint is hit. Useful to skip code or execute different code by adding another action to the breakpoint.</li>
<li>Adding a breakpoint to a variable declaration will pause execution every time the variable is accessed.</li>
<li><code>po &lt;variable&gt;</code> uses the debugDescription method of an object, <code>p &lt;variable&gt;</code> is more verbose in some cases.</li>
<li>Watchpoints pause the execution every time the value of a variable is changed. They can be added by right-clicking on the variable in the left side of the debugger pane. They appear below regular breakpoints in Xcode's navigator.</li>
<li><code>expression -l objc -O -- [’self.view' recursiveDescription]</code> will print the description of the view as well as all contained subviews. It is an internal Objective-C method and can not be called using Swift. “’“ around <code>self.view</code> will tell the debugger to evaluate this expression first.</li>
<li>Printing the content of memory addresses like <code>po 0x67aef7891</code> will not work using swift. One must use <code>expression -l objc -O -- po 0x67aef7891</code>​. A handy way to create a shortcut is by typing <code>command alias poc expression -l objc -O --</code>. Now it is possible to type <code>poc 0x67aef7891</code>.</li>
<li>In Swift, you can also use <code>po unsafeBitcast(0x2d34bd25, to: YourView.self)</code> instead of using Objective-C evaluation. You can even chain expressions together, like <code>po unsafeBitcast(0x2d34bd25, to: YourView.self).center.y = 300</code>. When a <code>UIView</code> property is changed in this way, one need to tell CoreAnimation to update its frame buffer, as the execution is currently paused: <code>expression CATransaction.flush()</code></li>
<li>A python script that changes properties of the view using the debugger is presented. To use commands provided by this script, open <code>~/.lldbinit</code> in you home directory and add <code>command script import ~/nudge.py</code>. You can also define your previously created aliases there.</li>
<li>Using the nudge tool, one can live update positions of views by using the debugger.</li>
</ul>]]></description>
    </item>
        <item>
      <title>Open Source: FeedbackController</title>
      <link>https://tantal.uber.space/blog/open-source-feedbackcontroller</link>
      <guid>blog/open-source-feedbackcontroller</guid>
      <pubDate>Fri, 18 May 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>The Taptic Engine was introduced in Apples iPhone 7. It not only made possible to replace the mechanical home button but it also introduced API for developers. Through this API it became possible to give the user a subtle mechanical feedback about what is happening on screen.</p>
<p>For example an app can combine user interactions (like tapping a button, fading a slider oder flipping a view) with a light shake of the phone.<br><br />
Even more sophisticated feedback types are possible. A notification can be combined with the according haptic feedback and then the user can literally feel if a task was successful or not.</p>
<p>My new open source project makes the usage of the mentioned API easier. You can find the project over <a href="https://github.com/Tantalum73/FeedbackController">here at GitHub</a>. As always, I would love to hear feedback from you 🤗.<br><br />
This blogpost describes the usage of the haptic feedback API and explains the approach of my new project. If it makes live easier for you, feel free to use it. Otherwise it could provide a reference implementation for you.</p>
<h3>Implementation</h3>
<p>To integrate the haptic feedback, one must use <code>UIFeedbackGenerator</code>. This superclass is derived by three different subclasses: <code>UIImpactFeedbackGenerator</code>, <code>UINotificationFeedbackGenerator</code> and <code>UISelectionFeedbackGenerator</code>.<br><br />
These three subclasses can be used for three different types of feedback:</p>
<ol>
<li>A single tap-like feedback provided by <code>UIImpactFeedbackGenerator</code></li>
<li>A more complex feedback to indicate that a task has completed (successfully / erroneous) or that a warning occurred by using <code>UINotificationFeedbackGenerator</code></li>
<li>A single tap that implies a change in the users selection, for example when a switch is used or an image snaps into place, provided by <code>UISelectionFeedbackGenerator</code></li>
</ol>
<p>In any case, the Taptic Engine must be transitioned into its active state, by calling <code>prepare()</code> on any of the mentioned subclasses. To align the UI interaction or even sounds with the haptic feedback, <code>prepare</code> should be called about one or two seconds in advance.</p>
<h3>What FeedbackController does</h3>
<p>As it turns out, the API is not as straight forward as it could be. For example, the type of the notification-feedback is set when the feedback should fire.<br><br />
Then again, the type of an impact feedback must be set during the <code>prepare</code> state.<br><br />
In addition to that, the API is only available on iPhone 7 or newer, running iOS 11 or higher. Prior to using the described APIs, one must perform availability checks. As a reference to the <code>UIFeedbackGenerator</code> must be held strongly and calls to <code>prepare</code> and <code>perform</code> can occur in many places of your app, those checks need to be implemented quite frequently.</p>
<p>That is where FeedbackController comes into play. It simplifies the calls, needed to perform haptic feedback. It eliminates the need for the developer to keep in mind where the types of feedback can be configured.<br><br />
In addition to that, it comes with easy to use extensions of <code>UIViewController</code>. They make it possible to use feedback everywhere in the app by two simple method calls:</p>
<figure><img src="https://tantal.uber.space/content/4-blog/49-open-source-feedbackcontroller/1.png" alt="image showing the usage of FeedbackController"></figure>
<p>However, a call to <code>prepareFeedback(for:)</code> should be made as early as possible so that the Taptic Engine can be powered on. Please see the example project for further details.</p>
<h3>How to use it</h3>
<p>If you integrate FeedbackController using CocoaPods, you need to <code>import FeedbackController</code> first.</p>
<p>When you expect that a user interaction will take place in the next couple of seconds, call <code>prepareFeedback(for:)</code>. In doing so, the Taptic Engine will be powered on and is ready for your feedback. <em>The timing is not as critical as might have guessed and it turned out that preparing the Taptic Engine in <code>viewDidAppear(_:)</code> is sufficient.</em> <br><br />
However, the type of feedback is determined by the call to <code>prepareFeedback(for:)</code>.</p>
<p>Secondly, a feedback needs to be performed. To do so, just call </p>
<ol>
<li><code>hapticFeedbackImpactOccured()</code> for a impact feedback.</li>
<li><code>hapticFeedbackNotificationOccured(with:)</code> for a notification feedback, specifying the type of said notification.</li>
<li><code>hapticFeedbackSelectionChanged()</code> for a selection feedback.</li>
</ol>
<p>When you are done with the feedback, call <code>doneWithHapticFeedback()</code> to allow the Taptic Engine going back to its idle state.</p>
<h3>Installation</h3>
<p>You can install it by using CocoaPods or just by copying the <code>FeedbackController.swift</code> file.<br />
Again, here is the  <a href="https://github.com/Tantalum73/FeedbackController">link to the project</a>.</p>
<p>Please <a href="https://twitter.com/klaarname">let me know what you think about it</a>.</p>]]></description>
    </item>
        <item>
      <title>Memory Management of Value Types</title>
      <link>https://tantal.uber.space/blog/memory-management-of-value-types</link>
      <guid>blog/memory-management-of-value-types</guid>
      <pubDate>Thu, 03 May 2018 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Yesterday, I read a <a href="https://swiftrocks.com/memory-management-and-performance-of-value-types.html">great article</a> about value and reference types and how their performance behaves. I want to share my notes with you.</p>
<p><br><br></p>
<p><strong>Stack Allocation</strong></p>
<ul>
<li>Fast and simple place to store data</li>
<li>Uses a stack pointer that points to the top of the stack</li>
<li>Each scope provides the amount of memory it needs to run before it gets executed, then the stack pointer is moved and the scope is executed. Now, the scope can add and remove data to the memory it got allocated.</li>
<li>When the scope is done, the stack pointer is decreased and its data deallocated</li>
<li>Very little costs as only an integer (the pointer) needs to be increased/decreased</li>
<li>Some forms of value types can be stored directly on the stack, as long as their sizes are known at compile time</li>
<li>They also must not be contained by a reference type or contain one, as it would require reference counting</li>
<li>Absence of reference counting overhead leads to an increased performance</li>
<li>When a value type is assigned, it gets copied (called Copy-On-Assignment). However, the copy is performed in a constant time.
<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/1.png" alt=""></figure></li>
</ul>
<p><strong>Heap Allocations</strong></p>
<ul>
<li>Used for objects that are flexible in size and whose lifetime can be longer than the scopes</li>
<li>Pointers point to the object in memory</li>
<li>When a resource should be allocated, the memory management is asked to reserve a certain amount of memory for that resource</li>
<li>If the resource is not used anymore, its memory can be freed (managed for us by ARC)</li>
<li>Heap allocation is slower than stack allocation because the data structure is more complex (page tables, …) and it needs to be thread save (in comparison: every thread has its own stack)</li>
</ul>
<p>Unfortunately, it is not as easy as ’value types go on the stack and reference typed to the heap’.</p>
<p><strong>Value Types on the Heap</strong></p>
<ul>
<li>Every struct whose size is not known at compile time must be stored on the heap.</li>
<li>Heap allocation is necessary when the struct contains a reference type or is contained by one (or even recursively, meaning it stores a value type object that stores a reference type)</li>
<li>Common situation when for example storing <code>@escaping</code> closures or if the struct is stored by a <code>class</code>​ directly</li>
<li>Possible for the <code>struct</code> to perform worse than a <code>class</code>​ when it comes to copying</li>
<li>Value types, that store heap allocated <code>class</code> objects, will not be per-se stored on the heap but will gain a reference counting overhead</li>
<li>Stack allocated value types are copied in constant time, heap allocated value types are not and will take more time.</li>
<li>Copy-On-Write (COW) prevents it from being problematic and many Standard Libraries data structures make heavy use of it (e.g. <code>Array</code>, <code>Dictionary</code> and <code>Set</code>)</li>
<li>With COW, an assigned property will not be copied immediately but only if its content get mutated
<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/2.png" alt=""></figure></li>
</ul>
<h1>Problems with Value Types that store Reference Types</h1>
<ul>
<li>Consider the following <code>struct</code> and <code>class</code>​ that contain many reference type objects:
<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/3.png" alt=""></figure></li>
</ul>
<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/4.png" alt=""></figure>
<ul>
<li>The next snippet will create <code>HugeClass</code> and <code>HugeStruct</code> many times and check how long the creation takes</li>
<li>Result: the <code>struct</code> needs more time then <code>class</code>, because it uses copy-on-assignment, in comparison to the <code>class</code> which just increases a reference count</li>
<li>If executed not only 10_000_000 but 20_000_000 times, the struct needs more than <strong>twice the time</strong> than before, the time of class only merely increases</li>
<li>The more reference types a value type contains (more instances of <code>EmptyClass</code> in <code>HugeStruct</code>), the more reference counting overhead is added on copying it.</li>
</ul>
<h1>Takeaway</h1>
<ul>
<li>Improve the performance by replacing unnecessary references with proper static size value types</li>
<li>For example, this:<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/5.png" alt=""></figure></li>
<li>May be replaced by this:<figure><img src="https://tantal.uber.space/content/4-blog/48-memory-management-of-value-types/6.png" alt=""></figure></li>
<li>Now, the struct is statically sized and the reference counting overhead is removed.</li>
<li>Also keep asking yourself if you really need a <code>struct</code> with its Copy-On-Write behavior or if a <code>class</code> would fit you better</li>
</ul>]]></description>
    </item>
    
  </channel>
</rss>