1

I have a table in a mySQL database with the variable "show" that I need to convert from an Int to a Bool within SwiftUI.

Not being able to directly declare 'false' as a field value within SQL - I need to code SwiftUI to interpret this integer as a boolean value.

The JSON output reads as

[
    {
        "establishmentId": 2,
        "name": "O'Reilly's Pub",
        "slogan": "Insert slogan here."
        "city" : "Insert city here."
        "state" : "Insert state here."
        "email": "oreillys@email.com",
        "phone" : "Insert phone here."
        "zip" : 12345
        "latitude" : 12.22222222
        "longitude" : -31.111111
        "hours" : "Insert hours here."
        "show" : 0
    }
]

In SwiftUI I have a structure called 'Establishment'


    struct Establishment: Codable, Identifiable {
    let id = UUID()
    let name: String
    let slogan: String
    let city: String
    let state: String
    let email: String
    let phone: String
    let zip: Int
    let signatureItem: String
    let latitude: CLLocationDegrees
    let longitude: CLLocationDegrees
    let logo: URL
    let image: URL
    var show: Bool
}

I receive errors when trying to iterate between the establishments due to the 'show' variable being an integer: Errors


    import SwiftUI
    import SDWebImageSwiftUI
    import MapKit

    struct EstablishmentList: View {

        @ObservedObject var store = DataStore()
        @State var active = false
        @State var activeIndex = -1
        @State var activeView = CGSize.zero

        var body: some View {
            ZStack {
                Color.black.opacity(Double(self.activeView.height/500))
                    .edgesIgnoringSafeArea(.all)
                    .statusBar(hidden: active ? true : false)
                    .animation(.linear)

                ScrollView {
                    VStack(spacing: 30) {
                        Text("Nearby Establishments")
                            //.font(.largeTitle).bold()
                            .font(.system(.largeTitle))
                            .fontWeight(.bold)
                            .alignmentGuide(.leading, computeValue: { _         in -30})
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding(.top, 20)
                            //.blur(radius: active ? 20 : 0)
                            .animation(nil)

                        ForEach(store.establishments.indices, id: \.self) { index in
                            GeometryReader { geometry in
                                EstablishmentView(show: self.$store.establishments[index].show,
                                establishment: self.store.establishments[index],
                                active: self.$active,
                                index: index,
                                activeIndex: self.$activeIndex,
                                activeView: self.$activeView
                            )
                                .offset(y: self.store.establishments[index].show ? -geometry.frame(in: .global).minY : 0)
                                //.opacity(self.activeIndex != index && self.active ? 0 : 1)
                                .scaleEffect(self.activeIndex != index && self.active ? 0.5 : 1)
                                .offset(x: self.activeIndex != index && self.active ? screen.width : 0)
                        }
                        .frame(height: getCardHeight())
                        .frame(maxWidth: self.active ? 712 : getCardWidth())
                    }
                }
                .frame(width: screen.width)
                .padding(.bottom, 300)
                .animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0))
            }
        }
    }
}

