Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
220: Real-Time Data Updates in Swift Charts
When you first start pumping live data into a Swift Chart, you'll likely fall into a trap I did early on: thinking that simply appending a new value to your data array will automatically make the chart "slide" or scroll. You'll write your append() logic, run the app, and realize that instead of a smooth scrolling timeline, your chart is just getting more and more crowded as the X-axis compresses to fit every single point since the app started. It doesn't "move"; it just squishes.
The "Append and Pray" Approach vs. The Sliding Window
The misconception is that Chart handles the temporal windowing for you. It doesn't. It renders the data you give it. If you give it 10 points, it shows 10. If you give it 10,000 points over an hour, it tries to render 10,000 points in the same amount of screen real estate. Not only does this look terrible, but your frame rate will tank as the data set grows.
To get that professional "heart rate monitor" or "CPU dashboard" feel, you need a sliding window. This means you maintain a fixed number of samples. When a new piece of data arrives, you add it to the end and drop the oldest piece from the front. I usually recommend a Deque for this if you're doing heavy lifting, but for most Swift Charts use cases, a simple Array with dropFirst() works perfectly.
struct CPUUsageView: View {
@State private var usageData: [CPUReading] = []
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
var body: some View {
Chart(usageData) { reading in
LineMark(
x: .value("Time", reading.timestamp),
y: .value("Usage", reading.value)
)
}
.animation(.easeInOut, value: usageData) // The magic sauce
.onReceive(timer) { _ in
let newReading = CPUReading(timestamp: Date(), value: Double.random(in: 10...90))
// Maintain a window of 20 points
usageData.append(newReading)
if usageData.count > 20 {
usageData.removeFirst()
}
}
}
}
Triggering Animations Without Resetting the View
Now, if you run the code above, you'll notice the line jumps. It's not "sliding"; it's teleporting. This is where most people get frustrated. They try to wrap the whole chart in a withAnimation block, but that often causes the entire chart to fade in and out or glitch.
The secret is the .animation(_:value:) modifier. By attaching the animation to the usageData value specifically, you're telling SwiftUI: "Whenever the identity or content of this array changes, interpolate the positions of the marks." Because we are keeping the number of points constant (via our sliding window), SwiftUI can map the old point at index 0 to the new point at index 0, creating that fluid, shifting motion we're looking for.
One pro tip: make sure your data model conforms to Identifiable using a unique ID (like a UUID or the timestamp itself). If SwiftUI can't uniquely identify which point is which, it will give up on the animation and just "snap" the line to the new position. I've spent way too many hours debugging "jittery" charts only to realize I was using the index as the ID, which changes every time I removeFirst().
📋 Practical Task
Build a Real-Time Network Latency Monitor
Your goal is to create a view that simulates a network ping monitor. The chart should show the latency (in milliseconds) of a connection over time.
- Create a
LatencyReadingstruct that conforms toIdentifiable. - Implement a timer that generates a random latency value between 20ms and 150ms every 0.3 seconds.
- Implement a sliding window that keeps exactly 30 data points on the screen at all times.
- Use a
LineMarkto visualize the data. - Apply a
.animationmodifier so that the line slides smoothly to the left as new data arrives. - Bonus: Add a
RuleMarkat 100ms to indicate a "high latency" threshold.
There are no comments for now.