0
votes

I have a View, in which the user is able to enter some text into a TextField. I want to be able to get the text, which was entered in the TextField and use this value inside of a struct. The concept of the app, is that it shows the elevation degree of the sun. To be able to do this, it is scraping the values from a WebPage. However to make this app dynamic, the user has to be able to edit the url (you can change location, date etc in the url). I thought this would be fairly easy, since I only have to get some text, and edit a url before the url is being loaded. I have been able to pass the value into a view, however I need it in a struct. Maybe the whole "layout of my code is wrong, maybe I should get the data and draw the function in a view? I don't know. This is my first time coding with swift.

I want to change the latitude var.

This is my code:

View 1 (Input):

class ViewModel: ObservableObject {
    @Published var latitude:String = ""
    @Published var page = 0
}

struct ContentView: View {
    @EnvironmentObject var value1: ViewModel
    
    var body: some View {
        if value1.page == 0{
            VStack{
            TextField("", text: $value1.latitude)
            Button(action:{ value1.page = 1}){
                Text("To next view")
            }.frame(width: 300, height: 100, alignment: .center)
        }
    } else {
        elevationGraph()
    }
}
}

View 2 (Graph)

struct getHtml { 
    var url = URL(string: "https://midcdmz.nrel.gov/apps/spa.pl?syear=2020&smonth=1&sday=1&eyear=2020&emonth=1&eday=1&otype=0&step=60&stepunit=1&hr=12&min=0&sec=0&latitude=\(latitude)&longitude=10.757933&timezone=1.0&elev=53&press=835&temp=10&dut1=0.0&deltat=64.797&azmrot=180&slope=0&refract=0.5667&field=0")
    
    func loadData(from url: URL?) -> String {
        guard let url = url else {
            return "nil"
        }

        let html = try! String(contentsOf: url, encoding: String.Encoding.utf8)
        
        return html
        
    }
}



struct elevationFunction: Shape {

var url: URL? //This only works in views, is there a way to do it in shape structs?
    
    let html = getHtml.init().loadData(from: getHtml.init().url)

    private func dbl1() -> Double {
        
        let leftSideOfTheValue = "0:00:00,"
        
        let rightSideOfTheValue = "\(month)/\(day)/\(year),1:00:00,"
        
        guard let leftRange = html.range(of: leftSideOfTheValue) else {
            print("cant find left range")
            return 0
        }
        
        guard let rightRange = html.range(of: rightSideOfTheValue) else {
            print("cant find right range")
            return 0
        }
        
        let rangeOfTheValue = leftRange.upperBound..<rightRange.lowerBound
        
        return Double(html[rangeOfTheValue].dropLast()) ?? 90
    }

    
    
    func path(in rect: CGRect) -> Path {

        var path = Path()
        
        path.move(to: CGPoint(x: 10, y: (125 - (90-dbl1()))))
        path.addLine(to: CGPoint(x: 120, y: (125 - (90-45))))
        path.addLine(to: CGPoint(x: 250, y: (125 - (90-dbl1()))))
    
        var scale = (rect.height / 350) * (9/10)
        var xOffset = (rect.width / 6)
        var yOffset = (rect.height / 2)
        
        return path.applying(CGAffineTransform(scaleX: scale, y: scale)).applying(CGAffineTransform(translationX: xOffset, y: yOffset))
        
        
    }
}

struct elevationGraph: View {
        var body: some View {
            GeometryReader { geometry in 
                ZStack {
                    elevationFunction().stroke(LinearGradient(gradient: Gradient(colors: [Color.yellow, Color.red]), startPoint: .top , endPoint: .bottom), style: StrokeStyle(lineWidth: 6.0)).aspectRatio(contentMode: .fill)
    
                }
            .frame(width: 600, height: 800, alignment: .center)
        }
    }
}
1
Not sure why you say that the URL only works in Views. You can pass it as a parameter to a Shape as well. In your code it would look like elevationFunction(url: yourURL) (PS -- common practice is to always capitalize your data types; eg classes, structs) -- ElevationFunction, or better ElevationShape - jnpdx
I have done it like that var url1: URL? int he second view, and passed it like you said, but now I get the error "Thread 1: EXC_BAD_ACCES (code=2, address=0x7ffee73ceff8)" on the line were I get the html'let html = getHtml.init().loadData(from: elevationFunction.init().url1) It doesn't show, before I run the program, but as soon, as I click next view, the program crashes and shows this error. - Rosenkrieg ykke

1 Answers

0
votes

As mentioned in my comment, you can pass a parameter to a Shape just like you can a regular View:

elevationFunction(url: yourURL)

Best practice would be to capitalize this and name it ...Shape as well: elevationFunction becomes ElevationShape

Regarding your second question in the comment, first, you may want to fix the naming of getHtml for the same reason as above -- uncapitalized, it looks like a variable name. Maybe something like DataLoader.

Regarding the crash, you have some circular logic going on -- you call getHtml.init() and then pass a parameter that is again derived from getHtml.init() again. Why not just call getHtml() and have it loadData from its own internal URL property?

There's a larger problem at work, though, which is that you've declared html as a let property on your Shape, which is going to get recreated every time your Shape is rendered. So, on every render, with your current code, you'll create 2 new getHtmls and attempt to load the data (which very well may not actually have time to load the URL request). This very well could be blocking the first render of the Shape as well and is almost certainly causing your crash somewhere in the circular and repetitive logic going on.

Instead, you might want to consider moving your URL request to onAppear or as part of an ObservableObject where you can have a little more control of when and how often this data gets loaded. Here's a good resource on learning more about loading data using URLSession and SwiftUI: https://www.hackingwithswift.com/books/ios-swiftui/sending-and-receiving-codable-data-with-urlsession-and-swiftui