In the EstablishmentView structure I declare '@Binding var show: Bool' and I think this is where my issue rests EstablishmentView


    struct EstablishmentView: View {
    @Binding var show: Bool
    var establishment: Establishment
    @Binding var active: Bool
    var index: Int
    @Binding var activeIndex: Int
    @Binding var activeView: CGSize


    var body: some View {
        ZStack(alignment: .top) {
            VStack(alignment: .leading, spacing: 30.0) {
                Text(establishment.name)

                Text("About this establishment")
                    .font(.title)
                    .fontWeight(.bold)

                Text(establishment.slogan)
                    .foregroundColor(Color("secondary"))

                Text(establishment.signatureItem)
                    .foregroundColor(Color("secondary"))

            }
            .padding(30)
            .offset(y: show ? 460 : 0)
            .frame(maxWidth: show ? .infinity : getCardWidth())
            .frame(maxHeight: show ? screen.height : 280, alignment: .top)
            .background(Color("background2"))
            .clipShape(RoundedRectangle(cornerRadius: 30, style: .continuous))
            .shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 20)
            .opacity(show ? 1 : 0)

            VStack {
                HStack(alignment: .top) {
                    VStack(alignment: .leading, spacing: 8.0) {
                        Text(establishment.name)
                            .font(.system(size: 24, weight: .bold))
                            .lineLimit(3)
                            .foregroundColor(.white)
                            .animation(nil)
                        Text(establishment.email.uppercased())
                            .foregroundColor(Color.white.opacity(0.7))
                            .animation(nil)
                        Text(establishment.state)
                            .foregroundColor(Color.white).opacity(0.7)
                            .animation(nil)
                    }
                    Spacer()
                    ZStack {
                        WebImage(url: establishment.image)
                            .opacity(show ? 0 : 1)
                        VStack {
                            Image(systemName: "xmark")
                                .font(.system(size: 16, weight: .medium))
                                .foregroundColor(.white)
                        }
                        .frame(width: 36, height: 36)
                        .background(Color.black)
                        .clipShape(Circle())
                        .opacity(show ? 1 : 0)
                    }
                }
                Spacer()
                WebImage(url: establishment.image)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(maxWidth: 414)
                    .frame(height: 140, alignment: .top)
            }
            .padding(show ? 30 : 20)
            .padding(.top, show ? 30 : 0)
            .frame(height: show ? 460 : 280)
            .frame(maxWidth: show ? .infinity : getCardWidth())
            .background(Color(#colorLiteral(red: 0.5843137503, green: 0.8235294223, blue: 0.4196078479, alpha: 1)))
            .clipShape(RoundedRectangle(cornerRadius: 30, style: .continuous))
            .shadow(color: Color(#colorLiteral(red: 0.5843137503, green: 0.8235294223, blue: 0.4196078479, alpha: 1)).opacity(0.3), radius: 20, x: 0, y: 20)
            .gesture(
                show ?
                    DragGesture()
                        .onChanged { value in
                            guard !self.show else { return }
                            guard value.translation.height > 0 else { return }
                            guard value.translation.height < 300 else { return }

                            self.activeView = value.translation
                    }
                    .onEnded { value in
                        if self.activeView.height > 50 {
                            self.show = false
                            self.active = false
                            self.activeIndex = -1
                        }
                        self.activeView = .zero
                    }
                    : nil
            )
                .onTapGesture {
                    self.show.toggle()
                    self.active.toggle()

                    if self.show {
                        self.activeIndex = self.index
                    } else {
                        self.activeIndex = -1
                    }
            }
            if show {
                EstablishmentDetail(establishment: establishment, show: $show, active: $active, activeIndex: $activeIndex)
                    .background(Color("background1"))
                    .animation(.linear(duration: 0))
            }
        }
        .gesture(
            show ?
                DragGesture()
                    .onChanged { value in
                        guard value.translation.height > 0 else { return }
                        guard value.translation.height < 300 else { return }

                        self.activeView = value.translation
                }
                .onEnded { value in
                    if self.activeView.height > 50 {
                        self.show = false
                        self.active = false
                        self.activeIndex = -1
                    }
                    self.activeView = .zero
                }
                : nil
        )
            .frame(height: show ? screen.height : 280)
            .edgesIgnoringSafeArea(.all)
            .animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0))
            .scaleEffect(1 - self.activeView.height / 1000)
            .rotation3DEffect(Angle(degrees: Double(self.activeView.height / -10)), axis: (x: 10, y: -10, z: 0))
            .hueRotation(Angle(degrees: Double(self.activeView.height)))
    }
}

(I have been working with hardcoded values which is why I never ran into issues when I declared 'var show = false' in my original Establishment struct.)

In an APIManager class I call on my API


import SwiftUI

class APIManager {

    func getEstablishments(completion: @escaping ([Establishment]) -> ()) {
        guard let url = URL(string: "api address here") else { return }


        URLSession.shared.dataTask(with: url) { (data, _, _) in
            guard let data = data else { return }

            let establishments = try! JSONDecoder().decode([Establishment].self, from: data)

            DispatchQueue.main.async {
                completion(establishments)
            }
        }
        .resume()
    }

and in a DataStore class I initialize a function to utilize the APIManager


import SwiftUI
import Combine

class DataStore: ObservableObject {
    @Published var establishments: [Establishment] = []

    init() {
        getEstablishments()
    }

    func getEstablishments() {
        APIManager().getEstablishments { (establishments) in
            self.establishments = establishments
        }
    }

Can anybody recommend a method of converting the Int to Bool datatype within SwiftUI - that would eliminate my errors. I hope i've provided enough of my code to be clear but let me know if there is more I can provide for clarity's sake.

Edit: Replaced images of code with actual text

Edit: Results from Chris's answer


    extension Establishment: Decodable {

    private struct JSONSettings: Decodable {
        var id = UUID()
        var name, slogan, city, state, email, phone, signatureItem: String
        var latitude, longitude: Double
        var logo, image: String
        var zip, show: Int
    }

    private enum CodingKeys: String, CodingKey {
        case establishmentList // Top level
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let settings  = try container.decode(JSONSettings.self, forKey: .establishmentList)
        id = settings.id
        name = settings.name
        slogan = settings.slogan
        city = settings.city
        state = settings.state
        email = settings.email
        phone = settings.phone
        zip = settings.zip
        signatureItem = settings.signatureItem
        latitude = settings.latitude
        longitude = settings.longitude
        show = settings.show == 1 ? true : false
    }
}

error of code recommended by Chris

awentz
  • 9
  • 3

1 Answers1

0

You can use a custom initialiser for the struct that converts a String or Int to Bool.

struct Establishment {
    let establishmentID: Int
    let name, email: String
    let show: Bool
}

extension Establishment: Decodable {

    private struct JSONSettings: Decodable {
        var establishmentId: String
        var name: String
        var email: String
        var show: Int
    } 

    private enum CodingKeys: String, CodingKey {
        case establishmentList // Top level
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let settings  = try container.decode(JSONSettings.self, forKey: .establishmentList)
        establishmentId = settings.establishmentId
        name = settings.name
        email = settings.email
        show = settings.show == 1 ? true : false
    }
}

All credit to this answer.

Chris
  • 4,009
  • 3
  • 21
  • 52
  • I tried your code out and have been receiving conformance to protocol errors in the compiler - the exact errors are contained within the edit in the parent post. – awentz May 21 '20 at 09:55
  • @awentz Thanks for posting more of your code. That error might mean there are multiple implementations of conformance to the encodable and decodable protocols. Maybe try my answer without `Decodable` in the declaration, or remove `Codable` from your declaration of `Establishment`. – Chris May 21 '20 at 12:57