DeveloperDanK
DeveloperDanK

Reputation: 5

Passing text between view and class SwiftUI

When a user pastes a hexcode into my textfield, I want my API function to take this hexcode and use it as a parameter in the API call. This means I have to share data from my View (containing the textfield) to my Class (containing API call). What is the best way to go about this? Appreciate the time and advice 🙏

View:

import SwiftUI

struct TestingText: View {
    
    @StateObject var fetch = fetchResults()
    @Binding var text: String
    
    var body: some View {
        
        VStack {
            
            TextField("Paste Clout Hexcode Here", text: $text)
                .font(.title2)
                .padding()
            
            Text(fetch.clout.postFound?.body ?? "n/a")
            
        }
    }
}

struct TestingText_Previews: PreviewProvider {
    static var previews: some View {
        TestingText(text: .constant("8004bb672ad3f46118775cd4b2cb5306c63f6d68787457990bf0d2fda3f7993a"))
    }
}

Class with API call:

class fetchResults: ObservableObject {
    
    @Published var clout = Cloutington()
    @Published var dataHasLoaded = false
    @State var postHashHex: String = "8004bb672ad3f46118775cd4b2cb5306c63f6d68787457990bf0d2fda3f7993a"
    
    
    init() {
        
        getData { clout in
            self.clout = clout
        }
        
    }
    
    private func getData(completion: @escaping (Cloutington) -> ()) {
        
        let parameters =  "{\r\n \"PostHashHex\": \"\(postHashHex)\"\r\n}"
        let postData = parameters.data(using: .utf8)
        var request =  URLRequest(url: URL(string: "https://bitclout.com/api/v0/get-single-post")!,timeoutInterval: Double.infinity)
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = postData
        request.httpMethod = "POST"
        
        let task =  URLSession.shared.dataTask(with: request) { (responseData, response, error) in

            print(error)
            print(response)
            print(responseData)
            
            if let resData = responseData {
                let decoder = JSONDecoder()
                
                do
                {
                    let finalData = try decoder.decode(Cloutington.self, from: resData)
                    DispatchQueue.main.async {

                        completion(finalData)
                        self.dataHasLoaded = true

                    }
                    
                }
                catch (let error)
                {
                    print(error)
                }

            }
            
        }
        task.resume()
        
    }
    
}

Model with JSON data:

import Foundation

struct Cloutington: Decodable {
    
    var postFound: PostFound?
    
    enum CodingKeys: String, CodingKey {
        case postFound = "PostFound"
        
    }
    
}

struct PostFound: Decodable {
    
    var id: String?
    var postHashHex, posterPublicKeyBase58Check, parentStakeID, body: String?
    var imageURLs: [String]?
    //    var recloutedPostEntryResponse: JSONNull?
    var creatorBasisPoints, stakeMultipleBasisPoints: Int?
    var timestampNanos: Double?
    var isHidden: Bool?
    var confirmationBlockHeight: Int?
    var inMempool: Bool?
    var profileEntryResponse: ProfileEntryResponse?
    var likeCount, diamondCount: Int?
    var isPinned: Bool?
    var commentCount, recloutCount: Int?
    var diamondsFromSender: Int?
    
    enum CodingKeys: String, CodingKey {
        case postHashHex = "PostHashHex"
        case posterPublicKeyBase58Check = "PosterPublicKeyBase58Check"
        case parentStakeID = "ParentStakeID"
        case body = "Body"
        case imageURLs = "ImageURLs"
        case creatorBasisPoints = "CreatorBasisPoints"
        case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
        case timestampNanos = "TimestampNanos"
        case isHidden = "IsHidden"
        case confirmationBlockHeight = "ConfirmationBlockHeight"
        case inMempool = "InMempool"
        case profileEntryResponse = "ProfileEntryResponse"
        case likeCount = "LikeCount"
        case diamondCount = "DiamondCount"
        case isPinned = "IsPinned"
        case commentCount = "CommentCount"
        case recloutCount = "RecloutCount"
        case diamondsFromSender = "DiamondsFromSender"
    }
}

// MARK: - ProfileEntryResponse
struct ProfileEntryResponse: Decodable {
    var publicKeyBase58Check, username, profileEntryResponseDescription, profilePic: String?
    var isHidden, isReserved, isVerified: Bool?
    var coinPriceBitCloutNanos, stakeMultipleBasisPoints: Int?
    
    enum CodingKeys: String, CodingKey {
        case publicKeyBase58Check = "PublicKeyBase58Check"
        case username = "Username"
        case profileEntryResponseDescription = "Description"
        case profilePic = "ProfilePic"
        case isHidden = "IsHidden"
        case isReserved = "IsReserved"
        case isVerified = "IsVerified"
        case coinPriceBitCloutNanos = "CoinPriceBitCloutNanos"
        case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
    }
}

Upvotes: 0

Views: 653

Answers (1)

Phil Dukhov
Phil Dukhov

Reputation: 87794

First of all, do not name classes in lowercase like fetchResults, also don't call a class like an action: every programmer expects fetchResults() to be a function call. Use something like ResultFetcher to name this class. And class instance usually will be lowercased class name, or part of it:

@StateObject var resultFetcher = ResultFetcher()
// or
@StateObject var fetcher = ResultFetcher()

You can only use @State inside SwiftUI views, you cannot use it inside ObservableObject. Use @Published as you already do with other variables if you need to update the view after updating that variable, or use an unannotated variable.

In your case, it doesn't look like you need to store this hash string; you can just pass it from the view.

You can use .onChange(of: text) to track changes to the @State or @Binding variables:

TextField("Paste Clout Hexcode Here", text: $text)
    .font(.title2)
    .padding()
    .onChange(of: text) { text in
        resultFetcher.updateData(postHashHex: text)
    }

Not sure if you need that initial call of updateData with hardcoded hex code, anyway your view model can be updated to this:

class ResultFetcher: ObservableObject {

    @Published var clout = Cloutington()
    @Published var dataHasLoaded = false
    private let initialPostHashHex: String = "8004bb672ad3f46118775cd4b2cb5306c63f6d68787457990bf0d2fda3f7993a"

    init() {
        updateData(postHashHex: initialPostHashHex)
    }
    
    func updateData(postHashHex: String) {
        getData(postHashHex: postHashHex) { clout in
            self.clout = clout
        }
    }

    private func getData(postHashHex: String, completion: @escaping (Cloutington) -> ()) {
        // your code
    }
}

Upvotes: 1

Related Questions