Cannot convert value of type 'Notifications.Type' to expected argument type 'Notifications' - swift

I trying to integreat firebase with my program to make true or false but I keep get the error
Cannot convert value of type 'Notifications.Type' to expected argument type 'Notifications'
Here is my code
import SwiftUI
struct ProfileHost: View {
#Environment(\.editMode) var editMode
#EnvironmentObject var modelData: ModelData
#State private var draftProfile = Profile.default
#State private var notifications = Notifications(id: "", prefersNotifications: true)
var body: some View {
VStack(alignment: .leading, spacing: 20) {
HStack {
if editMode?.wrappedValue == .active {
Button("Cancel", role: .cancel) {
draftProfile = modelData.profile
notifications = modelData.notifications
editMode?.animation().wrappedValue = .inactive
}
}
Spacer()
EditButton()
}
if editMode?.wrappedValue == .inactive {
ProfileSummary(profile: modelData.profile, notifications: modelData.notifications)
} else {
ProfileEditor(profile: $draftProfile, notifications: $notifications)
.onAppear {
draftProfile = modelData.profile
}
.onDisappear {
modelData.profile = draftProfile
}
}
}
.padding()
}
}
struct ProfileHost_Previews: PreviewProvider {
static var previews: some View {
ProfileHost()
.environmentObject(ModelData())
}
}
Modeldata is the file I made the notifications var
This the code of modeldata
import Foundation
import Combine
final class ModelData: ObservableObject {
#Published var landmarks: [Landmark] = load("landmarkData.json")
var hikes: [Hike] = load("hikeData.json")
#Published var profile = Profile.default
#Published var notifications = Notifications.self
var features: [Landmark] {
landmarks.filter { $0.isFeatured }
}
var categories: [String: [Landmark]] {
Dictionary(
grouping: landmarks,
by: { $0.category.rawValue }
)
}
}
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
at
if editMode?.wrappedValue == .inactive {
ProfileSummary(profile: modelData.profile, notifications: modelData.notifications)
I get an error that says.
Cannot convert value of type 'Notifications.Type' to expected argument
type 'Notifications'
I tried to look at other wedsites like github and reddit

Related

Refresh remotely loaded image in SwiftUI

It's a lot of code and looks daunting, but it's pretty simple--I'm trying to load remote image, and when the image is clicked, I'd like to switch to the next image:
struct TestView: View {
#State var selectedIndex: Int = 0
#State var arrayOfImages: [String] = ["https://s3-media3.fl.yelpcdn.com/bphoto/_0bkRz0wln3URHevWORCkA/o.jpg", "https://s3-media2.fl.yelpcdn.com/bphoto/MDZXc4pDt5xUfXF0Rw6rMw/o.jpg", "https://s3-media3.fl.yelpcdn.com/bphoto/feYg35an2MilNK3dCwwqTQ/o.jpg"]
var body: some View {
RemoteImage(url: arrayOfImages[selectedIndex])
.scaledToFill()
.frame(width: 200, height: 200)
.clipped()
.onTapGesture {
selectedIndex += 1
}
}
}
struct RemoteImage: View {
private enum LoadState {
case loading, success, failure
}
private class Loader: ObservableObject {
var data = Data()
var state = LoadState.loading
init(url: String) {
guard let parsedURL = URL(string: url) else {
fatalError("Invalid URL: \(url)")
}
URLSession.shared.dataTask(with: parsedURL) { data, response, error in
if let data = data, data.count > 0 {
self.data = data
self.state = .success
} else {
self.state = .failure
}
DispatchQueue.main.async {
self.objectWillChange.send()
}
}.resume()
}
}
#StateObject private var loader: Loader
var loading: Image
var failure: Image
var body: some View {
selectImage()
.resizable()
}
init(url: String, loading: Image = Image(""), failure: Image = Image(systemName: "multiply.circle")) {
_loader = StateObject(wrappedValue: Loader(url: url))
self.loading = loading
self.failure = failure
}
private func selectImage() -> Image {
switch loader.state {
case .loading:
return loading
case .failure:
return failure
default:
if let image = UIImage(data: loader.data) {
return Image(uiImage: image)
} else {
return failure
}
}
}
}
Here's the problem: the image doesn't go to the next one when you tap on it. I think it's because the RemoteImage view isn't being reloaded, but I'm not sure how to fix. Any help is appreciated!
I think you are trying to do too much inside RemoteImage, in particular
declaring private #StateObject private var loader: Loader and all that derives from this.
Try this approach with #StateObject var loader = Loader() outside your RemoteImage.
Works for me.
struct ContentView: View {
var body: some View {
TestView()
}
}
struct TestView: View {
#StateObject var loader = Loader() // <-- here
#State var selectedIndex: Int = 0
#State var arrayOfImages: [String] = ["https://s3-media3.fl.yelpcdn.com/bphoto/_0bkRz0wln3URHevWORCkA/o.jpg", "https://s3-media2.fl.yelpcdn.com/bphoto/MDZXc4pDt5xUfXF0Rw6rMw/o.jpg", "https://s3-media3.fl.yelpcdn.com/bphoto/feYg35an2MilNK3dCwwqTQ/o.jpg"]
var body: some View {
RemoteImage(loader: loader) // <--- here
.scaledToFill()
.frame(width: 200, height: 200)
.clipped()
.onTapGesture {
selectedIndex += 1
if selectedIndex < arrayOfImages.count {
loader.load(url: arrayOfImages[selectedIndex]) // <-- here
} else {
//...
}
}
.onAppear {
loader.load(url: arrayOfImages[selectedIndex]) // <--- here
}
}
}
class Loader: ObservableObject {
var data = Data()
var state = LoadState.loading
func load(url: String) { // <--- here
guard let parsedURL = URL(string: url) else {
fatalError("Invalid URL: \(url)")
}
URLSession.shared.dataTask(with: parsedURL) { data, response, error in
if let data = data, data.count > 0 {
self.data = data
self.state = .success
} else {
self.state = .failure
}
DispatchQueue.main.async {
self.objectWillChange.send()
}
}.resume()
}
}
enum LoadState {
case loading, success, failure
}
struct RemoteImage: View {
#ObservedObject var loader: Loader // <--- here
var loading: Image = Image("")
var failure: Image = Image(systemName: "multiply.circle")
var body: some View {
selectImage().resizable()
}
private func selectImage() -> Image {
switch loader.state {
case .loading:
return loading
case .failure:
return failure
default:
if let image = UIImage(data: loader.data) {
return Image(uiImage: image)
} else {
return failure
}
}
}
}

How to initialize a class using that is using #Binding for an #AppStorage variable

// I am trying to pass in the value of the #AppStorage onto the class using #Binding.
// Declaration of #AppStorge, this the the single source of truth.
// I get the following error on the SigninViewModel = Class 'SigninViewModel' has no initializers
import SwiftUI
struct MainView: View {
#AppStorage("userSignIn") var userSignIn = false
var body: some View {
ZStack {
TabView {
AppointmentsView()
.tabItem {
Image(systemName: "calendar")
Text("Appointments")
}
AccountView(userSignIn: .constant(true))
.tabItem {
Image(systemName: "person")
Text("Profile")
}
StatsView()
.tabItem {
Image(systemName: "chart.bar")
Text("Stats")
}
}
}
}
}
// This the class with the #Binding is being declared and where I am trying to have access to the #AppStorae value.
class SigninViewModel: ObservableObject {
#Published var nonce = ""
#Binding var userSignIn: Bool
func authenticate(credential: ASAuthorizationAppleIDCredential) {
// getting Token...
guard let token = credential.identityToken else {
print("Error with Firebase")
return
}
//Token String...
guard let tokenString = String(data: token, encoding: .utf8) else {
print("Error with Token")
return
}
let firebaseCredential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: tokenString,
rawNonce: nonce)
Auth.auth().signIn(with: firebaseCredential) { (result, err) in
if let error = err {
print(error.localizedDescription)
return
}
// User succesfully logged into Firebase...
print("Logged in Success")
// Directing user to Main page...
withAnimation(.easeInOut) {
self.userSignIn = true
}
}
}
}
You can put the #AppStorage("userSignIn") inside the ObservableObject, but your code quickly becomes a mess.
I think a more simple structure would be to put all of your authentication state inside a single AuthenticationModel: ObservableObject and use it as a #StateObject in the MainView. Additionally, I would recommend pushing it up the view tree.
class AuthenticationModel: ObservableObject {
#Published var nonce = ""
#Published var userSignIn: Bool
init() {
let defaults = UserDefaults.standard
userSignIn = defaults.bool(forKey: "userSignIn")
}
func authenticate(credential: ASAuthorizationAppleIDCredential)
...
// Successful Login
let defaults = UserDefaults.standard
defaults.set(true, forKey: "userSignIn")
// Update the views
userSignIn = true
}
}
struct MainView: View {
#StateObject var authModel: AuthenticationModel = AuthenticationModel()
var body: some View {
if authModel.userSignIn {
Home(authModel: authModel)
} else {
SignIn(authModel: authModel)
}
}
}
struct SignIn: View {
#ObservedObject var authModel: AuthenticationModel
var body: some View {
Button("Sign In") {
authModel.authenticate(...)
}
}
}

