UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Use variable of one class in another class

Forums > SwiftUI

Hi all,

I'm halfway through 100daysOfSwiftUI and started working on my own app (maybe too early but learned a lot already by trying ;) I got following issue now :

I have a class with user preferences which can be stored in UserDefaults :

struct Prefs: Codable {
    var apiKey: String
   // some other stuff as well
}

class UserPreferences: ObservableObject {
    @Published var prefs: Prefs {
        didSet {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(prefs) {
                UserDefaults.standard.set(encoded, forKey: "Prefs")
            }
        }
    }

    init() {
        if let prefs = UserDefaults.standard.data(forKey: "Prefs") {
            let decoder = JSONDecoder()
            if let decoded = try? decoder.decode(Prefs.self, from: prefs) {
                self.prefs = decoded
                return
            }
        }

        self.prefs = Prefs(apiKey: "")
    }
}

I have a View that is shown in a sheet to fill in the User Preferences :

struct SheetView: View {

    @State private var apiKey: String = ""

    @ObservedObject var userPrefs: UserPreferences

    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack {

            Form {
                Section(header: Text("API key")) {
                    TextField("\(userPrefs.prefs.apiKey)", text: $apiKey)
                }
            }

            Button(action: {
                self.userPrefs.prefs = Prefs(apiKey: self.apiKey)
                self.presentationMode.wrappedValue.dismiss()
            })
            {
                Text("Save")
            }
        }
    }
}

My ContentView has 2 @ObservedObject variables : userPrefs and info. It also shows the sheet when a button is pressed :

struct ContentView: View {

    @ObservedObject var userPrefs = UserPreferences()
    @ObservedObject var info = InfoData()

 //...
                 .sheet(isPresented: $showingSheet) {
                    SheetView(userPrefs: self.userPrefs)
//...          

InfoData is another class where data is being read from an API. It has a base URL to access the API and I want to add the apiKey from the userPrefs to that base URL.

class InfoData: ObservableObject {
    private var baseURL = URL(string: "http://www.example.com/\(HERE I SHOULD HAVE MY APIKEY)")!

I fail to see however how I can read that apiKey value in that class... If it was a view instead of a class, I could use a @ObservedObject var userPrefs: UserPreferences in that View and do something like InfoData(userPrefs: self.userPrefs) in my ContentView. However, it's a class, not a View and if I try to write following in my ContentView, I get an error :

struct ContentView: View {

    @ObservedObject var userPrefs = UserPreferences()
    @ObservedObject var info = InfoData(prefs: userPrefs)

--> Cannot use instance member 'userPrefs' within property initializer; property initializers run before 'self' is available

So I'm not sure how I can use that apiKey from the UserPreferences in my InfoData class...

3      

Why is InfoData an ObservableObject? The baseURL does not change (except the key)! Make it a static var baseURL: String = "..." and when you need it you can add let info = InfoData.baseURL + userPrefs.prefs.apiKey in function that URL is called.

3      

sorry, i should have explained the InfoData better : it's an @ObservableObject class because it loads data from an api and provides that data (@Published variables) to the ContentView.

The function that calls the URL is a method in the InfoData class and the whole issue is exactly that i cannot access userPrefs.prefs.apiKey from there because that class doesnt know 'userPrefs'.

3      

Hi. Not quite sure what you mean! Are you trying to store the data in UserDefaults? Do you have the project on GitHub?

3      

Fixed it by looking through the code of Paul his ControlRoom app on Github : https://github.com/twostraws/ControlRoom

Made UserPreferences an @EnvironmentObject so its data can be shared with all Views and also with the InfoData class. This article gives more info : https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

Basically, in SceneDelegate.swift I added following :

    lazy var userPrefs: UserPreferences = UserPreferences()
    lazy var info: InfoData = InfoData(prefs: userPrefs)

and passed these as environmentObjects to ContentView. In any View I can now use :

@EnvironmentObject var userPrefs: UserPreferences

and in my InfoData class I can now finally use the userPrefs also :

class InfoData: ObservableObject {

    @ObservedObject var userPrefs: UserPreferences

        private var apiURL: URL {
                return URL(string: "https://www.example.com/key=\(userPrefs.prefs.apiKey)")!
        }

    init(prefs: UserPreferences) {
        self.userPrefs = prefs
        }

    func doSomeThingWithTheURL() {
        //load data from the apiURL
    }

If the user would change the apiKey in the preferences, this change is automatically visible in all the Views and in my InfoData class :)

Learned a lot researching this. Also looking at the code of ControlRoom teached me a lot (although it's way over my current level, it doesn't seem to be pure SwiftUI, also lot of UIKit (and Combine)).

3      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.