# Project export: Immerse

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Have you ever wanted to craft a world of your most vivid imaginations? Create one of your own with Immerse.
- Devpost: https://devpost.com/software/verse-w1sbrp
- GitHub: https://github.com/nourgajial26/TreeHacks2024
- Video: https://player.vimeo.com/video/914182528?byline=0&portrait=0&title=0#t=
- Team: 1 GitHub contributor(s) — Nour Gajial (3 commits)

## Devpost submission (written by the team)

### Inspiration

We're passionate about the endless opportunities spatial computing holds for the future. For our project, we wanted to create an environment for the Vision Pro where anyone's personal space is as limitless as their imagination. Our idea was inspired by the existing Vision Pro "Environments" which allows users to transform their physical surroundings into a completely different place, from snow-capped mountains to a sunset in the desert. With the click of your fingers, you can immerse yourself in the most beautiful parts of the world.

### What it does

Now imagine just that, but instead of choosing from a limited number of ~10 presets, you can generate infinite places to visit in the world. Here's how it works: you open Immerse, input a prompt such as "a verdant meadow with a beautiful sunset" and you'll automatically be transported into that scene. You will be immersed entirely in a world of your choosing, and you can personalize your prompt as you wish until you've created the perfect setting. For seniors, young people, and others of all ages, Immerse could improve mental health and meditation, boost creativity and memory, and increase imagination.

### How we built it

Immerse was built with SwiftUI, Vision OS SDK, and an API for the text-to-3D image generation.

### Challenges we ran into

None of us had prior knowledge of SwiftUI or Vision Pro hardware so this was an ambitious but exciting tackle for us.

### Accomplishments we're proud of

We're proud of creating an app that will inspire the imagination and creativity of people.

### What we learned

We learned how to code in a new language, with a new piece of hardware, in a team of new people. It was definitely a time of creative exploration and keeping an open mind.

### What's next

for Verse We hope to incorporate audio and make it even more immersive so that people can come to Immerse as a space to relax, meditate, and be endlessly creative in their own world.

## README (from the GitHub repository)

# TreeHacks2024
Immerse Vision Pro App for TreeHacks 2024

