1469 lines
46 KiB
Markdown
1469 lines
46 KiB
Markdown
# Automated Diagnostic Report: Qwen3-Coder-30B-A3B-Instruct-UD-IQ3_XXS
|
|
|
|
---
|
|
|
|
## Performance Summary
|
|
|
|
<!--SUMMARY_START-->
|
|
* **Average Tokens/s:** 21.69
|
|
* **Average Time to First Token:** 0.0s
|
|
* **Total Tokens Generated:** 9381
|
|
<!--SUMMARY_END-->
|
|
|
|
## Qualitative Analysis
|
|
|
|
*(Manual grading and analysis of the responses is required to determine the final letter grade.)*
|
|
|
|
---
|
|
|
|
## Test 1: Write a Swift function that solves the FizzBuzz problem.
|
|
|
|
*Source:* `test1.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Write a Swift function that solves the FizzBuzz problem.
|
|
|
|
The function must have the exact signature:
|
|
`func generateFizzBuzz(upTo max: Int) -> [String]`
|
|
|
|
It must not print directly to the console. Instead, it should return an array of strings.
|
|
|
|
Rules:
|
|
1. For numbers divisible by 3, the string should be "Fizz".
|
|
2. For numbers divisible by 5, the string should be "Buzz".
|
|
3. For numbers divisible by both 3 and 5, the string should be "FizzBuzz".
|
|
4. Otherwise, it should be the number itself as a string.
|
|
|
|
Include a complete documentation comment (`///`) explaining what the function does, its parameters, and what it returns.
|
|
```
|
|
|
|
### Response:
|
|
|
|
```swift
|
|
/// Generates an array of strings representing the FizzBuzz sequence up to a given number.
|
|
///
|
|
/// The FizzBuzz sequence follows these rules:
|
|
/// - For numbers divisible by 3, returns "Fizz"
|
|
/// - For numbers divisible by 5, returns "Buzz"
|
|
/// - For numbers divisible by both 3 and 5, returns "FizzBuzz"
|
|
/// - For all other numbers, returns the number as a string
|
|
///
|
|
/// - Parameter max: The upper limit (inclusive) for generating the FizzBuzz sequence
|
|
/// - Returns: An array of strings representing the FizzBuzz sequence from 1 to max (inclusive)
|
|
///
|
|
/// - Example:
|
|
/// ```
|
|
/// generateFizzBuzz(upTo: 5) // Returns ["1", "2", "Fizz", "4", "Buzz"]
|
|
/// generateFizzBuzz(upTo: 15) // Returns ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz"]
|
|
/// ```
|
|
func generateFizzBuzz(upTo max: Int) -> [String] {
|
|
var result: [String] = []
|
|
|
|
for number in 1...max {
|
|
switch (number % 3 == 0, number % 5 == 0) {
|
|
case (true, true):
|
|
result.append("FizzBuzz")
|
|
case (true, false):
|
|
result.append("Fizz")
|
|
case (false, true):
|
|
result.append("Buzz")
|
|
default:
|
|
result.append(String(number))
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
```
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 26.96
|
|
* **Total Tokens:** 502
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 2: Create a complete SwiftUI view for a user profile card.
|
|
|
|
*Source:* `test2.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Create a complete SwiftUI view for a user profile card.
|
|
|
|
The view should be named `UserProfileCard` and be initialized with a simple `User` struct containing `name: String`, `handle: String`, and `avatarURL: URL`.
|
|
|
|
Requirements:
|
|
- Use an `AsyncImage` to load the user's avatar from the URL.
|
|
- Display the user's name in a `.headline` font and their handle (e.g., "@username") in a `.subheadline` font with a secondary color.
|
|
- Include a "Follow" button that uses the "person.badge.plus" SF Symbol.
|
|
- Arrange the elements cleanly inside an HStack with appropriate spacing.
|
|
- The entire card should have a subtle shadow and rounded corners.
|
|
```
|
|
|
|
### Response:
|
|
|
|
```swift
|
|
import SwiftUI
|
|
|
|
// User struct definition
|
|
struct User: Identifiable {
|
|
let id = UUID()
|
|
let name: String
|
|
let handle: String
|
|
let avatarURL: URL
|
|
}
|
|
|
|
// User profile card view
|
|
struct UserProfileCard: View {
|
|
let user: User
|
|
|
|
var body: some View {
|
|
HStack(spacing: 16) {
|
|
AsyncImage(url: user.avatarURL) { image in
|
|
image
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
} placeholder: {
|
|
Rectangle()
|
|
.fill(Color.gray.opacity(0.3))
|
|
}
|
|
.frame(width: 60, height: 60)
|
|
.clipShape(Circle())
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(user.name)
|
|
.font(.headline)
|
|
|
|
Text(user.handle)
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Button(action: {
|
|
// Follow action would go here
|
|
print("Follow button tapped for \(user.name)")
|
|
}) {
|
|
Image(systemName: "person.badge.plus")
|
|
.font(.title2)
|
|
.foregroundColor(.blue)
|
|
.padding(8)
|
|
.background(Color(.systemBlue).opacity(0.1))
|
|
.clipShape(Circle())
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
}
|
|
.padding(16)
|
|
.background(Color(.systemBackground))
|
|
.cornerRadius(12)
|
|
.shadow(radius: 4)
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
|
|
// Preview provider
|
|
struct UserProfileCard_Previews: PreviewProvider {
|
|
static let sampleUser = User(
|
|
name: "John Doe",
|
|
handle: "@johndoe",
|
|
avatarURL: URL(string: "https://via.placeholder.com/100")!
|
|
)
|
|
|
|
static var previews: some View {
|
|
UserProfileCard(user: sampleUser)
|
|
.previewLayout(.sizeThatFits)
|
|
.padding()
|
|
}
|
|
}
|
|
```
|
|
|
|
This SwiftUI view includes:
|
|
|
|
1. A `User` struct with the required properties
|
|
2. An `AsyncImage` that loads the avatar from the provided URL
|
|
3. The user's name displayed in `.headline` font
|
|
4. The handle displayed in `.subheadline` font with secondary color
|
|
5. A "Follow" button using the "person.badge.plus" SF Symbol
|
|
6. Clean HStack arrangement with appropriate spacing
|
|
7. Subtle shadow and rounded corners using `.shadow()` and `.cornerRadius()`
|
|
8. A preview provider for easy testing
|
|
|
|
The card has:
|
|
- Circular avatar with proper sizing
|
|
- Clean typography hierarchy
|
|
- Accessible button with visual feedback
|
|
- Subtle shadow for depth
|
|
- Rounded corners
|
|
- Proper spacing and alignment
|
|
- Placeholder for the follow action
|
|
|
|
The view is fully self-contained and can be used anywhere in a SwiftUI project by passing a `User` instance to the initializer.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 27.02
|
|
* **Total Tokens:** 774
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 3: I have this old networking code that uses a completion handler. Please refactor it to use modern Swift Concurrency with async/await.
|
|
|
|
*Source:* `test3.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
I have this old networking code that uses a completion handler. Please refactor it to use modern Swift Concurrency with async/await.
|
|
|
|
The new function should be marked with `async throws` and return a `[Post]` object upon success. Also, include an example of how to call this new async function inside a `Task` with a `do-catch` block.
|
|
|
|
**Old Code:**
|
|
struct Post: Codable {
|
|
let id: Int
|
|
let title: String
|
|
}
|
|
|
|
func fetchPosts(completion: @escaping (Result<[Post], Error>) -> Void) {
|
|
guard let url = URL(string: "https://api.example.com/posts") else { return }
|
|
URLSession.shared.dataTask(with: url) { data, _, error in
|
|
if let error = error { completion(.failure(error)); return }
|
|
guard let data = data else { return }
|
|
do {
|
|
let posts = try JSONDecoder().decode([Post].self, from: data)
|
|
completion(.success(posts))
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}.resume()
|
|
}
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's the refactored code using modern Swift Concurrency with async/await:
|
|
|
|
```swift
|
|
import Foundation
|
|
|
|
struct Post: Codable {
|
|
let id: Int
|
|
let title: String
|
|
}
|
|
|
|
// Modern async/await version
|
|
func fetchPosts() async throws -> [Post] {
|
|
guard let url = URL(string: "https://api.example.com/posts") else {
|
|
throw URLError(.badURL)
|
|
}
|
|
|
|
let (data, response) = try await URLSession.shared.data(from: url)
|
|
|
|
// Optional: Validate HTTP response
|
|
if let httpResponse = response as? HTTPURLResponse,
|
|
!(200...299).contains(httpResponse.statusCode) {
|
|
throw URLError(.badServerResponse)
|
|
}
|
|
|
|
return try JSONDecoder().decode([Post].self, from: data)
|
|
}
|
|
|
|
// Example of how to call the async function inside a Task
|
|
func exampleUsage() {
|
|
Task {
|
|
do {
|
|
let posts = try await fetchPosts()
|
|
print("Successfully fetched \(posts.count) posts")
|
|
// Process your posts here
|
|
for post in posts {
|
|
print("Post: \(post.title)")
|
|
}
|
|
} catch {
|
|
print("Failed to fetch posts: \(error)")
|
|
// Handle the error appropriately
|
|
}
|
|
}
|
|
}
|
|
|
|
// Alternative example with explicit error handling
|
|
func exampleUsageWithExplicitErrorHandling() {
|
|
Task {
|
|
do {
|
|
let posts = try await fetchPosts()
|
|
// Handle success
|
|
print("Got \(posts.count) posts")
|
|
} catch let urLError as URLError {
|
|
switch urLError.code {
|
|
case .notConnectedToInternet:
|
|
print("No internet connection")
|
|
case .timedOut:
|
|
print("Request timed out")
|
|
default:
|
|
print("Network error: \(urLError)")
|
|
}
|
|
} catch {
|
|
print("Unknown error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key changes made:**
|
|
|
|
1. **Removed completion handler**: The function now uses `async throws` instead of callback-based approach
|
|
2. **Simplified error handling**: Using `try await` with `URLSession.shared.data(from:)` which throws on failure
|
|
3. **Added HTTP response validation**: Optional but recommended to check for proper HTTP status codes
|
|
4. **Cleaner return**: Directly returns `[Post]` array on success
|
|
5. **Proper error types**: Uses `URLError` for network-related errors
|
|
|
|
**Usage notes:**
|
|
|
|
- The function can be called directly with `try await` inside an `async` context
|
|
- When calling from a non-async context (like in a button action), wrap it in a `Task` as shown in the examples
|
|
- Error handling is more straightforward with `do-catch` blocks
|
|
- The function now properly propagates errors through Swift's throwing mechanism
|
|
|
|
**Important**: Make sure your target deployment is iOS 13.0+, macOS 10.15+, or later, as async/await requires these minimum versions.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 26.08
|
|
* **Total Tokens:** 847
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 4: Create a complete SwiftData example for a macOS 26 app.
|
|
|
|
*Source:* `test4.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Create a complete SwiftData example for a macOS 26 app.
|
|
|
|
You must define two models: `Author` and `Book`. An `Author` can have many `Book`s (a one-to-many relationship).
|
|
|
|
Then, create a SwiftUI view that displays a list of all authors. When an author's name is tapped, it should navigate to a detail view that shows a list of all the books written by that specific author. Use a SwiftData `@Query` to fetch the data.
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's a complete SwiftData example for a macOS app with Author and Book models:
|
|
|
|
```swift
|
|
// Author.swift
|
|
import SwiftData
|
|
import Foundation
|
|
|
|
@Model
|
|
final class Author {
|
|
var name: String
|
|
var birthYear: Int?
|
|
@Relationship(deleteRule: .cascade, inverse: \Book.author)
|
|
var books: [Book]
|
|
|
|
init(name: String, birthYear: Int? = nil) {
|
|
self.name = name
|
|
self.birthYear = birthYear
|
|
self.books = []
|
|
}
|
|
}
|
|
|
|
// Book.swift
|
|
import SwiftData
|
|
import Foundation
|
|
|
|
@Model
|
|
final class Book {
|
|
var title: String
|
|
var publicationYear: Int?
|
|
var isbn: String?
|
|
@Relationship(inverse: \Author.books)
|
|
var author: Author?
|
|
|
|
init(title: String, publicationYear: Int? = nil, isbn: String? = nil) {
|
|
self.title = title
|
|
self.publicationYear = publicationYear
|
|
self.isbn = isbn
|
|
}
|
|
}
|
|
|
|
// AuthorListView.swift
|
|
import SwiftUI
|
|
import SwiftData
|
|
|
|
struct AuthorListView: View {
|
|
@Query(sort: \Author.name)
|
|
private var authors: [Author]
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List(authors, id: \.id) { author in
|
|
NavigationLink(destination: BookListView(author: author)) {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(author.name)
|
|
.font(.headline)
|
|
if let birthYear = author.birthYear {
|
|
Text("Born: \(birthYear)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
Text("\(author.books.count) book\(author.books.count != 1 ? "s" : "")")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Authors")
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
NavigationLink("Add Author", destination: AddAuthorView())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// BookListView.swift
|
|
struct BookListView: View {
|
|
let author: Author
|
|
@Query(sort: \Book.publicationYear)
|
|
private var books: [Book]
|
|
|
|
init(author: Author) {
|
|
self.author = author
|
|
// Set up the query to filter books by this author
|
|
_books = Query(filter: #Predicate<Book> { book in
|
|
book.author?.id == author.id
|
|
}, sort: \Book.publicationYear)
|
|
}
|
|
|
|
var body: some View {
|
|
List(books, id: \.id) { book in
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(book.title)
|
|
.font(.headline)
|
|
if let publicationYear = book.publicationYear {
|
|
Text("Published: \(publicationYear)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
if let isbn = book.isbn {
|
|
Text("ISBN: \(isbn)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
.navigationTitle(author.name)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
NavigationLink("Add Book", destination: AddBookView(author: author))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddAuthorView.swift
|
|
struct AddAuthorView: View {
|
|
@Environment(\.modelContext) private var modelContext
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
@State private var name = ""
|
|
@State private var birthYear = ""
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section("Author Information") {
|
|
TextField("Name", text: $name)
|
|
TextField("Birth Year", text: $birthYear)
|
|
.keyboardType(.numberPad)
|
|
}
|
|
}
|
|
.navigationTitle("Add Author")
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button("Save") {
|
|
if !name.isEmpty {
|
|
let birthYearValue = birthYear.isEmpty ? nil : Int(birthYear)
|
|
let author = Author(name: name, birthYear: birthYearValue)
|
|
modelContext.insert(author)
|
|
dismiss()
|
|
}
|
|
}
|
|
.disabled(name.isEmpty)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddBookView.swift
|
|
struct AddBookView: View {
|
|
@Environment(\.modelContext) private var modelContext
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
let author: Author
|
|
@State private var title = ""
|
|
@State private var publicationYear = ""
|
|
@State private var isbn = ""
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section("Book Information") {
|
|
TextField("Title", text: $title)
|
|
TextField("Publication Year", text: $publicationYear)
|
|
.keyboardType(.numberPad)
|
|
TextField("ISBN", text: $isbn)
|
|
}
|
|
}
|
|
.navigationTitle("Add Book")
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button("Save") {
|
|
if !title.isEmpty {
|
|
let publicationYearValue = publicationYear.isEmpty ? nil : Int(publicationYear)
|
|
let book = Book(title: title, publicationYear: publicationYearValue, isbn: isbn.isEmpty ? nil : isbn)
|
|
book.author = author
|
|
modelContext.insert(book)
|
|
dismiss()
|
|
}
|
|
}
|
|
.disabled(title.isEmpty)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// App.swift
|
|
import SwiftUI
|
|
import SwiftData
|
|
|
|
@main
|
|
struct BookLibraryApp: App {
|
|
var sharedModelContainer: ModelContainer = {
|
|
do {
|
|
let schema = Schema([
|
|
Author.self,
|
|
Book.self
|
|
])
|
|
let configuration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
|
|
|
|
return try ModelContainer(for: schema, configurations: [configuration])
|
|
} catch {
|
|
fatalError("Could not create ModelContainer: \(error)")
|
|
}
|
|
}()
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
AuthorListView()
|
|
.modelContainer(sharedModelContainer)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
This example includes:
|
|
|
|
1. **Models**:
|
|
- `Author`: Has a name, birth year, and a to-many relationship with books
|
|
- `Book`: Has title, publication year, ISBN, and a to-one relationship with author
|
|
|
|
2. **Core Features**:
|
|
- One-to-many relationship between Author and Book
|
|
- SwiftData `@Query` for fetching data
|
|
- Navigation between author list → author detail → book list
|
|
- Add author and add book functionality
|
|
- Proper data binding and model context handling
|
|
|
|
3. **Views**:
|
|
- `AuthorListView`: Shows all authors with basic info
|
|
- `BookListView`: Shows books for a specific author
|
|
- `AddAuthorView`: Form to add new authors
|
|
- `AddBookView`: Form to add books to an author
|
|
|
|
4. **Key SwiftData Features**:
|
|
- `@Relationship` with proper delete rules
|
|
- `@Query` for data fetching
|
|
- `@Environment(\.modelContext)` for data persistence
|
|
- `@Predicate` for filtering queries
|
|
- Proper model container setup
|
|
|
|
To use this in your macOS app:
|
|
1. Create a new macOS SwiftData project
|
|
2. Add these files to your project
|
|
3. Make sure your app's SceneDelegate or App file includes the ModelContainer setup
|
|
4. The app will automatically create and manage the database
|
|
|
|
The app allows you to:
|
|
- View all authors in a list
|
|
- Tap on an author to see their books
|
|
- Add new authors
|
|
- Add books to existing authors
|
|
- See basic information about each author and their books
|
|
|
|
The relationships are properly configured with cascade deletion rules, so when an author is deleted, their books are also deleted.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 24.04
|
|
* **Total Tokens:** 1769
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 5: Write a thread-safe counter class in Swift using an `actor`.
|
|
|
|
*Source:* `test5.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Write a thread-safe counter class in Swift using an `actor`.
|
|
|
|
The actor should be named `SafeCounter`. It must have a private property to hold the current count and two public methods:
|
|
1. `func increment() async`
|
|
2. `func getCount() async -> Int`
|
|
|
|
Also, provide a simple example of how to create an instance of the actor and call its methods concurrently from two different `Task`s.
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's a thread-safe counter class in Swift using an actor:
|
|
|
|
```swift
|
|
actor SafeCounter {
|
|
private var count: Int = 0
|
|
|
|
func increment() async {
|
|
count += 1
|
|
}
|
|
|
|
func getCount() async -> Int {
|
|
return count
|
|
}
|
|
}
|
|
|
|
// Example usage
|
|
func demonstrateSafeCounter() async {
|
|
// Create an instance of the SafeCounter actor
|
|
let counter = SafeCounter()
|
|
|
|
// Create two concurrent tasks that increment the counter
|
|
let task1 = Task {
|
|
for _ in 0..<1000 {
|
|
await counter.increment()
|
|
}
|
|
}
|
|
|
|
let task2 = Task {
|
|
for _ in 0..<1000 {
|
|
await counter.increment()
|
|
}
|
|
}
|
|
|
|
// Wait for both tasks to complete
|
|
await task1.value
|
|
await task2.value
|
|
|
|
// Get the final count
|
|
let finalCount = await counter.getCount()
|
|
print("Final count: \(finalCount)") // Should print 2000
|
|
|
|
// Example of concurrent access to get count
|
|
let task3 = Task {
|
|
await counter.increment()
|
|
let count1 = await counter.getCount()
|
|
print("Count after increment: \(count1)")
|
|
}
|
|
|
|
let task4 = Task {
|
|
await counter.increment()
|
|
let count2 = await counter.getCount()
|
|
print("Count after another increment: \(count2)")
|
|
}
|
|
|
|
await task3.value
|
|
await task4.value
|
|
}
|
|
|
|
// Run the example
|
|
Task {
|
|
await demonstrateSafeCounter()
|
|
}
|
|
```
|
|
|
|
Key features of this implementation:
|
|
|
|
1. **Actor Declaration**: The `SafeCounter` actor ensures thread safety by serializing all access to its properties and methods.
|
|
|
|
2. **Private Property**: The `count` property is private, so it can only be accessed within the actor's context.
|
|
|
|
3. **Async Methods**: Both `increment()` and `getCount()` are `async` methods that can be called from other tasks.
|
|
|
|
4. **Thread Safety**: Since actors are inherently thread-safe, concurrent access to the counter from multiple tasks is safe without additional synchronization mechanisms.
|
|
|
|
5. **Usage Example**: The example demonstrates:
|
|
- Creating multiple concurrent tasks that increment the counter
|
|
- Using `await` to call actor methods
|
|
- Ensuring proper synchronization with the actor's serialized execution model
|
|
|
|
The actor automatically handles the serialization of method calls, so even when multiple tasks call `increment()` or `getCount()` concurrently, the operations are executed one at a time in the order they were received, making the counter completely thread-safe.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 25.51
|
|
* **Total Tokens:** 624
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 6: Create the code for a simple, non-configurable watchOS 26 complication that displays the current date in the `.accessoryRectangular` family.
|
|
|
|
*Source:* `test6.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Create the code for a simple, non-configurable watchOS 26 complication that displays the current date in the `.accessoryRectangular` family.
|
|
|
|
The code must include:
|
|
1. A `TimelineProvider` to generate the timeline entries.
|
|
2. A `TimelineEntry` struct.
|
|
3. The main `Widget` struct.
|
|
4. A SwiftUI view for the complication's appearance.
|
|
|
|
Ensure the code correctly uses the modern WidgetKit framework.
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's a complete implementation of a watchOS 26 complication that displays the current date in the `.accessoryRectangular` family:
|
|
|
|
```swift
|
|
import WidgetKit
|
|
import SwiftUI
|
|
|
|
// MARK: - Timeline Entry
|
|
struct DateComplicationEntry: TimelineEntry {
|
|
let date: Date
|
|
let dateString: String
|
|
|
|
init(date: Date) {
|
|
self.date = date
|
|
self.dateString = Self.formatDate(date)
|
|
}
|
|
|
|
private static func formatDate(_ date: Date) -> String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .medium
|
|
return formatter.string(from: date)
|
|
}
|
|
}
|
|
|
|
// MARK: - Timeline Provider
|
|
struct DateComplicationProvider: TimelineProvider {
|
|
func placeholder(in context: Context) -> DateComplicationEntry {
|
|
DateComplicationEntry(date: Date())
|
|
}
|
|
|
|
func getSnapshot(in context: Context, completion: @escaping (DateComplicationEntry) -> ()) {
|
|
let entry = DateComplicationEntry(date: Date())
|
|
completion(entry)
|
|
}
|
|
|
|
func getTimeline(in context: Context, completion: @escaping (Timeline<DateComplicationEntry>) -> ()) {
|
|
let currentDate = Date()
|
|
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
|
|
|
|
let entry = DateComplicationEntry(date: currentDate)
|
|
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
|
|
|
|
completion(timeline)
|
|
}
|
|
}
|
|
|
|
// MARK: - Widget
|
|
struct DateComplicationWidget: Widget {
|
|
let provider = DateComplicationProvider()
|
|
|
|
var body: some WidgetConfiguration {
|
|
StaticConfiguration(
|
|
kind: "DateComplicationWidget",
|
|
provider: provider
|
|
) { entry in
|
|
DateComplicationView(entry: entry)
|
|
}
|
|
.configurationDisplayName("Date Complication")
|
|
.description("Displays the current date")
|
|
.supportedFamilies([.accessoryRectangular])
|
|
}
|
|
}
|
|
|
|
// MARK: - View
|
|
struct DateComplicationView: View {
|
|
let entry: DateComplicationEntry
|
|
|
|
var body: some View {
|
|
VStack(spacing: 4) {
|
|
Text(entry.dateString)
|
|
.font(.system(size: 14, weight: .medium, design: .monospaced))
|
|
.foregroundColor(.white)
|
|
.lineLimit(1)
|
|
.truncationMode(.tail)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.background(Color.black)
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
struct DateComplicationWidget_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
DateComplicationView(entry: DateComplicationEntry(date: Date()))
|
|
.previewContext(WidgetPreviewContext(family: .accessoryRectangular))
|
|
}
|
|
}
|
|
```
|
|
|
|
This implementation includes:
|
|
|
|
1. **TimelineEntry** (`DateComplicationEntry`):
|
|
- Stores the current date and a formatted date string
|
|
- Includes a static method to format the date using `DateFormatter`
|
|
|
|
2. **TimelineProvider** (`DateComplicationProvider`):
|
|
- Implements all required methods: `placeholder`, `getSnapshot`, and `getTimeline`
|
|
- Updates every minute to ensure the date stays current
|
|
- Uses `.after` policy to schedule the next update
|
|
|
|
3. **Widget** (`DateComplicationWidget`):
|
|
- Configured for the `.accessoryRectangular` family
|
|
- Uses `StaticConfiguration` since it's a simple, non-configurable complication
|
|
- Includes proper configuration metadata
|
|
|
|
4. **SwiftUI View** (`DateComplicationView`):
|
|
- Displays the formatted date in a clean, monospaced font
|
|
- Uses a black background to match watchOS conventions
|
|
- Properly sized for the rectangular accessory family
|
|
- Handles text truncation for long dates
|
|
|
|
The complication will automatically update every minute and display the current date in a compact, readable format suitable for the watchOS accessory rectangular display.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 9.06
|
|
* **Total Tokens:** 937
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 7: Refactor this SwiftUI view and its `ObservableObject` to use the modern `@Observable` macro introduced in Swift 5.9. The refactored code should be simpler and should not use the `@Published` property wrapper or the `ObservableObject` protocol.
|
|
|
|
*Source:* `test7.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Refactor this SwiftUI view and its `ObservableObject` to use the modern `@Observable` macro introduced in Swift 5.9. The refactored code should be simpler and should not use the `@Published` property wrapper or the `ObservableObject` protocol.
|
|
|
|
**Old Code:**
|
|
class UserSettings: ObservableObject {
|
|
@Published var score: Int = 0
|
|
@Published var username: String = "Guest"
|
|
}
|
|
|
|
struct SettingsView: View {
|
|
@StateObject private var settings = UserSettings()
|
|
var body: some View {
|
|
Form {
|
|
TextField("Username", text: $settings.username)
|
|
Stepper("Score: \(settings.score)", value: $settings.score)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's the refactored code using the modern `@Observable` macro:
|
|
|
|
```swift
|
|
import SwiftUI
|
|
|
|
@Observable
|
|
class UserSettings {
|
|
var score: Int = 0
|
|
var username: String = "Guest"
|
|
}
|
|
|
|
struct SettingsView: View {
|
|
@State private var settings = UserSettings()
|
|
|
|
var body: some View {
|
|
Form {
|
|
TextField("Username", text: $settings.username)
|
|
Stepper("Score: \(settings.score)", value: $settings.score)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key changes made:**
|
|
|
|
1. **Removed `@Published`** - Properties are now directly declared without the `@Published` wrapper
|
|
2. **Removed `ObservableObject` protocol** - No longer needed with `@Observable`
|
|
3. **Simplified `@StateObject` to `@State`** - Since we're using `@Observable` instead of `@Published`, we can use `@State` directly
|
|
4. **Cleaner class declaration** - Just the `@Observable` macro and regular property declarations
|
|
|
|
The `@Observable` macro automatically handles the observation mechanism, making the code much simpler and more concise while maintaining the same functionality. The view will automatically update when any observed properties change, just like with the previous `@Published` approach.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 25.25
|
|
* **Total Tokens:** 423
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 8: Create the code for a simple, configurable WidgetKit widget for iOS 26. The widget should display a single "Note" string. The configuration logic must use the modern App Intents framework, allowing the user to edit the note's text directly from the widget's configuration screen. Do not use the old `INIntent` based system.
|
|
|
|
*Source:* `test8.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Create the code for a simple, configurable WidgetKit widget for iOS 26. The widget should display a single "Note" string. The configuration logic must use the modern App Intents framework, allowing the user to edit the note's text directly from the widget's configuration screen. Do not use the old `INIntent` based system.
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's a complete implementation of a configurable WidgetKit widget using the modern App Intents framework for iOS 26:
|
|
|
|
```swift
|
|
// MARK: - Widget Entry
|
|
import WidgetKit
|
|
import SwiftUI
|
|
|
|
struct NoteWidgetEntry: TimelineEntry {
|
|
let date: Date
|
|
let note: String
|
|
}
|
|
|
|
// MARK: - Widget Provider
|
|
struct NoteWidgetProvider: AppIntentTimelineProvider {
|
|
func placeholder(in context: Context) -> NoteWidgetEntry {
|
|
NoteWidgetEntry(date: Date(), note: "Sample Note")
|
|
}
|
|
|
|
func snapshot(for configuration: NoteWidgetConfigurationIntent, in context: Context) async -> NoteWidgetEntry {
|
|
NoteWidgetEntry(date: Date(), note: configuration.note ?? "Default Note")
|
|
}
|
|
|
|
func timeline(for configuration: NoteWidgetConfigurationIntent, in context: Context) async -> Timeline<NoteWidgetEntry> {
|
|
let entry = NoteWidgetEntry(date: Date(), note: configuration.note ?? "Default Note")
|
|
return Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(3600)))
|
|
}
|
|
}
|
|
|
|
// MARK: - Widget View
|
|
struct NoteWidgetEntryView: View {
|
|
var entry: NoteWidgetEntry
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(entry.note)
|
|
.font(.headline)
|
|
.multilineTextAlignment(.leading)
|
|
.padding(.horizontal)
|
|
.padding(.vertical, 4)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
.background(Color(.systemBackground))
|
|
}
|
|
}
|
|
|
|
// MARK: - Widget Definition
|
|
struct NoteWidget: Widget {
|
|
let kind = "NoteWidget"
|
|
|
|
var body: some WidgetConfiguration {
|
|
AppIntentConfiguration(
|
|
kind: kind,
|
|
intent: NoteWidgetConfigurationIntent.self,
|
|
provider: NoteWidgetProvider()
|
|
) { entry in
|
|
NoteWidgetEntryView(entry: entry)
|
|
}
|
|
.configurationDisplayName("Note Widget")
|
|
.description("Displays a configurable note")
|
|
}
|
|
}
|
|
|
|
// MARK: - Configuration Intent
|
|
struct NoteWidgetConfigurationIntent: WidgetConfigurationIntent {
|
|
@Parameter(title: "Note", description: "The note text to display", default: "Default Note")
|
|
var note: String?
|
|
}
|
|
|
|
// MARK: - Widget Bundle
|
|
import WidgetKit
|
|
import SwiftUI
|
|
|
|
@main
|
|
struct NoteWidgetBundle: WidgetBundle {
|
|
var body: some Widget {
|
|
NoteWidget()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Key Features:
|
|
|
|
1. **Modern App Intents Framework**:
|
|
- Uses `AppIntentConfiguration` instead of the deprecated `IntentConfiguration`
|
|
- Implements `WidgetConfigurationIntent` with `@Parameter` for configuration
|
|
- No `INIntent` usage
|
|
|
|
2. **Configuration**:
|
|
- Users can edit note text directly in widget configuration
|
|
- Default value set to "Default Note"
|
|
- Text parameter with description
|
|
|
|
3. **Widget Functionality**:
|
|
- Displays note text in a clean, readable format
|
|
- Uses system background for proper appearance
|
|
- Responsive layout that adapts to widget size
|
|
|
|
4. **Timeline Management**:
|
|
- Provides placeholder and snapshot entries
|
|
- Generates timeline entries with 1-hour refresh policy
|
|
- Uses current date for entry timestamp
|
|
|
|
5. **Configuration Intent**:
|
|
- `note` parameter with optional string type
|
|
- Proper parameter attributes for configuration UI
|
|
- Default value handling
|
|
|
|
### Setup Instructions:
|
|
|
|
1. Add this widget to your iOS app target
|
|
2. Enable "Widget Extension" capability in your app target
|
|
3. Add the widget to your app's Info.plist under "NSUserActivityTypes" if needed
|
|
4. The widget will appear in the widget configuration screen with a "Note" parameter
|
|
|
|
### Usage:
|
|
1. Add widget to home screen
|
|
2. Tap "Edit" on widget
|
|
3. Tap on the note text field to edit
|
|
4. Save changes to update the widget
|
|
|
|
The widget will automatically refresh every hour or when the system determines it's appropriate. The note text is fully configurable through the modern App Intents interface without any legacy INIntent dependencies.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 25.91
|
|
* **Total Tokens:** 929
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 9: Write a complete SwiftUI view for visionOS 26 that presents a fully immersive space when a button is tapped.
|
|
|
|
*Source:* `test9.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Write a complete SwiftUI view for visionOS 26 that presents a fully immersive space when a button is tapped.
|
|
|
|
The view must:
|
|
1. Have a button labeled "Enter Immersive Space".
|
|
2. Use the `@Environment(\.openImmersiveSpace)` and `@Environment(\.dismissImmersiveSpace)` variables.
|
|
3. Be able to open an immersive space with the ID "MyImmersiveScene".
|
|
4. Include a second button inside the immersive space to dismiss it.
|
|
```
|
|
|
|
### Response:
|
|
|
|
Here's a complete SwiftUI view for visionOS 26 that creates a fully immersive space when the button is tapped:
|
|
|
|
```swift
|
|
import SwiftUI
|
|
|
|
struct ContentView: View {
|
|
@Environment(\.openImmersiveSpace) private var openImmersiveSpace
|
|
@Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
|
|
|
|
var body: some View {
|
|
VStack(spacing: 20) {
|
|
Text("Welcome to Immersive Space")
|
|
.font(.largeTitle)
|
|
.fontWeight(.bold)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Text("Experience the wonders of 3D space")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Spacer()
|
|
|
|
Button(action: {
|
|
openImmersiveSpace(id: "MyImmersiveScene")
|
|
}) {
|
|
Text("Enter Immersive Space")
|
|
.font(.title2)
|
|
.fontWeight(.semibold)
|
|
.padding(.horizontal, 30)
|
|
.padding(.vertical, 15)
|
|
.background(Color.blue)
|
|
.foregroundColor(.white)
|
|
.cornerRadius(12)
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
.padding()
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.background(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [.black, .blue.opacity(0.3)]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
struct ImmersiveSceneView: View {
|
|
@Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
// Background with gradient
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [.black, .purple, .blue]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
.ignoresSafeArea()
|
|
|
|
// Floating 3D elements
|
|
ForEach(0..<50) { _ in
|
|
Sphere()
|
|
.fill(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [.white, .blue, .purple]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
)
|
|
.frame(width: 20, height: 20)
|
|
.position(
|
|
x: CGFloat.random(in: 0...UIScreen.main.bounds.width),
|
|
y: CGFloat.random(in: 0...UIScreen.main.bounds.height)
|
|
)
|
|
.opacity(0.7)
|
|
}
|
|
|
|
// Center content
|
|
VStack(spacing: 30) {
|
|
Text("Immersive Space")
|
|
.font(.largeTitle)
|
|
.fontWeight(.bold)
|
|
.foregroundColor(.white)
|
|
.shadow(radius: 10)
|
|
|
|
Text("You are now in a fully immersive 3D environment")
|
|
.font(.subheadline)
|
|
.foregroundColor(.white.opacity(0.8))
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal)
|
|
|
|
Spacer()
|
|
|
|
Button(action: {
|
|
dismissImmersiveSpace()
|
|
}) {
|
|
Text("Exit Immersive Space")
|
|
.font(.title3)
|
|
.fontWeight(.semibold)
|
|
.padding(.horizontal, 30)
|
|
.padding(.vertical, 15)
|
|
.background(Color.white.opacity(0.2))
|
|
.foregroundColor(.white)
|
|
.cornerRadius(12)
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.tint(Color.white)
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Preview provider for visionOS
|
|
#Preview(as: .immersiveSpace) {
|
|
ImmersiveSceneView()
|
|
}
|
|
```
|
|
|
|
This implementation includes:
|
|
|
|
1. **Main Content View**:
|
|
- A "Enter Immersive Space" button with a modern design
|
|
- Gradient background for visual appeal
|
|
- Responsive layout that works in visionOS
|
|
|
|
2. **Immersive Scene View**:
|
|
- Full-screen immersive experience with gradient background
|
|
- Floating 3D spheres that create a sense of depth and space
|
|
- Centered content with clear instructions
|
|
- "Exit Immersive Space" button to return to the main view
|
|
- Proper styling with shadows and transparency effects
|
|
|
|
3. **Environment Variables**:
|
|
- Uses `@Environment(\.openImmersiveSpace)` to open the immersive space
|
|
- Uses `@Environment(\.dismissImmersiveSpace)` to close it
|
|
- Opens with ID "MyImmersiveScene" as requested
|
|
|
|
4. **visionOS Specific Features**:
|
|
- Uses `#Preview(as: .immersiveSpace)` for proper preview
|
|
- Responsive design that works in immersive space
|
|
- Visual elements that take advantage of 3D space
|
|
|
|
To use this in your visionOS app:
|
|
1. Add the `ContentView` as your main view
|
|
2. The immersive scene will be triggered when the button is tapped
|
|
3. The exit button in the immersive scene will return to the main view
|
|
|
|
The immersive scene features a space-like environment with floating spheres and a dark gradient background to create a sense of depth and wonder.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 2.23
|
|
* **Total Tokens:** 1190
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|
|
## Test 10: Create a custom SwiftUI Layout container called `SimpleFlowLayout`.
|
|
|
|
*Source:* `test10.txt`
|
|
|
|
### Prompt:
|
|
```swift
|
|
Create a custom SwiftUI Layout container called `SimpleFlowLayout`.
|
|
|
|
This layout should arrange its subviews in a horizontal flow, starting a new row when the current row is full. It should behave similarly to a basic left-aligned flow layout in CSS.
|
|
|
|
The implementation must conform to the `Layout` protocol and implement both the `sizeThatFits(...)` and `placeSubviews(...)` methods.
|
|
```
|
|
|
|
### Response:
|
|
|
|
```swift
|
|
import SwiftUI
|
|
|
|
struct SimpleFlowLayout: Layout {
|
|
private let spacing: CGFloat
|
|
|
|
init(spacing: CGFloat = 8) {
|
|
self.spacing = spacing
|
|
}
|
|
|
|
func sizeThatFits(
|
|
proposal: Proposal,
|
|
subviews: Subviews
|
|
) -> CGSize {
|
|
guard !subviews.isEmpty else {
|
|
return .zero
|
|
}
|
|
|
|
var totalHeight: CGFloat = 0
|
|
var currentRowHeight: CGFloat = 0
|
|
var currentRowWidth: CGFloat = 0
|
|
var rowSpacing: CGFloat = 0
|
|
|
|
for (index, subview) in subviews.enumerated() {
|
|
let subviewSize = subview.sizeThatFits(Proposal(
|
|
dimensions: proposal.dimensions,
|
|
spacing: spacing
|
|
))
|
|
|
|
// If this is not the first item and adding it would exceed the width,
|
|
// start a new row
|
|
if index > 0 && currentRowWidth + spacing + subviewSize.width > proposal.dimensions.width {
|
|
totalHeight += currentRowHeight + rowSpacing
|
|
currentRowWidth = 0
|
|
currentRowHeight = 0
|
|
rowSpacing = spacing
|
|
}
|
|
|
|
// Update current row dimensions
|
|
currentRowWidth += (index > 0 ? spacing : 0) + subviewSize.width
|
|
currentRowHeight = max(currentRowHeight, subviewSize.height)
|
|
}
|
|
|
|
// Add the last row
|
|
totalHeight += currentRowHeight
|
|
|
|
return CGSize(
|
|
width: proposal.dimensions.width,
|
|
height: totalHeight
|
|
)
|
|
}
|
|
|
|
func placeSubviews(
|
|
in bounds: CGRect,
|
|
proposal: Proposal,
|
|
subviews: Subviews
|
|
) {
|
|
guard !subviews.isEmpty else {
|
|
return
|
|
}
|
|
|
|
var currentX: CGFloat = 0
|
|
var currentY: CGFloat = 0
|
|
var currentRowHeight: CGFloat = 0
|
|
var rowSpacing: CGFloat = 0
|
|
var rowWidth: CGFloat = 0
|
|
var isFirstItemInRow = true
|
|
|
|
for (index, subview) in subviews.enumerated() {
|
|
let subviewSize = subview.sizeThatFits(Proposal(
|
|
dimensions: proposal.dimensions,
|
|
spacing: spacing
|
|
))
|
|
|
|
// Check if we need to start a new row
|
|
if !isFirstItemInRow && currentX + spacing + subviewSize.width > bounds.width {
|
|
// Move to next row
|
|
currentY += currentRowHeight + rowSpacing
|
|
currentX = 0
|
|
currentRowHeight = 0
|
|
rowSpacing = spacing
|
|
isFirstItemInRow = true
|
|
}
|
|
|
|
// Position the subview
|
|
let x = currentX + (isFirstItemInRow ? 0 : spacing)
|
|
let y = currentY
|
|
|
|
subview.place(
|
|
at: CGPoint(x: x, y: y),
|
|
proposal: Proposal(
|
|
dimensions: CGSize(width: subviewSize.width, height: subviewSize.height),
|
|
spacing: spacing
|
|
)
|
|
)
|
|
|
|
// Update tracking variables
|
|
currentX = x + subviewSize.width
|
|
currentRowHeight = max(currentRowHeight, subviewSize.height)
|
|
isFirstItemInRow = false
|
|
rowWidth = max(rowWidth, currentX)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extension to make it easier to use with different spacing values
|
|
extension SimpleFlowLayout {
|
|
init() {
|
|
self.init(spacing: 8)
|
|
}
|
|
}
|
|
|
|
// Example usage:
|
|
struct ContentView: View {
|
|
var body: some View {
|
|
SimpleFlowLayout(spacing: 12) {
|
|
ForEach(0..<20) { index in
|
|
Text("Item \(index)")
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(Color.blue.opacity(0.2))
|
|
.cornerRadius(4)
|
|
}
|
|
}
|
|
.frame(width: 300, height: 300)
|
|
.border(Color.gray)
|
|
}
|
|
}
|
|
|
|
// For testing purposes, here's a simple example of how to use it:
|
|
struct SimpleFlowLayout_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
ScrollView {
|
|
VStack(spacing: 20) {
|
|
Text("Simple Flow Layout Example")
|
|
.font(.headline)
|
|
|
|
SimpleFlowLayout(spacing: 8) {
|
|
ForEach(0..<15) { index in
|
|
Text("Item \(index)")
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 6)
|
|
.background(Color.blue.opacity(0.1))
|
|
.cornerRadius(8)
|
|
}
|
|
}
|
|
.frame(width: 250)
|
|
.border(Color.gray)
|
|
|
|
SimpleFlowLayout(spacing: 16) {
|
|
ForEach(0..<8) { index in
|
|
Text("Item \(index)")
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 8)
|
|
.background(Color.green.opacity(0.1))
|
|
.cornerRadius(12)
|
|
}
|
|
}
|
|
.frame(width: 300)
|
|
.border(Color.gray)
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
This implementation of `SimpleFlowLayout` provides:
|
|
|
|
1. **Horizontal Flow Layout**: Items are arranged horizontally and wrap to new rows when they exceed the available width.
|
|
|
|
2. **Customizable Spacing**: The `spacing` parameter controls the spacing between items and rows.
|
|
|
|
3. **Proper Sizing**: The `sizeThatFits` method calculates the total required size based on the content and layout constraints.
|
|
|
|
4. **Correct Placement**: The `placeSubviews` method positions each subview correctly within the available bounds.
|
|
|
|
5. **Row Management**: The layout properly handles row transitions when items don't fit in the current row.
|
|
|
|
Key features:
|
|
- Starts a new row when the current row is full
|
|
- Left-aligns items in each row
|
|
- Handles variable item sizes
|
|
- Supports customizable spacing
|
|
- Conforms to SwiftUI's Layout protocol
|
|
- Works with any number of subviews
|
|
|
|
The layout will automatically wrap items to new rows when they exceed the available width, similar to CSS's `flex-wrap: wrap` behavior.
|
|
|
|
### Performance Metrics:
|
|
* **Tokens/s:** 24.84
|
|
* **Total Tokens:** 1386
|
|
* **Time to First Token:** 0s
|
|
* **Stop Reason:** stop
|
|
|
|
---
|
|
|