SWIFTUI Observable Object Data Task only runs once?

I have an observable object class that downloads an image from a url to display:
class ImageLoader : ObservableObject {
var didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}
init(urlString:String){
guard let url = URL(string: urlString) else {return}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
print("imageloader1")
}
}
task.resume()
}
and I show it using:
struct ShowImage1: View {
#ObservedObject var imageLoader:ImageLoader
#State var image:UIImage = UIImage()
init(withURL url:String) {
imageLoader = ImageLoader(urlString:url)
}
var body: some View {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.edgesIgnoringSafeArea(.top)
.onReceive(imageLoader.didChange) {
data in self.image = UIImage(data: data) ?? UIImage()
}
}
The problem I'm having is this is only capable of running once, If i click off the ShowImage1 view and then click back on to it, ImageLoader doesn't run again, and I'm left with a blank page.
How can I ensure that ImageLoader Runs every time the ShowImage1 view is accessed?
EDIT:
I access ShowImage1 like this:
struct PostCallForm: View {
var body: some View {
NavigationView {
Form {
Section {
Button(action: {
if true {
self.showImage1 = true
}
}){
Text("View Camera 1 Snapshot")
}.overlay(NavigationLink(destination: ShowImage1(withURL: "example.com/1.jpg"), isActive: self.$showImage1, label: {
EmptyView()
}))
}
}
Section {
Button(action: {
}){
Text("Submit")
}
}
}.disabled(!submission.isValid)
}
}
}
import SwiftUI
import Combine
class ImageLoader : ObservableObject {
var didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}
func loadImage(urlString:String) {
guard let url = URL(string: urlString) else {return}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
print("imageloader1")
}
}
task.resume()
}
}
struct ShowImage1Parent: View {
#State var url: String = ""
var sampleURLs: [String] = ["https://image.shutterstock.com/image-vector/click-here-stamp-square-grunge-600w-1510095275.jpg", "https://image.shutterstock.com/image-vector/certified-rubber-stamp-red-grunge-600w-1423389728.jpg", "https://image.shutterstock.com/image-vector/sample-stamp-square-grunge-sign-600w-1474408826.jpg" ]
var body: some View {
VStack{
Button("load-image", action: {
url = sampleURLs.randomElement()!
})
ShowImage1(url: $url)
}
}
}
struct ShowImage1: View {
#StateObject var imageLoader:ImageLoader = ImageLoader()
#State var image:UIImage = UIImage()
#Binding var url: String
var body: some View {
VStack{
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.edgesIgnoringSafeArea(.top)
.onReceive(imageLoader.didChange) {
data in self.image = UIImage(data: data) ?? UIImage()
}
.onChange(of: url, perform: { value in
imageLoader.loadImage(urlString: value)
})
}
}
}

