changeset 14:edf2bfcd8d97

Reorganise structure
author Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com>
date Wed, 28 Apr 2021 19:01:40 +0200
parents fdab6510dc46
children 820202017183
files InteractiveCharts.xcodeproj/project.pbxproj InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate InteractiveCharts/Info.plist InteractiveCharts/InteractiveCharts.h InteractiveCharts/LineChart/ChartView.swift InteractiveCharts/LineChart/Helpers/ChartLabel.swift InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift InteractiveCharts/LineChart/Helpers/LinePath.swift InteractiveCharts/LineChart/Helpers/LineView.swift InteractiveCharts/UI Previews/ChartViewPreview.swift InteractiveCharts/UI Previews/ContentView.swift InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift InteractiveChartsTests/Info.plist InteractiveChartsTests/InteractiveChartsTests.swift Sources/InteractiveCharts/Info.plist Sources/InteractiveCharts/InteractiveCharts.h Sources/InteractiveCharts/LineChart/ChartView.swift Sources/InteractiveCharts/LineChart/Helpers/ChartLabel.swift Sources/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift Sources/InteractiveCharts/LineChart/Helpers/LinePath.swift Sources/InteractiveCharts/LineChart/Helpers/LineView.swift Sources/InteractiveCharts/UI Previews/ChartViewPreview.swift Sources/InteractiveCharts/UI Previews/ContentView.swift Sources/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift Sources/InteractiveChartsTests/Info.plist Sources/InteractiveChartsTests/InteractiveChartsTests.swift
diffstat 26 files changed, 469 insertions(+), 461 deletions(-) [+]
line wrap: on
line diff
--- a/InteractiveCharts.xcodeproj/project.pbxproj	Wed Apr 28 18:58:15 2021 +0200
+++ b/InteractiveCharts.xcodeproj/project.pbxproj	Wed Apr 28 19:01:40 2021 +0200
@@ -87,6 +87,15 @@
 			path = Helpers;
 			sourceTree = "<group>";
 		};