![immerse](https://github.com/nourgajial26/TreeHacks2024/assets/114798831/8fbfb04a-0d71-4397-b494-9824b6fd856f)


## Detected evidence (automated analysis)

Indexed codebase: 4 recognized source files, 8 KB.
- Swift (language) — detected in the code

## Codebase structure (from repository index)

### Files (5 of 5)

```
ContentView.swift
ImageViewModel.swift
Info.plist
README.md
VisionOS_APIApp.swift
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Update README.md
- Main Files
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### VisionOS_APIApp.swift

```swift
//
//  VisionOS_APIApp.swift
//  VisionOS_API
//
//  Created by Nour Gajial on 2/17/24.
//

import SwiftUI

@main
struct VisionOS_APIApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

```

### ImageViewModel.swift

```swift
import SwiftUI
import Foundation

class ImageViewModel: ObservableObject {
    @Published var image: Image? = nil

    // Function to load the image by initiating the generation and then fetching the image if successful
    func loadImage() async {
        do {
            // Replace `checkImgStatus` with your actual function to initiate and check the image status
            // Assume `checkImgStatus` returns an `ImageObj` with a status and optionally a fileUrl and errorMessage
            let imageObj = try await checkImgStatus() // Assuming this is your corrected function

            print("in loadImage rn")
            if imageObj.status == "complete", let urlString = imageObj.fileUrl {
                // Fetch and display the image
                await fetchImage(from: urlString)
            } else {
                // Handle error message if available, else print a generic message
                if let errorMessage = imageObj.errorMessage {
                    print("Error during image generation: \(errorMessage)")
                } else {
                    print("Image generation in progress or failed without an error message.")
                }
            }
        } catch {
            print("Error loading image: \(error)")
        }
    }

    // Function to fetch and update the UI with the image from the given URL
    private func fetchImage(from urlString: String) async {
        guard let url = URL(string: urlString) else {
            print("Invalid URL for image")
            return
        }
        print("in fetch rn")

        do {
            let (data, _) = try await URLSession.shared.data(from: url) // Only interested in data, not response
            guard let uiImage = UIImage(data: data) else {
                print("Failed to convert data into UIImage")
                return
            }
            // Update the UI on the main thread
            await MainActor.run {
                self.image = Image(uiImage: uiImage)
            }
        } catch {
            print("Error fetching image: \(error)")
        }
    }
}

```

### ContentView.swift

```swift
//
//  ContentView.swift
//  VisionOS_API
//
//  Created by Nour Gajial on 2/17/24.
//

import SwiftUI
import RealityKit
import Combine


struct ContentView: View {
    @StateObject private var viewModel = ImageViewModel()

    var body: some View {
        VStack {
            if let image = viewModel.image {
                image
                    .resizable()
                    .scaledToFit()
            } else {
                Text("Loading image...")
                    .padding()
            }
            
            Button(action: {
                Task {
                    await viewModel.loadImage()
                }
            }) {
                Text("Generate")
                    .padding()
                    .background(.black)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
        }
    }
}



import Foundation

enum ImgError: Error {
    case badURL, invalidResponse, invalidData, serverError(String), generationFailed(String), timeout
}

struct ImageObj: Codable {
    let status: String
    let fileUrl: String?
    let id: Int?
    let errorMessage: String?
}

struct ImageRequestStatus: Codable {
    var request:  ImageObj
}

// Function to initiate the skybox generation request and check its status
func checkImgStatus() async throws -> ImageObj {
    print("Firing off image request.")
    let imageObj = try await imgPostReq()
//    print("Initial status check")

    guard let requestId = imageObj.id else {
        throw ImgError.invalidData
    }
    print("got requestID")
    var currentStatus = imageObj.status

    // Polling interval in seconds
    let pollingInterval = 5.0
    // Timeout in seconds
    let timeoutInterval = 300.0
    var elapsedTime = 0.0
    print("before while loop")
    while currentStatus == "pending" || currentStatus == "dispatched" || currentStatus == "processing" {
        // Wait for the polling interval
        try await Task.sleep(nanoseconds: UInt64(pollingInterval * 1_000_000_000))
        elapsedTime += pollingInterval
        
        // Check for timeout
        if elapsedTime >= timeoutInterval {
            throw ImgError.timeout
        }
        print("after if statement")
        // Check the status again
        let newStatusObj = try await imgGetReq(requestId: requestId)
        currentStatus = newStatusObj.request.status
        
        print("Current status: \(currentStatus)")

        if currentStatus == "complete" {
            return newStatusObj.request
        } else if currentStatus == "abort" || currentStatus == "error" {
            if let errorMessage = newStatusObj.request.errorMessage {
                throw ImgError.generationFailed(errorMessage)
            } else {
                throw ImgError.generationFailed("Generation was aborted or encountered an error without a specific message.")
            }
        }
    }

    // If the loop exits because the status is 'complete', return the final status object
    return try await imgGetReq(requestId: requestId).request
}

// Function to make the POST request
func imgPostReq() async throws -> ImageObj {
    guard let url = URL(string: "https://backend.blockadelabs.com/api/v1/skybox") else {
        throw ImgError.badURL
    }
    print("in Post rn")
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("cOnB3oc5RWGLAWt57gO80v6o5tNGN3xoCWSeXC2bxvRhUvwytGCs4T8uKkcp", forHTTPHeaderField: "x-api-key")
    print("middle of forming request")
    let json = ["prompt": "A beach in Santa Monica magical"]
    guard let jsonData = try? JSONSerialization.data(withJSONObject: json) else {
        throw ImgError.invalidData
    }
    request.httpBody = jsonData
    print("get data/response")
    let (data, response) = try await URLSession.shared.data(for: request)
    
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        throw ImgError.invalidResponse
    }
    print("decoder")
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    print("converted to snakecase")
    let imageObj = try decoder.decode(ImageObj.self, from: data)
    print("about to return")
    print(imageObj)
    return imageObj
}

// Function to make the GET request
func imgGetReq(requestId: Int) async throws -> ImageRequestStatus {
    print("entered get")
    guard let url = URL(string: "https://backend.blockadelabs.com/api/v1/imagine/requests/\(requestId)") else {
        throw ImgError.badURL
    }
    print("in Get rn")
    var request = URLRequest(url: url)
    request.addValue("cOnB3oc5RWGLAWt57gO80v6o5tNGN3xoCWSeXC2bxvRhUvwytGCs4T8uKkcp", forHTTPHeaderField: "x-api-key")
    
    let (data, response) = try await URLSession.shared.data(for: request)
    
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        throw ImgError.invalidResponse
    }
    print(data)
    print("get decoder")
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let statusObj = try decoder.decode(ImageRequestStatus.self, from: data)
    print(statusObj)
    return statusObj
}

#Preview(windowStyle: .automatic) {
    ContentView()
}

```