SwiftUI - Not able to render the jsonData

I am very new to SwiftUi and I am trying to view the json data and I am currently working on retreiving the weather data from the openweathermap.org which is a free api to retrieve current weather. I am getting Error parsing Weather Json message. I am not sure what I am doing wrong!! Any help would be greatly appreciated and I have been stuck on this for a day. I referred many blogs and tutorials on how to use the Published var and ObservableObject I am not able to fix the problem.
This is my swift file
struct WeatherData {
public var Id: Int
public var main: String
public var weather: [Weather]
public var icon: String
}
extension WeatherData: Decodable, Identifiable {
var id: Int {return Id}
}
struct WeatherView: View {
#ObservedObject var fetch = FetchWeather()
var body: some View {
VStack {
List(fetch.weatherData) {
wthr in
VStack(alignment: .leading){
Text("\(wthr.id)")
Text("\(wthr.weather[0].description)")
Text("\(wthr.icon)")
.font(.system(size:11))
.foregroundColor(Color.gray)
}
}
}
}
}
struct Weather: Decodable {
let description: String
}
struct WeatherView_Previews: PreviewProvider {
static var previews: some View {
WeatherView()
}
}
class FetchWeather: ObservableObject {
#Published var weatherData = [WeatherData] ()
init() {
load()
}
func load() {
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=London&appid=myapikey")!
URLSession.shared.dataTask(with: url) {
(data, response, error) in
do {
if let wthData = data {
let decodedData = try JSONDecoder().decode([WeatherData].self, from: wthData)
DispatchQueue.main.sync {
self.weatherData = decodedData
}
}
else {
print("No json Data available")
}
}catch {
print("Error parsing Weather Json")
}
}.resume()
}
}
Try this code. I have signed up to get the api and corrected the Model, ViewModel and View accordingly. I have not added the image loader for icon strings.
import SwiftUI
struct Weather: Decodable{
var description: String
var icon :String
}
struct MainData: Decodable {
var temp: Double
var pressure: Int
var humidity: Int
var temp_min: Double
var temp_max: Double
}
struct WeatherData: Decodable, Identifiable {
var id: Int
var main: MainData
var weather: [Weather]
var name: String
}
struct WeatherView: View {
#ObservedObject var fetch = FetchWeather()
var body: some View {
VStack(alignment: .leading) {
Text("Current Weather").font(.title).padding()
List(fetch.weatherData) { wthr in
HStack {
VStack(alignment: .leading){
Text("\(wthr.name)")
Text("\(wthr.weather[0].description)")
.font(.system(size:11))
.foregroundColor(Color.gray)
}
Spacer()
VStack(alignment: .trailing){
Text("\(wthr.main.temp-273.15, specifier: "%.1f") ÂșC")
}
Text("\(wthr.weather[0].icon)") // Image from "https://openweathermap.org/img/w/\(wthr.weather[0].icon).png"
.foregroundColor(Color.gray)
}
}
}
}
}
class FetchWeather: ObservableObject {
#Published var weatherData = [WeatherData]()
private let baseURL = "https://api.openweathermap.org/data/2.5/weather?q="
private let cities = [ "London", "Mumbai", "New+york", "Vatican+City" ]
private let api = "&appid="+"e44ebeb18c332fff46ab956bb38f9e07"
init() {
for city in self.cities {
self.load(self.baseURL+city+self.api)
}
}
func load(_ urlString: String) {
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
if let wthData = data {
let decodedData = try JSONDecoder().decode(WeatherData.self, from: wthData)
DispatchQueue.main.sync {
self.weatherData.append(decodedData)
}
}
else {
print("No json Data available")
}
} catch let error as NSError{
print(error.localizedDescription)
}
}.resume()
} else {
print("Unable to decode URL")
}
}
}