+		950EFF0D2639CC4D00CE1B7B /* Sources */ = {
+			isa = PBXGroup;
+			children = (
+				9557883B2636B8D800D1192D /* InteractiveCharts */,
+				955788462636B8D800D1192D /* InteractiveChartsTests */,
+			);
+			path = Sources;
+			sourceTree = "<group>";
+		};
 		951D9BDE26375DDA006B6A6D /* UI Previews */ = {
 			isa = PBXGroup;
 			children = (
@@ -109,8 +118,7 @@
 			isa = PBXGroup;
 			children = (
 				950EFF0C2639CAB300CE1B7B /* Package.swift */,
-				9557883B2636B8D800D1192D /* InteractiveCharts */,
-				955788462636B8D800D1192D /* InteractiveChartsTests */,
+				950EFF0D2639CC4D00CE1B7B /* Sources */,
 				9557883A2636B8D800D1192D /* Products */,
 			);
 			sourceTree = "<group>";
Binary file InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- a/InteractiveCharts/Info.plist	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-</dict>
-</plist>
--- a/InteractiveCharts/InteractiveCharts.h	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-//
-//  InteractiveCharts.h
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-#import <Foundation/Foundation.h>
-
-//! Project version number for InteractiveCharts.
-FOUNDATION_EXPORT double InteractiveChartsVersionNumber;
-
-//! Project version string for InteractiveCharts.
-FOUNDATION_EXPORT const unsigned char InteractiveChartsVersionString[];
-
-// In this header, you should import all the public headers of your framework using statements like #import <InteractiveCharts/PublicHeader.h>
-
-
--- a/InteractiveCharts/LineChart/ChartView.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-//
-//  ChartView.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct ChartView: View {
-    var data: [Double]
-    var dates: [String]?
-    var hours: [String]?
-    
-    @State private var showingIndicators = false
-    @State private var indexPosition = Int()
-    
-    var body: some View {
-        VStack {
-            ChartLabel(data: data, dates: dates, hours: hours, indexPosition: $indexPosition)
-                .opacity(showingIndicators ? 1: 0)
-                .padding(.vertical)
-
-            LineView(data: data, showingIndicators: $showingIndicators, indexPosition: $indexPosition)
-        }
-    }
-}
--- a/InteractiveCharts/LineChart/Helpers/ChartLabel.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-//
-//  ChartLabel.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct ChartLabel: View {
-    var data: [Double]
-    var dates: [String]?
-    var hours: [String]?
-    
-    @Binding var indexPosition: Int  // Data point position
-    
-    var body: some View {
-        HStack {
-            Group {
-                if let dates = self.dates {
-                    let date = formatStringDate(dates[indexPosition])
-                    Text(date)
-                }
-                if let hours = self.hours {
-                    let hour = hours[indexPosition]
-                    Text(hour)
-                }
-                Text("\(data[indexPosition], specifier: "%.2f")")
-                    .foregroundColor(Color(.systemBlue))
-            }
-            .font(.headline)
-        }
-    }
-    
-    /*
-     Take string in format yy-MM-dd (2021-01-01) and transform it
-     to long default string format
-     */
-    private func formatStringDate(_ stringDate: String) -> String {
-        let dateFormatter = DateFormatter()
-        dateFormatter.dateFormat = "yy-MM-dd"
-        let date = dateFormatter.date(from: stringDate)
-        
-        // Format date to the final format
-        dateFormatter.dateStyle = .long
-        let finalDate = dateFormatter.string(from: date!)
-        
-        return finalDate
-    }
-}
--- a/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-//
-//  IndicatorPoint.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct IndicatorPoint: View {
-    var body: some View {
-        Circle()
-            .frame(width: 20, height: 20)
-            .foregroundColor(Color(.systemBlue))
-            
-    }
-}
-
-struct IndicatorPoint_Previews: PreviewProvider {
-    static var previews: some View {
-        IndicatorPoint()
-    }
-}
--- a/InteractiveCharts/LineChart/Helpers/LinePath.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-//
-//  LinePath.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct LinePath: Shape {
-    var data: [Double]
-    var (width, height): (CGFloat, CGFloat)
-    @Binding var pathPoints: [CGPoint]
-    
-    func path(in rect: CGRect) -> Path {
-        var path = Path()
-        
-        let normalizedData = normalize(data)
-        let widthBetweenDataPoints = Double(width) / Double(normalizedData.count - 1)  // Remove first point
-        let initialPoint = normalizedData[0] * Double(height)
-        var x: Double = 0
-        
-        path.move(to: CGPoint(x: x, y: initialPoint))
-        for y in normalizedData {
-            if normalizedData.firstIndex(of: y) != 0 {  // Skip first point
-                x += widthBetweenDataPoints
-                let y = y * Double(height)
-                path.addLine(to: CGPoint(x: x, y: y))
-            }
-
-            // Append current point to an array. Later will be used for Drag Gesture
-            pathPoints.append(path.currentPoint!)
-        }
-        
-        return path
-    }
-    
-    /*
-     Get data -> normalize it -> 0 <= output <= 1
-     */
-    func normalize(_ data: [Double]) -> [Double] {
-        var normalData = [Double]()
-        let min = data.min()!
-        let max = data.max()!
-
-        for value in data {
-            let normal = (value - min) / (max - min)
-            normalData.append(normal)
-        }
-        
-        return normalData
-    }
-}
-
--- a/InteractiveCharts/LineChart/Helpers/LineView.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-//
-//  LineView.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct LineView: View {
-    var data: [Double]
-    var dates: [String]?
-    var hours: [String]?
-    
-    @Binding var showingIndicators: Bool
-    @Binding var indexPosition: Int
-    @State private var IndicatorPointPosition: CGPoint = .zero
-    @State private var pathPoints = [CGPoint]()
-    
-    var body: some View {
-        ZStack {            
-            GeometryReader { proxy in
-                LinePath(data: data, width: proxy.size.width, height: proxy.size.height, pathPoints: $pathPoints)
-                    .stroke(colorLine(), lineWidth: 2)
-            }
-            
-            if showingIndicators {
-                IndicatorPoint()
-                    .position(x: IndicatorPointPosition.x, y: IndicatorPointPosition.y)
-            }
-        }
-        .rotationEffect(.degrees(180), anchor: .center)
-        .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0))
-        .contentShape(Rectangle())  // Control tappable area
-        .gesture(
-            LongPressGesture(minimumDuration: 0.2)
-                .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local))
-                .onChanged({ value in  // Get value of the gesture
-                    switch value {
-                    case .second(true, let drag):
-                        if let longPressLocation = drag?.location {
-                            dragGesture(longPressLocation)
-                        }
-                    default:
-                        break
-                    }
-                })
-                // Hide indicator when finish
-                .onEnded({ value in
-                    self.showingIndicators = false
-                })
-        )
-    }
-    
-    /*
-     Color path depending on data.
-     */
-    private func colorLine() -> Color {
-        var color = Color(.systemGreen)
-        
-        if data.first! > data.last! {
-            color = Color(.systemRed)
-        } else if data.first! == data.last! {
-            color = Color(.systemTeal)
-        }
-        else if showingIndicators {
-            color = Color(.systemBlue)
-        }
-        
-        return color
-    }
-    
-    /*
-     When the user drag on Path -> Modifiy indicator point to move it on the path accordingly
-     */
-    private func dragGesture(_ longPressLocation: CGPoint) {
-        let (closestXPoint, closestYPoint, yPointIndex) = getClosestValueFrom(longPressLocation, inData: pathPoints)
-        self.IndicatorPointPosition.x = closestXPoint
-        self.IndicatorPointPosition.y = closestYPoint
-        self.showingIndicators = true
-        self.indexPosition = yPointIndex
-    }
-    
-    /*
-     First, search the closest X point in Path from the tapped location.
-     Then, find the correspondent Y point in Path.
-     */
-    private func getClosestValueFrom(_ value: CGPoint, inData: [CGPoint]) -> (CGFloat, CGFloat, Int) {
-        let touchPoint: (CGFloat, CGFloat) = (value.x, value.y)
-        let xPathPoints = inData.map { $0.x }
-        let yPathPoints = inData.map { $0.y }
-        
-        // Closest X value
-        let closestXPoint = xPathPoints.enumerated().min( by: { abs($0.1 - touchPoint.0) < abs($1.1 - touchPoint.0) } )!
-        let closestYPointIndex = xPathPoints.firstIndex(of: closestXPoint.element)!
-        let closestYPoint = yPathPoints[closestYPointIndex]
-        
-        // Index of the closest points in the array
-        let yPointIndex = yPathPoints.firstIndex(of: closestYPoint)!
-
-        return (closestXPoint.element, closestYPoint, yPointIndex)
-    }
-}
--- a/InteractiveCharts/UI Previews/ChartViewPreview.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-//
-//  ChartViewPreview.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct ChartViewPreview: View {
-    var data: [Double]
-    var dates: [String]?
-    var hours: [String]?
-    
-    var range = ["5D", "1M", "3M", "1Y", "5Y"]
-    @State private var selectedRange = "3M"
-    
-    var body: some View {
-        NavigationView {
-            VStack(alignment: .leading) {
-                Text("Apple Inc")
-                    .font(.title3)
-                    .padding([.horizontal, .bottom])
-                
-                Picker("Select a range", selection: $selectedRange) {
-                    ForEach(range, id: \.self) {
-                        Text($0)
-                    }
-                }
-                .pickerStyle(SegmentedPickerStyle())
-                .padding(.horizontal)
-                
-                ChartView(data: data, dates: dates, hours: hours)
-                    .padding(.vertical)
-            }
-            .navigationTitle("AAPL")
-            .toolbar {
-                ToolbarItem(placement: .navigationBarTrailing) {
-                    Button(action: {}) {
-                        Image(systemName: "star")
-                    }
-                }
-                
-                ToolbarItem(placement: .navigationBarLeading) {
-                    Button(action: {}) {
-                        Image(systemName: "plus.circle")
-                    }
-                }
-            }
-        }
-        .navigationViewStyle(StackNavigationViewStyle())
-    }
-}
--- a/InteractiveCharts/UI Previews/ContentView.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-//
-//  ContentView.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-struct ContentView: View {
-    var body: some View {
-        TabView {
-            ChartViewPreview(data: generateSampleData(350))
-                .tabItem {
-                    Label("ChartView", systemImage: "house")
-                }
-        }
-    }
-}
-
-struct ContentView_Previews: PreviewProvider {
-    static var previews: some View {
-        ContentView()
-    }
-}
--- a/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-//
-//  GenerateSampleData.swift
-//  InteractiveCharts
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import SwiftUI
-
-/*
- Generate sample data
- */
-func generateSampleData(_ n: Int) -> [Double] {
-    var prices = [Double]()
-
-    for _ in (1..<n) {
-        var lastPrice = prices.last ?? 50.0
-        let randomNumber = Double.random(in: 0...0.02)
-        
-        if randomNumber < 0.013 {
-            lastPrice = lastPrice * (1 - randomNumber)
-        } else {
-            lastPrice = lastPrice * (1 + randomNumber)
-        }
-        
-        prices.append(lastPrice)
-    }
-    return prices
-}
--- a/InteractiveChartsTests/Info.plist	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-</dict>
-</plist>
--- a/InteractiveChartsTests/InteractiveChartsTests.swift	Wed Apr 28 18:58:15 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-//
-//  InteractiveChartsTests.swift
-//  InteractiveChartsTests
-//
-//  Created by Dennis Concepción Martín on 26/4/21.
-//
-
-import XCTest
-@testable import InteractiveCharts
-
-class InteractiveChartsTests: XCTestCase {
-
-    override func setUpWithError() throws {
-        // Put setup code here. This method is called before the invocation of each test method in the class.
-    }
-
-    override func tearDownWithError() throws {
-        // Put teardown code here. This method is called after the invocation of each test method in the class.
-    }
-
-    func testExample() throws {
-        // This is an example of a functional test case.
-        // Use XCTAssert and related functions to verify your tests produce the correct results.
-    }
-
-    func testPerformanceExample() throws {
-        // This is an example of a performance test case.
-        self.measure {
-            // Put the code you want to measure the time of here.
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/Info.plist	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/InteractiveCharts.h	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,18 @@
+//
+//  InteractiveCharts.h
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for InteractiveCharts.
+FOUNDATION_EXPORT double InteractiveChartsVersionNumber;
+
+//! Project version string for InteractiveCharts.
+FOUNDATION_EXPORT const unsigned char InteractiveChartsVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <InteractiveCharts/PublicHeader.h>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/LineChart/ChartView.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,27 @@
+//
+//  ChartView.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct ChartView: View {
+    var data: [Double]
+    var dates: [String]?
+    var hours: [String]?
+    
+    @State private var showingIndicators = false
+    @State private var indexPosition = Int()
+    
+    var body: some View {
+        VStack {
+            ChartLabel(data: data, dates: dates, hours: hours, indexPosition: $indexPosition)
+                .opacity(showingIndicators ? 1: 0)
+                .padding(.vertical)
+
+            LineView(data: data, showingIndicators: $showingIndicators, indexPosition: $indexPosition)
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/LineChart/Helpers/ChartLabel.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,50 @@
+//
+//  ChartLabel.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct ChartLabel: View {
+    var data: [Double]
+    var dates: [String]?
+    var hours: [String]?
+    
+    @Binding var indexPosition: Int  // Data point position
+    
+    var body: some View {
+        HStack {
+            Group {
+                if let dates = self.dates {
+                    let date = formatStringDate(dates[indexPosition])
+                    Text(date)
+                }
+                if let hours = self.hours {
+                    let hour = hours[indexPosition]
+                    Text(hour)
+                }
+                Text("\(data[indexPosition], specifier: "%.2f")")
+                    .foregroundColor(Color(.systemBlue))
+            }
+            .font(.headline)
+        }
+    }
+    
+    /*
+     Take string in format yy-MM-dd (2021-01-01) and transform it
+     to long default string format
+     */
+    private func formatStringDate(_ stringDate: String) -> String {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "yy-MM-dd"
+        let date = dateFormatter.date(from: stringDate)
+        
+        // Format date to the final format
+        dateFormatter.dateStyle = .long
+        let finalDate = dateFormatter.string(from: date!)
+        
+        return finalDate
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,23 @@
+//
+//  IndicatorPoint.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct IndicatorPoint: View {
+    var body: some View {
+        Circle()
+            .frame(width: 20, height: 20)
+            .foregroundColor(Color(.systemBlue))
+            
+    }
+}
+
+struct IndicatorPoint_Previews: PreviewProvider {
+    static var previews: some View {
+        IndicatorPoint()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/LineChart/Helpers/LinePath.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,54 @@
+//
+//  LinePath.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct LinePath: Shape {
+    var data: [Double]
+    var (width, height): (CGFloat, CGFloat)
+    @Binding var pathPoints: [CGPoint]
+    
+    func path(in rect: CGRect) -> Path {
+        var path = Path()
+        
+        let normalizedData = normalize(data)
+        let widthBetweenDataPoints = Double(width) / Double(normalizedData.count - 1)  // Remove first point
+        let initialPoint = normalizedData[0] * Double(height)
+        var x: Double = 0
+        
+        path.move(to: CGPoint(x: x, y: initialPoint))
+        for y in normalizedData {
+            if normalizedData.firstIndex(of: y) != 0 {  // Skip first point
+                x += widthBetweenDataPoints
+                let y = y * Double(height)
+                path.addLine(to: CGPoint(x: x, y: y))
+            }
+
+            // Append current point to an array. Later will be used for Drag Gesture
+            pathPoints.append(path.currentPoint!)
+        }
+        
+        return path
+    }
+    
+    /*
+     Get data -> normalize it -> 0 <= output <= 1
+     */
+    func normalize(_ data: [Double]) -> [Double] {
+        var normalData = [Double]()
+        let min = data.min()!
+        let max = data.max()!
+
+        for value in data {
+            let normal = (value - min) / (max - min)
+            normalData.append(normal)
+        }
+        
+        return normalData
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/LineChart/Helpers/LineView.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,103 @@
+//
+//  LineView.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct LineView: View {
+    var data: [Double]
+    var dates: [String]?
+    var hours: [String]?
+    
+    @Binding var showingIndicators: Bool
+    @Binding var indexPosition: Int
+    @State private var IndicatorPointPosition: CGPoint = .zero
+    @State private var pathPoints = [CGPoint]()
+    
+    var body: some View {
+        ZStack {            
+            GeometryReader { proxy in
+                LinePath(data: data, width: proxy.size.width, height: proxy.size.height, pathPoints: $pathPoints)
+                    .stroke(colorLine(), lineWidth: 2)
+            }
+            
+            if showingIndicators {
+                IndicatorPoint()
+                    .position(x: IndicatorPointPosition.x, y: IndicatorPointPosition.y)
+            }
+        }
+        .rotationEffect(.degrees(180), anchor: .center)
+        .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0))
+        .contentShape(Rectangle())  // Control tappable area
+        .gesture(
+            LongPressGesture(minimumDuration: 0.2)
+                .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local))
+                .onChanged({ value in  // Get value of the gesture
+                    switch value {
+                    case .second(true, let drag):
+                        if let longPressLocation = drag?.location {
+                            dragGesture(longPressLocation)
+                        }
+                    default:
+                        break
+                    }
+                })
+                // Hide indicator when finish
+                .onEnded({ value in
+                    self.showingIndicators = false
+                })
+        )
+    }
+    
+    /*
+     Color path depending on data.
+     */
+    private func colorLine() -> Color {
+        var color = Color(.systemGreen)
+        
+        if data.first! > data.last! {
+            color = Color(.systemRed)
+        } else if data.first! == data.last! {
+            color = Color(.systemTeal)
+        }
+        else if showingIndicators {
+            color = Color(.systemBlue)
+        }
+        
+        return color
+    }
+    
+    /*
+     When the user drag on Path -> Modifiy indicator point to move it on the path accordingly
+     */
+    private func dragGesture(_ longPressLocation: CGPoint) {
+        let (closestXPoint, closestYPoint, yPointIndex) = getClosestValueFrom(longPressLocation, inData: pathPoints)
+        self.IndicatorPointPosition.x = closestXPoint
+        self.IndicatorPointPosition.y = closestYPoint
+        self.showingIndicators = true
+        self.indexPosition = yPointIndex
+    }
+    
+    /*
+     First, search the closest X point in Path from the tapped location.
+     Then, find the correspondent Y point in Path.
+     */
+    private func getClosestValueFrom(_ value: CGPoint, inData: [CGPoint]) -> (CGFloat, CGFloat, Int) {
+        let touchPoint: (CGFloat, CGFloat) = (value.x, value.y)
+        let xPathPoints = inData.map { $0.x }
+        let yPathPoints = inData.map { $0.y }
+        
+        // Closest X value
+        let closestXPoint = xPathPoints.enumerated().min( by: { abs($0.1 - touchPoint.0) < abs($1.1 - touchPoint.0) } )!
+        let closestYPointIndex = xPathPoints.firstIndex(of: closestXPoint.element)!
+        let closestYPoint = yPathPoints[closestYPointIndex]
+        
+        // Index of the closest points in the array
+        let yPointIndex = yPathPoints.firstIndex(of: closestYPoint)!
+
+        return (closestXPoint.element, closestYPoint, yPointIndex)
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/UI Previews/ChartViewPreview.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,53 @@
+//
+//  ChartViewPreview.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct ChartViewPreview: View {
+    var data: [Double]
+    var dates: [String]?
+    var hours: [String]?
+    
+    var range = ["5D", "1M", "3M", "1Y", "5Y"]
+    @State private var selectedRange = "3M"
+    
+    var body: some View {
+        NavigationView {
+            VStack(alignment: .leading) {
+                Text("Apple Inc")
+                    .font(.title3)
+                    .padding([.horizontal, .bottom])
+                
+                Picker("Select a range", selection: $selectedRange) {
+                    ForEach(range, id: \.self) {
+                        Text($0)
+                    }
+                }
+                .pickerStyle(SegmentedPickerStyle())
+                .padding(.horizontal)
+                
+                ChartView(data: data, dates: dates, hours: hours)
+                    .padding(.vertical)
+            }
+            .navigationTitle("AAPL")
+            .toolbar {
+                ToolbarItem(placement: .navigationBarTrailing) {
+                    Button(action: {}) {
+                        Image(systemName: "star")
+                    }
+                }
+                
+                ToolbarItem(placement: .navigationBarLeading) {
+                    Button(action: {}) {
+                        Image(systemName: "plus.circle")
+                    }
+                }
+            }
+        }
+        .navigationViewStyle(StackNavigationViewStyle())
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/UI Previews/ContentView.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,25 @@
+//
+//  ContentView.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+struct ContentView: View {
+    var body: some View {
+        TabView {
+            ChartViewPreview(data: generateSampleData(350))
+                .tabItem {
+                    Label("ChartView", systemImage: "house")
+                }
+        }
+    }
+}
+
+struct ContentView_Previews: PreviewProvider {
+    static var previews: some View {
+        ContentView()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,29 @@
+//
+//  GenerateSampleData.swift
+//  InteractiveCharts
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import SwiftUI
+
+/*
+ Generate sample data
+ */
+func generateSampleData(_ n: Int) -> [Double] {
+    var prices = [Double]()
+
+    for _ in (1..<n) {
+        var lastPrice = prices.last ?? 50.0
+        let randomNumber = Double.random(in: 0...0.02)
+        
+        if randomNumber < 0.013 {
+            lastPrice = lastPrice * (1 - randomNumber)
+        } else {
+            lastPrice = lastPrice * (1 + randomNumber)
+        }
+        
+        prices.append(lastPrice)
+    }
+    return prices
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveChartsTests/Info.plist	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/InteractiveChartsTests/InteractiveChartsTests.swift	Wed Apr 28 19:01:40 2021 +0200
@@ -0,0 +1,33 @@
+//
+//  InteractiveChartsTests.swift
+//  InteractiveChartsTests
+//
+//  Created by Dennis Concepción Martín on 26/4/21.
+//
+
+import XCTest
+@testable import InteractiveCharts
+
+class InteractiveChartsTests: XCTestCase {
+
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testExample() throws {
+        // This is an example of a functional test case.
+        // Use XCTAssert and related functions to verify your tests produce the correct results.
+    }
+
+    func testPerformanceExample() throws {
+        // This is an example of a performance test case.
+        self.measure {
+            // Put the code you want to measure the time of here.
+        }
+    }
+
+}