I need to post a https request to login view (SwiftUI), my code follow I have in getres.swift:
so I neet to get value from response and put it in the text
import Foundation
import Combine
struct result: Decodable {
let res, ordercount, rate: String
}
class getres: ObservableObject {
let objectWillChange = PassthroughSubject<getres, Never>()
#Published var authenticated = ""
#Published var todos = [result]() {
didSet {
objectWillChange.send(self)
}
}
func auth(username: String, password: String) {
guard let url = URL(string: "http://company.com/auth.php") else { return }
let body: [String: String] = ["username": username, "password": password]
let finalBody = try! JSONSerialization.data(withJSONObject: body)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = finalBody
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data=data else{return}
let fineldata=try! JSONDecoder().decode(result.self, from: data)
DispatchQueue.main.async {
self.todos = [fineldata]
self.authenticated=fineldata.res
}
print(fineldata)
}.resume()
}
}
and in the login page I try to show other view
in this code I will get values from function as json response I will get ordercount and rate I put it in the other view
import SwiftUI
struct ContentView: View {
#State private var username: String = ""
#State private var password: String = ""
#ObservedObject var manager = getres()
var body: some View {
VStack(alignment: .leading) {
if manager.authenticated == "2"{
userdetails()
}else{
Text("Username")
TextField("placeholder", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
.autocapitalization(.none)
Text("Password")
SecureField("placeholder", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
Button(action: {
self.manager.auth(username: self.username, password: self.password)
}) {
HStack{
Spacer()
Text("Login")
Spacer()
}
.accentColor(Color.white)
.padding(.vertical, 10)
.background(Color.red)
.cornerRadius(5)
.padding(.horizontal, 40)
}
}.padding()}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and in userdetails this so I need to get values from response
struct userdetails: View {
#State var controller = getres()
var body : some View{
ScrollView(Axis.Set.vertical, showsIndicators: true) {
VStack(spacing: 20){
Image("wlogo").renderingMode(.original); HStack(spacing: 15){
Spacer()
VStack(alignment: .center, spacing: 10) {
Text(???).foregroundColor(Color.white).bold()
.font(.largeTitle)
Text("")
.foregroundColor(Color.white)
.font(.headline) }
Spacer()
}}}}
how can I get ordercount from response and put in
text(???)
in the view userdetails
for example controller.todos.ordercount
I get this error
Value of type '[result]' has no member 'ordercount'
when I try
Text(controller.todos.ordercount)
json response
{"res":"2","ordercount":"20","rate":"5"}
UPDATED ANSWER
1.) it would be helpful if you copy your code in just one part - this is easier for all of us to copy
2.) you should try out your code yourself before you copy or change something in your code manually. it is annoying to find mistakes like:
if manager.authenticated == "2"{
userdetails()
} else{
Text("Username")
TextField("placeholder", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
.autocapitalization(.none)
Text("Password")
SecureField("placeholder", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
Button(action: {
self.manager.auth(username: self.username, password: self.password)
}) {
HStack{
Spacer()
Text("Login")
Spacer()
}
.accentColor(Color.white)
.padding(.vertical, 10)
.background(Color.red)
.cornerRadius(5)
.padding(.horizontal, 40)
}
}.padding()}
}
where you are adding .padding() to an if-statement....
3.) you should name class names in that way people can understand what the class if for and start with a capitalized letter like Apple does with all class names
4.) you should also begin View names capitalized (like Apple does)
5.) since i have to access to your page and you did not provide example data and/or password i have no idea what data are coming there...
here is my solution code so far:
i added some faking data, because i cannot access your data ....so you can see something in details.
struct ToDo: Decodable, Identifiable {
var id = UUID().uuidString
let res, ordercount, rate: String
}
class ToDoGetter: ObservableObject {
let objectWillChange = PassthroughSubject<ToDoGetter, Never>()
#Published var authenticated = ""
#Published var todos = [ToDo]() {
didSet {
objectWillChange.send(self)
}
}
let someFakingTodos = [
ToDo(res: "a", ordercount: "5", rate: "75%"),
ToDo(res: "b", ordercount: "52", rate: "5%"),
ToDo(res: "c", ordercount: "566", rate: "7%"),
ToDo(res: "d", ordercount: "53", rate: "33%"),
ToDo(res: "e", ordercount: "15", rate: "44%"),
ToDo(res: "f", ordercount: "345", rate: "10%")
]
func auth(username: String, password: String) {
guard let url = URL(string: "http://company.com/auth.php") else { return }
let body: [String: String] = ["username": username, "password": password]
let finalBody = try! JSONSerialization.data(withJSONObject: body)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = finalBody
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data=data else{return}
let fineldata=try! JSONDecoder().decode(ToDo.self, from: data)
DispatchQueue.main.async {
self.todos = [fineldata]
self.authenticated=fineldata.res
}
print(fineldata)
}.resume()
}
}
struct ContentView: View {
#EnvironmentObject var todoGetter : ToDoGetter
#State private var username: String = ""
#State private var password: String = ""
#State var navigateToDetail : Bool = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
if todoGetter.authenticated == "2"{
Userdetails().environmentObject(todoGetter)
} else{
Text("Username")
TextField("placeholder", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
.autocapitalization(.none)
Text("Password")
SecureField("placeholder", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.border(Color.green)
NavigationLink(destination: Userdetails(), isActive: self.$navigateToDetail) {
EmptyView() }
.hidden()
.padding()
Button(action: {
self.todoGetter.auth(username: self.username, password: self.password)
self.navigateToDetail.toggle()
}) {
HStack{
Spacer()
Text("Login")
Spacer()
}
.accentColor(Color.white)
.padding(.vertical, 10)
.background(Color.red)
.cornerRadius(5)
.padding(.horizontal, 40)
}
}
}
}
}
}
struct Userdetails: View {
#EnvironmentObject var todoGetter : ToDoGetter
var body : some View{
VStack(spacing: 20) {
Image("wlogo").renderingMode(.original); HStack(spacing: 15){
Spacer()
List(todoGetter.someFakingTodos) { todo in
VStack(alignment: .center, spacing: 10) {
HStack {
Text(todo.res).foregroundColor(Color.white).bold()
.font(.largeTitle)
Text(todo.ordercount)
.foregroundColor(Color.white)
.font(.headline)
Text(todo.rate)
.foregroundColor(Color.white)
.font(.headline)
}
}.background(Color.black)
Spacer()
}
}
}
}
}
OLD ANSWER
you call getres 2 times. you have to call it just once and then give the value to the detailview.
the model should only be created once per app.
Related
am a beginner in swiftui, still learning how to deal with ObservableObject
let me show my code and illustrate what is my question...
JsonResponse as follows:
{
"error" : false,
"user" : {
"username" : "Maxwell",
"id" : 84560,
"name" : "Max",
"authority" : "Manager"
}
}
Authenticate User Class (ObservableObject):
import Foundation
import Alamofire
struct UserDetails{
static var id: String = ""
static var name: String = ""
static var authority: String = ""
}
class AuthenticateUser: ObservableObject{
#Published var isLoggedin: Bool = false
func Authenticate(Username:String,Password:String){
let url:String = "http://…./Login.php"
let headers: HTTPHeaders = ["Content-Type":"application/x-www-form-urlencoded"]
let data: Parameters = ["username":Username,"password":Password]
AF.request(url, method: .post, parameters: data, encoding: URLEncoding.default, headers: headers).validate(statusCode: 200 ..< 299).response { AFdata in
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: AFdata.data!, options: .mutableContainers) as? NSDictionary else {
print("Error: Cannot convert data to JSON object")
return
}
guard let prettyJsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted) else {
print("Error: Cannot convert JSON object to Pretty JSON data")
return
}
guard let prettyPrintedJson = String(data: prettyJsonData, encoding: .utf8) else {
print("Error: Cannot print JSON in String")
return
}
//if there is no error
if(!(jsonObject.value(forKey: "error") as! Bool)){
print(prettyPrintedJson)
//getting the user from response
let user = jsonObject.value(forKey: "user") as! NSDictionary
//getting user values
let userID = user.value(forKey: "id") as! Int
let name = user.value(forKey: "name") as! String
let authority = user.value(forKey: "authority") as! String
//saving user values
UserDetails.id = String(userID)
UserDetails.name = name
UserDetails.authority = authority
self.isLoggedin = true
}else{
//error message in case of wrong credentials
print("Wrong Credentials")
}
} catch {
print("Error: Trying to convert JSON data to string")
return
}
}
}
}
Login View:
import SwiftUI
struct Login: View {
#StateObject private var loginManager = AuthenticateUser()
#State var username = ""
#State var password = ""
var body: some View {
ZStack{
Rectangle()
.fill(Color(red: 33 / 255, green: 34 / 255, blue: 36 / 255))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
VStack(){
Spacer()
Image("Logo")
Spacer()
VStack(){
Text("RIVIERA BEACH CHALETS")
.font(.title2)
.fontWeight(.semibold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
Text("Administration System")
.font(.headline)
.fontWeight(.light)
.foregroundColor(.white)
}
Spacer()
Text("Sign in")
.font(.largeTitle)
.fontWeight(.medium)
.foregroundColor(.white)
Spacer()
VStack(){
ZStack {
if username.isEmpty {
Text("Username")
.foregroundColor(.white.opacity(0.3))
}
TextField("", text: $username)
.padding()
.multilineTextAlignment(.center)
.foregroundColor(.white)
.overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.white,lineWidth: 1))
}.padding(.horizontal,5).padding(.vertical,5)
Spacer().frame(height: 15)
ZStack {
if password.isEmpty {
Text("Password")
.foregroundColor(.white.opacity(0.3))
}
SecureField("", text: $password)
.padding()
.multilineTextAlignment(.center)
.foregroundColor(.white)
.overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.white,lineWidth: 1))
}.padding(.horizontal,5).padding(.vertical,5)
Spacer().frame(height: 15)
HStack {
Button(action:{
//BUTTON ACTION
loginManager.Authenticate(Username: username, Password: password)
},label: {
Spacer()
Text("AUTHENTICATE")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(.white.opacity(0.8))
Spacer()
}) .padding()
.background(Color(red: 40 / 255, green: 41 / 255, blue: 43 / 255))
.cornerRadius(20)
}.padding(.horizontal,20).padding(.vertical,5).padding()
Spacer()
}.padding(20)
Spacer()
}
}
}
}
struct LoginPreview: PreviewProvider {
static var previews: some View {
Login()
}
}
let me explain what is going on... and what is my question:
As you can see in code, we have a Json Object response being parsed as NS dictionary while revalidating the UserDetails variables with the data from the Json response, and a published Bool to detect whether logged in or not !
so the question is, how to setup that bool to let the view detect whether user logged in or not ... ? in another words ,what to type in the view code to let the app switch the view to Home view for example if logged in was true ... ? while parsing the user details to show it in that Home view.
Any help would be appreciated !
I'm new to SwiftUI and have worked through the server requests and JSON. I now need to programmatically transition to a new view which is where I get stuck with a "Cannot find 'json' in scope" error on the NavigationLink in ContentView.swift. I've watched videos and read articles but nothing quite matches, and everything I try just seems to make things worse.
JSON response from server
{"status":{"errno":0,"errstr":""},
"data":[
{"home_id":1,"name":"Dave's House","timezone":"Australia\/Brisbane"},
{"home_id":2,"name":"Mick's House","timezone":"Australia\/Perth"},
{"home_id":3,"name":"Jim's House","timezone":"Australia\/Melbourne"}
]}
JSON Struct file
import Foundation
struct JSONStructure: Codable {
struct Status: Codable {
let errno: Int
let errstr: String
}
struct Home: Codable, Identifiable {
var id = UUID()
let home_id: Int
let name: String
let timezone: String
}
let status: Status
let data: [Home]
}
ContentView file
import SwiftUI
struct ContentView: View {
#State private var PushViewAfterAction = false
var body: some View {
NavigationLink(destination: ListView(json: json.data), isActive: $PushViewAfterAction) {
EmptyView()
}.hidden()
Button(action: {
Task {
await performAnAction()
}
}, label: {
Text("TEST")
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue.cornerRadius(10))
.foregroundColor(.white)
.font(.headline)
})
}
func performAnAction() {
PushViewAfterAction = true
return
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ListView file
import SwiftUI
struct ListView: View {
#State var json: JSONStructure
var body: some View {
VStack {
List (self.json.data) { (home) in
HStack {
Text(home.name).bold()
Text(home.timezone)
}
}
}.onAppear(perform: {
guard let url: URL = URL(string: "https://... ***removed*** ") else {
print("invalid URL")
return
}
var urlRequest: URLRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
// check if response is okay
guard let data = data, error == nil else { // check for fundamental networking error
print((error?.localizedDescription)!)
return
}
let httpResponse = (response as? HTTPURLResponse)!
if httpResponse.statusCode != 200 { // check for http errors
print("httpResponse Error: \(httpResponse.statusCode)")
return
}
// convert JSON response
do {
self.json = try JSONDecoder().decode(JSONStructure.self, from: data)
} catch {
print(error.localizedDescription)
print(String(data: data, encoding: String.Encoding.utf8)!)
}
print(json)
if (json.status.errno != 0) {
print(json.status.errstr)
}
print("1. \(json.data[0].name)), \(json.data[0].timezone)")
print("2. \(json.data[1].name)), \(json.data[1].timezone)")
}).resume()
})
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
I've tried to keep the code to a minimum for clarity.
It's because there is no "json" in ContentView, you need to pass json object to ListView, but since you load json in ListView, then you need to initialize json in ListView like:
struct ListView: View {
#State var json: JSONStructure = JSONStructure(status: JSONStructure.Status(errno: 0, errstr: ""), data: [JSONStructure.Home(home_id: 0, name: "", timezone: "")])
var body: some View {
and remove it form NavigationLink in ContentView like:
NavigationLink(destination: ListView(), isActive: $PushViewAfterAction) {
or you could build your JSONStructure to accept optional like:
import Foundation
struct JSONStructure: Codable {
struct Status: Codable {
let errno: Int?
let errstr: String?
init() {
errno = nil
errstr = nil
}
}
struct Home: Codable, Identifiable {
var id = UUID()
let home_id: Int?
let name: String?
let timezone: String?
init() {
home_id = nil
name = nil
timezone = nil
}
}
let status: Status?
let data: [Home]
init() {
status = nil
data = []
}
}
but then you need to check for optionals or provide default value like:
struct ListView: View {
#State var json: JSONStructure = JSONStructure()
var body: some View {
VStack {
List (self.json.data) { (home) in
HStack {
Text(home.name ?? "Could not get name").bold()
Text(home.timezone ?? "Could not get timeZone")
}
}
}.onAppear(perform: {
guard let url: URL = URL(string: "https://... ***removed*** ") else {
print("invalid URL")
return
}
var urlRequest: URLRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
// check if response is okay
guard let data = data, error == nil else { // check for fundamental networking error
print((error?.localizedDescription)!)
return
}
let httpResponse = (response as? HTTPURLResponse)!
if httpResponse.statusCode != 200 { // check for http errors
print("httpResponse Error: \(httpResponse.statusCode)")
return
}
// convert JSON response
do {
self.json = try JSONDecoder().decode(JSONStructure.self, from: data)
} catch {
print(error.localizedDescription)
print(String(data: data, encoding: String.Encoding.utf8)!)
}
print(json)
if (json.status?.errno != 0) {
print(json.status?.errstr)
}
print("1. \(json.data[0].name)), \(json.data[0].timezone)")
print("2. \(json.data[1].name)), \(json.data[1].timezone)")
}).resume()
})
}
}
i am making this app that uses jikan API to show a list of animes, so in the url there is an option to change stuff like the type - anime, manga, etc and the subtype - upcoming, tv, movie, etc, the API is working working fine and is fetching details but now I want to show two pickers in a form view preferably to allow the user to select type and subtype thus I have used #State properties for the picker but it's not updating the list when I run the app and select a different value from the picker
here is the code -
import SwiftUI
struct Response: Codable{
var top: [Result]
}
struct Result: Codable {
var mal_id: Int
var rank: Int
var title: String
var type: String
var start_date: String?
var image_url: String
}
struct ContentView: View {
#State private var str2 = ""
#State private var str3 = ""
func loadData() {
str3 = String("/\(subtype[subTypeSelection])")
str2 = String("\(type[typeSelection])/")
let str1 = "https://api.jikan.moe/v3/top/"
guard let url = URL(string: str1 + str2 + "\(1)" + str3) else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
// we have good data – go back to the main thread
DispatchQueue.main.async {
// update our UI
self.top = decodedResponse.top
}
// everything is good, so we can exit
return
}
}
// if we're still here it means there was a problem
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
var type = ["anime", "manga", "people", "characters"]
var subtype = ["airing", "upcoming", "tv", "movie", "ova", "special"]
#State private var page = 1
#State private var typeSelection = 0
#State private var subTypeSelection = 2
#State private var top = [Result]()
var body: some View {
// ScrollView {
VStack {
Picker("Your Selection", selection: $subTypeSelection) {
ForEach(0 ..< 6) {
somehting in
Text("\(subtype[somehting])")
}
}
Picker("Your Selection", selection: $typeSelection) {
ForEach(0 ..< 4) {
somehting in
Text("\(type[somehting])")
}
}
}
// .onAppear(perform: {
// loadData()
// })
List(top, id: \.mal_id) { item in
HStack {
AsyncImage(url: URL(string: item.image_url)!,
placeholder: { Text("Loading ...") },
image: { Image(uiImage: $0).resizable() })
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
// .frame(idealHeight: UIScreen.main.bounds.width / 10 * 10 )
.clipShape(Capsule())
VStack(alignment: .leading) {
Text(item.title)
.font(.headline)
Text(String("\(item.rank)"))
.font(.headline)
Text(item.type)
.font(.headline)
Text(item.start_date ?? "")
.font(.headline)
}
}
}
// }
.onAppear(perform: loadData)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
you could try to use "onChange" on each picker:
VStack {
Picker("Your subtype Selection", selection: $subTypeSelection) {
ForEach(0 ..< 6) { somehting in
Text("\(subtype[somehting])").tag(somehting)
}
}.onChange(of: subTypeSelection) { value in
loadData()
}
Picker("Your type Selection", selection: $typeSelection) {
ForEach(0 ..< 4) { somehting in
Text("\(type[somehting])").tag(somehting)
}
}.onChange(of: typeSelection) { value in
loadData()
}
}
It's not very efficient, but I'm sure you will find a better way to re-load the data.
I am new to swift (I am more used to python) and I am trying to send a POST request to a server and use the JSON response. I have been able to make the POST request and retrieve data and print it using this code that I got from a tutorial but the variable server.authenticated wasn't changing and after I made some changes now I'm getting two errors: Instance member 'authenticated' cannot be used on type 'HttpAuth' and 'String' is not convertible to 'Any'
Could someone please help?
import SwiftUI
import Combine
struct ServerMessage : Decodable {
let status, message: String
}
class HttpAuth: ObservableObject {
var didChange = PassthroughSubject<HttpAuth, Never>()
#Published var authenticated = false {
didSet{
didChange.send(self)
}
}
func checkDetails(username: String, password: String) {
guard let url = URL(string: "https://example.com") else { return }
let body: [String: String] = ["username": username, "password": password ]
let finalBody = try! JSONSerialization.data(withJSONObject: body)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = finalBody
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
let finalData = try! JSONDecoder().decode(ServerMessage.self, from: data)
print(finalData)
if finalData.status == "ok" {
DispatchQueue.global().async {
HttpAuth.authenticated = true //ERROR: Instance member 'authenticated' cannot be used on type 'HttpAuth'
print("correct credentials")
}
}
}.resume()
}
}
struct loginView: View {
#State private var username: String = ""
#State private var password: String = ""
#State var server = HttpAuth()
var body: some View {
VStack{
TextField("Username", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 200, height: nil)
.multilineTextAlignment(.center)
.disableAutocorrection(true)
.accessibility(identifier: "Username")
.autocapitalization(.none)
SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 200, height: nil)
.multilineTextAlignment(.center)
.disableAutocorrection(true)
.accessibility(identifier: "Password")
Button(action: {
self.server.checkDetails(username: self.username, password: self.password)
//print(self.username + ", " + self.password)
}) {
HStack{
Spacer()
Text("Login").font(.headline).foregroundColor(.white)
Spacer()
}.padding(.vertical, 10)
.background(Color.red)
.padding(.horizontal, 40)
}
if self.server.authenticated {
Text("Correct Credentials")
} //ERROR: 'String' is not convertible to 'Any'
}
}
}
struct loginView_Previews: PreviewProvider {
static var previews: some View {
loginView()
}
}
You need to pass HttpAuth to your loginView like
struct loginView_Previews: PreviewProvider {
static var previews: some View {
loginView().environmentObject(HttpAuth())
}
}
then in your LoginView declare EnvironmentObject of HttpAuth
struct loginView: View {
#State private var username: String = ""
#State private var password: String = ""
#EnvironmentObject var server : HttpAuth
...
I am trying to call a function I have made which pulls data from an API. The function is created before the var body: some View part and is call inside of that. There are no compiler errors in the function itself or anywhere else. Before I started to use SwiftUI I had no issue with this type of function and the code is almost the exact same. Here is the code for the function including the Struct I have for the JSON data;
var postDetailsData = postDetails.self
var commentsArray = comments.self
struct postDetails: Decodable {
let count: Int?
var results: [results]
}
struct results: Decodable {
var id: String
let author: author?
let anonym: Bool
let subscribed: Bool
let created: String
let active: Bool
let text: String
let image: String?
let comments: [comments]
}
struct author: Decodable {
let user: Int
var name: String
let avatar: String?
let banned: Bool
}
struct comments: Decodable, Identifiable {
let id: Int
let text: String
let author: authorOfComment?
}
struct authorOfComment: Decodable {
let name: String
let avatar: String?
}
//And now the actual function
let tokenData = UserDefaults.standard.object(forKey: "savedToken")
var spread = Bool()
let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:8000/areas/sample/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
func pullData(){
let headers = [
"Authorization": "token \(tokenData ?? "nope")",
"cache-control": "no-cache",
"Postman-Token": "53853353-f547-410a-b289-e3c4ced8e426"
]
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
}
guard let data = data else {return}
do{
let JSONFromServer = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(JSONFromServer)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let postDetailsDataArray = try decoder.decode(postDetails.self, from: data)
print(postDetailsDataArray.results)
for results in postDetailsDataArray.results{
DispatchQueue.main.async {
//Account Pic Post and Other Stuff In The HStack
let accountPicAndStuff = HStack {
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
ImageWithActivityIndicator(imageURL: results.author?.avatar ?? "")
.frame(width: 30, height: 30)
.clipShape(Circle())
.offset(x: -75, y: 0)
}
Text(results.author?.name ?? "Annon")
.offset(x: -75)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Image("BookMark").offset(x: 70)
}
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Image("more-vertical").offset(x: 70)
}
}.offset(y: 65)
//accountPicAndStuff.offset(y: 65)
//Description code (pulls data from api)
var postTextTest = results.text
Text(results.text)
//.padding(.bottom, 0)
.offset(y: 10)
.lineLimit(4)
//Image From Post Stuff
if results.image == nil{
print("no image")
}else{
ImageWithActivityIndicator(imageURL: results.image ?? "")
.offset(y: 50)
.scaledToFit()
}
//Date and time for post
Text(results.created)
.font(.footnote)
.fontWeight(.light)
.foregroundColor(Color.gray)
.multilineTextAlignment(.leading)
.offset(x: -85, y: 50)
for comments in results.comments{
print(comments)
//Comment View Code using Lists
List(results.comments){ comments in
HStack{
ImageWithActivityIndicator(imageURL: comments.author?.avatar ?? "")
.frame(width: 100.0, height: 100.0)
}
}
}
}
}
}catch{
print("Failed to decode:", error)
}
})
dataTask.resume()
}
This is how I call the function;
struct FunAreaSmall: View {
//Code from above goes here...
var body: some View {
//Smaller UI
VStack {
pullData()
HStack {
Button(action:{}) {
Image("SkipBtn")
.offset(y: 60)
.scaledToFit()
}
Button(action: {}) {
Image("IgniteBtn")
.offset(y: 60)
.scaledToFit()
}
}
}
}
}
The error itself,
'Int' is not convertible to 'CGFloat'
appears on .offset(y: 60) on the first button in the HStack which I know isn't the issue because when I comment out the call to the function, the error goes away. I have tried different ways of doing getting around this error but all I get is more errors. I have tried to look up the errors on google and nothing has helped yet.
You can't just put function call in a ViewBuilder, because it can't resolve opaque return type.
There are several possible solutions, depending on your needs:
1) Put call of pullData in init()
2) Provide explicit return in ViewBuilder (all of them)
3) Call in some closure (seems most appropriate in this use-case) as in below:
struct FunAreaSmall: View {
//Code from above goes here...
var body: some View {
//Smaller UI
VStack {
HStack {
Button(action:{}) {
Image("SkipBtn")
.offset(y: 60)
.scaledToFit()
}
Button(action: {}) {
Image("IgniteBtn")
.offset(y: 60)
.scaledToFit()
}
}
}
.onAppear() {
self.pullData()
}
}
}