Entries from text field are not adding into list

import SwiftUI
import Firebase
import FirebaseFirestore
struct ContentView: View {
var body: some View {
customView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct customView : View{
#State var msg = ""
#ObservedObject var datas = observer()
var body : some View{
VStack{
List{
ForEach(datas.data){i in
Text(i.msg)
}
.onDelete { (index) in
let id = self.datas.data[index.first!].id
let db = Firestore.firestore().collection("msgs")
db.document(id).delete{(err) in
if err != nil{
print((err!.localizedDescription))
return
}
print("deleted Successfully !!!")
self.datas.data.remove(atOffsets: index)
}
}
}
HStack{
TextField("msg", text: $msg).textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
print(self.msg)
self.addData(msg1: self.msg)
}) {
Text("Add")
}.padding()
}.padding()
}
}
func addData(msg1:String){ 'Here is the code for additon'
let db = Firestore.firestore()
let msg = db.collection("msgs").document()
msg.setData(["id":msg.documentID,"msg": msg1]) { (err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
print("Success")
self.msg = ""
}
}
}
class observer : ObservableObject{
#Published var data = [datatype]()
init() {
let db = Firestore.firestore().collection("msg")
db.addSnapshotListener{(snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges{
if i.type == .added{
let msgData = datatype(id: i.document.documentID, msg: i.document.get("msg")
as! String)
self.data.append(msgData)
}
}
}
}
}
struct datatype : Identifiable {
var id : String
var msg : String
}
Here is the code for the CRUD(creation, reading, update, delete) using the firebase cloud service. When i enter the text in the text field and then hit button, it adds to firebase database, but doesn't shows up in the interface in the list of the app. Can anybody tell me where i am going wrong?
In this i am trying to add data to firebase, delete it ,read it and modify/update it. But the data entered doesn't shows up in interface.
Try to update data container explicitly on main thread, like below
if i.type == .added{
let msgData = datatype(id: i.document.documentID, msg: i.document.get("msg")
as! String)
DispatchQueue.main.async {
self.data.append(msgData)
}
}
as alternate try assignment instead of modification (however this should not be the case)
DispatchQueue.main.async {
self.data = self.data + [msgData]
}