Handle SwiftUI and CoreLocation with MVVM-Pattern
SwiftUI 2
Use instead StateObject
in this case
struct LocationView: View {
@StateObject var locationManager: LocationManager = LocationManager()
...
SwiftUI 1
Actually LocationViewModel
is redundant here. As your LocationManager
is a ObservableObject
you can use it directly in your view, as below:
struct LocationView: View {
@ObservedObject var locationManager: LocationManager = LocationManager()
var body: some View {
VStack(alignment: .leading) {
Text("Latitude: \(locationManager.location.coordinate.latitude.description)")
Text("Longitude: \(locationManager.location.coordinate.longitude.description)")
}
}
}
Pure MVVM with SwiftUI & CoreLocation
I would use a Service-Oriented architecture approach. You can have your LocationServiceProtocol
, implemented by a LocationService
class, that you can inject in the view model. This class has the responsibility to instantiate your Location
model and has the CLLocationManager
property. In this way you can handle the interaction between the high and lower level implementations.
Mapping a single response object with SwiftUI and MVVM pattern
Seems your UserProfileService().getProfile already return UserProfile type so you maybe need to
UserProfileService().getProfile { profile in
if let profile = profile {
self.profile = profile
}
}
and
@Published var profile : UserProfile?
SwiftUI problem with displaying values from array using MVVM pattern
Conform your model to Hashable
and use .self to identify it:
struct JSONModel: Codable, Hashable{
var name: String
var value: String
}
ForEach(viewModel.resultArray, id:\.self){ item in
Text(item.name)
}
Swift, fetching data in viewmodel and passing it to view
Your init()
is trying to run asynchronously and it's updating a @Published
property. Even if you manage to avoid compile errors, you cannot update a property that will change the state of your views (@Published
) unless you are on the main thread.
What I propose:
@Published var weather = Response() // Initialise this property in some way, the dummy values will be used by the app until you complete fetching the data
init(weather: Response) { // Remove async
Task { // Here, you enter in an async environment
let data = await fetchData() // Read the data and pass it to a constant
DispatchQueue.main.async { // Get on the main thread
self.weather = data // Here, change the state of you app
}
}
}
I hope this works, but it would be better if after "But I stuck with compile errors..." you showed what kind of errors you find. I tried to use my best guess with the solution above.
Using startUpdatingLocation() and stopUpdatingLocation() to activate/deactivate location updates in specific Views
Solution:
My mistake was trying to call locationViewModel.locationManager.startUpdatingLocation() method before the declaration of the views's body.
.navigationTitle("Simulation")
.navigationBarTitleDisplayMode(.inline)
.onAppear(perform: {locationViewModel.locationManager.startUpdatingLocation()})
.onDisappear(perform: {locationViewModel.locationManager.stopUpdatingLocation()})
SwiftUI get values of another ViewModel in a ViewModel
Here is possible approach
class FilterViewModel: ObservableObject, LoadProtocol {
@Published var placemarkViewModel = PlacemarkViewModel()
private var cancellable: AnyCancellable? = nil
init() {
cancellable = placemarkViewModel.$placemark
.sink { placemark in
// handle updated placemark here
}
}
}
How do I write a core location with MVVM architecture and perform unit testing?
You could create a LocationService
protocol that defines all methods (for instance fetchCurrentPosition
).
Then create a concrete class conforming to that protocol, that implements apple CoreLocation
services; this class will be used by the view model for example, and it will be injected in the initializer. Think something like:
class MyViewModel {
let locationService: LocationService
init(locationService: LocationService = LocationServiceImpl()) {
self.locationService = locationService
}
}
LocationServiceImpl
is the class conforming to LocationService
that actually uses apple CoreLocation
; in this way, you can later mock every function of your location service by creating for example LocationServiceMock
class that conforms to LocationService
; in your unit tests, you'll create a view model with the mocked service.
Related Topics
Correct Handling/Cleanup/Etc of Cadisplaylink in Swift Custom Animation
Watchos3 Complication That Launches App
Is It Possible in Swift to Add Variables to an Object at Runtime
Convert Timestamp String with Epochal Time and Timezone into Nsdate
Swift Programming Nserrorpointer Error etc
Swift Error: Guard Body Must Not Fall Through
Swift Property Observer in Protocol Extension
Get Just the Date (No Time) from Uidatepicker
Filter by Multiple Array Conditions
How to Make Apple Sign in Revoke Token Post Request
How to Get All Days in Current Week in Swift
Scenekit - Scntext Centering Incorrectly
How to Get User Input in Apple's Swift Language in a Command Line Tool
How to Reload a UI View's Content Swift
Spritekit Not Deallocating All Used Memory
Can Swift Playgrounds See Other Source Files in the Same Project
How to Combine Two Strings (Date & Time) into a New Date in Swift 3