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": "[email protected]",
        "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

4
  • 2
    Would you show your EstablishmentView code? Commented May 21, 2020 at 8:12
  • Post code as text and not as images. Text can be searched and also copied into an editor or playground so it is much more useful for someone who wants to help you Commented May 21, 2020 at 9:17
  • @Asperi I just posted my EstablishmentView code Commented May 21, 2020 at 9:20
  • @JoakimDanielson Thank you for your comment! I will revise the post after attempting the answer posted by Chris below. Commented May 21, 2020 at 9:21

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.