Firebase iOS Swift - Adding Atname Update Function to Settings - ios

Using Firebase and Swift, I am attempting to update the "atname" data in Firebase. Currently, the function is currently set up to modify the "username" and "bio" sections in Firebase. The following code is in my SettingsViewModel.swift file:
func updateDetails(field: String){
alertView(msg: "Update \(field)") { (txt) in
if txt != ""{
self.updateBio(id: field == "Name" ? "username" : "bio", value: txt)
}
}
}
func updateBio(id: String, value: String){
ref.collection("Users").document(uid).updateData([
id: value,
]) { (err) in
if err != nil{return}
fetchUser(uid: self.uid) { (user) in
self.userInfo = user
}
}
}
}
"username" is the name inside of the Firebase database. "Name" is added before the ? to provide a better understanding in alertView on what the user is updating.
Here is the code inside my SettingsView.swift file: (The part that is seen on the app)
HStack(spacing: 15){
Text(settingsData.userInfo.username)
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
Button(action: {settingsData.updateDetails(field: "Name")}) {
Image(systemName: "pencil.circle.fill") //makes text off centered, figure out how to fix
.font(.system(size: 24))
.foregroundColor(.white)
}
}
HStack(spacing: 15){
Text(settingsData.userInfo.atname)
.foregroundColor(.white)
.fontWeight(.light)
Button(action: {settingsData.updateDetails(field: "Atname")}) {
Image(systemName: "pencil.circle.fill") //makes text off centered, figure out how to fix
.font(.system(size: 24))
.foregroundColor(.white)
}
}
.padding()
HStack(spacing: 15){
Text(settingsData.userInfo.bio)
.foregroundColor(.white)
Button(action: {settingsData.updateDetails(field: "Bio")}) {
Image(systemName: "pencil.circle.fill") //makes text off centered, figure out how to fix
.font(.system(size: 24))
.foregroundColor(.white)
}
}
As you can see, inside of SettingsView.swift, my field for the Atname section is "Atname". However, in SettingsViewModel.swift, I can not figure out how to add Atname into the code to get Atname to actually update in Firebase.
Any time I try to add "Atname" I end up with some kind of error. If launch the app and try to edit "Atname" in settings, it changes "bio". I am relatively new to Swift and I have no idea how to get this to work.
Any help will be appreciated! I can provide more code/details if needed.

Right now, because of the ternary expression that you're using, the only two possibilities for id are "username" or "bio".
I'd suggest refactoring updateDetails so that it can accept more types of input. I see that you're also displaying a message alertView, which looks like the reason you're trying to convert the actual field name into something more human readable -- I added another function called readableNameForField that uses a switch statement to provide the readable names.
What about something like this?
func readableNameForField(_ field : String) -> String {
switch field {
case "username":
return "Name"
case "atname":
return "At Name"
default:
return field.localizedCapitalized
}
}
func updateDetails(field: String){
alertView(msg: "Update \(readableNameForField(field))") { (txt) in
if !txt.isEmpty {
self.updateBio(id: field, value: txt)
}
}
}
Then, at your call sites, you'd use the actual field names:
settingsData.updateDetails(field: "username")
settingsData.updateDetails(field: "atname")
settingsData.updateDetails(field: "bio")

Related

My search bar won't show anything in list

I have a view model for country codes, names, flags...
How can I search through that list once I fetch it?
My search code is like this:
ForEach(countryVM.diallingCode.filter{$0.name.lowercased()
.contains(self.textlowercased())}, id:\.self) { option in
VStack{
Divider()
.frame(maxWidth:.infinity)
HStack{
Image(option.flag)
.resizable()
.frame(width:30, height:30)
Text(option.name)
.font(.custom("Ubuntu-Light", size:12))
Spacer()
}.frame(maxWidth:.infinity)
}
}
There is array in diallingCode, it shows me the countries, but I can't search through them.
The same code worked when I used static values.
To make it easier on yourself, move
countryVM.diallingCode.filter{$0.name.lowercased().contains(self.textlowercased())}`
to a method on your view model, e.g.
func filteredDialingCodes(for text: String) {
let codes = diallingCode.filter{$0.name.lowercased().contains(text.lowercased())}
print(codes) // see what's happening
return codes
}
Then call like:
ForEach(filteredDialingCodes(for: text), id:\.self) { option in
Not only is it clearer what you're trying to do, it also will allow you to see what values are returned when filtering.

Can we have a SwiftUI TabView with more than five entries?

I have a set of seven pages. I have made my own scrollable view.
I have a working solution where I have the current page and attach gestures to it like this...
switch page {
case .EyeTest:
EyeTestView(sd: $sd)
.gesture(DragGesture(minimumDistance: swipeMin)
.onEnded { value in
if value.translation.width < -swipeMin { page = Page.Tone }
} )
...`
This worked. It was rather clunky, but I could animate the drags better if I worked at it. However, the tab view worked beautifully with TabView for up to five entries. So, I thought I might be able to span the solution using two tab views. Here is what I did, concluding the custom scrollbar at the top...
VStack() {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
pageButton(Page.ByEye, "eye", "ByEye", proxy)
pageButton(Page.List, "list.bullet", "List", proxy)
pageButton(Page.Camera, "camera", "Camera", proxy)
pageButton(Page.Settings, "gear", "Settings", proxy)
}
}
.onAppear { proxy.scrollTo(Page.ByEye, anchor: .center) }
.onChange(of: page) { page in
withAnimation {
proxy.scrollTo(page, anchor: .center)
}
}
}
if (page == Page.EyeTest || page == Page.Tone || page == Page.Chart) {
TabView(selection:$page) {
EyeTestView(sd: $sd).tag(Page.EyeTest)
ToneView(sd: $sd).tag(Page.Tone)
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
} else {
TabView(selection:$page) {
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
CameraView(sd: $sd).tag(Page.Camera)
SettingsView(sd: $sd).tag(Page.Settings)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
}
}`
However, this does not scroll past the 5th entry.
I would be interested to know why this doesn't work, but I would be happy to replace it with something equivalent that does work. I would also like to know how people handle things like books, which have lost more pages. I feel I ought to make two synchronised scrollable lists.
Yes, maybe it is bad API to have a flat menu with more than five entries. However, some of the ones at the end of the list are rarely used, but need to be discoverable.
I am using Xcode 14.0.1 with iOS 16.0.
Here is a solution that seems to work for me. It is quite close to my original bodge: we have two TabViews, and swap between them depending on the current page so we can always have the neighbours on either side.
I had written my own scrollable index to replace the TabView indexes, but you might make do with the original fittings. I have only been doing Swift for a month, so this may not be the best code, so please post your improvements.
`
enum Page {
case EyeTest
case Tone
case Chart
case ByEye
case List
case Camera
case Settings
}
#AppStorage("menu") private var menu = "Measure"
#State private var page = Page.ByEye
func setMenu(_ page: Page) {
menu = (page == Page.EyeTest || page == Page.Tone || page == Page.Chart ) ? "Test" : "Measure"
}
func pageButton(_ select: Page, _ icon: String, _ title: String, _ proxy: ScrollViewProxy) -> some View {
return Button {
page = select
setMenu(page)
withAnimation {
proxy.scrollTo(select, anchor: .center)
}
} label: {
VStack {
Image(systemName: icon)
Text(title)
}
.frame(width: tabW)
}
.foregroundColor( page == select ? Color.white : Color.gray )
.id(select)
}
var body: some View {
VStack() {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
pageButton(Page.ByEye, "eye", "ByEye", proxy)
pageButton(Page.List, "list.bullet", "List", proxy)
pageButton(Page.Camera, "camera", "Camera", proxy)
pageButton(Page.Settings, "gear", "Settings", proxy)
}
}
.onAppear { proxy.scrollTo(page, anchor: .center) }
.onChange(of: page) { page in
withAnimation {
proxy.scrollTo(page, anchor: .center)
}
}
}
if (menu == "Test") {
TabView(selection:$page) {
EyeTestView(sd: $sd).tag(Page.EyeTest)
ToneView(sd: $sd).tag(Page.Tone)
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
SettingsView(sd: $sd).tag(Page.Settings)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
.onChange(of: page) { page in
setMenu(page)
}
} else {
TabView(selection:$page) {
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
CameraView(sd: $sd).tag(Page.Camera)
SettingsView(sd: $sd).tag(Page.Settings)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
.onChange(of: page) { page in
setMenu(page)
}
}
`

Firebase Analytics - How to see specific content and not a controller?

I have an app full of stories, and I want to see which stories users are reading.
I already installed Firebase Analytics, but all I'm seeing is this kind of content:
I want to be able to see the specific title of the stories users are reading.
This is the ForEach loop I'm using:
ForEach(modelData.stories.shuffled()) { item in
if item.paid == false {
NavigationLink {
StoryDetails(story: item)
} label: {
GeneralCard(isLocked: false, story: item)
}
}
}
And on StoryDetails I have this code, I would like to see the title of the story inside Firebase Analytics.
VStack (spacing: 0) {
Text(story.title)
.font(AppFont.detailTitle())
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.mainColor)
Text(story.category.rawValue)
.font(AppFont.detailDescription())
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.mainColor)
VStack(alignment: .leading, spacing: 20) {
ForEach(story.text, id: \.self) { item in
VStack {
Text(item)
.font(AppFont.detailText())
}
}
}
//.padding(.horizontal, 10)
}
Is this something that can be achieved?
Thanks in advance
You could set this up by manually logging your screenviews instead. For SwiftUI, it is something like this:
struct ContentView: View {
var body: some View {
Text("Hello, world!")
// Logging screen name with class and a custom parameter.
.analyticsScreen(name: "main_content",
class: "ContentView",
extraParameters: ["my_custom_param": 5])
// OR Logging screen name only, class and extra parameters are optional.
.analyticsScreen(name: "main_content")
}
}

iOS Swift SwiftUI - Use NavigationLink after clicking on subtext

I got this text:
Text("Indem du fortfährst, stimmst du unseren ") +
Text("Nutzungsbedingungen")
.underline()
.foregroundColor(Color("ClickableLink")) +
Text(" und unserer ") +
Text("Datenschutzerklärung")
.underline()
.foregroundColor(Color("ClickableLink")) +
Text(" zu")
I'd like to open a new view using NavigationLink after taping on Nutzungsbedingungen or Datenschutzerklärung, both need to open different views.
I've seen those answers:
SwiftUI tappable subtext
but those are either not what I need or trying them gives me errors and I don't know how to modify them since I'm absolutely new to swift/swiftui
Here are two possible approaches, one using the new NavigationStack with NavigationDestination and the other using popOver.
Both have the downside that you can't concatenate Text views within them. But you can still work with HStack/VStack to define the layout.
enum Legals: Identifiable {
case nutzungsbedingungen
case datenschutzerklärung
var id: Legals { self }
}
struct ContentView: View {
#State private var selected: Legals?
var body: some View {
NavigationStack {
// NavigationLink version
VStack(alignment: .leading) {
Text("Indem du fortfährst, stimmst du unseren")
NavigationLink("Nutzungsbedingungen", value: Legals.nutzungsbedingungen)
Text("und unserer")
NavigationLink("Datenschutzerklärung", value: Legals.datenschutzerklärung)
Text("zu")
}
.navigationDestination(for: Legals.self) { selection in
switch selection {
case .datenschutzerklärung:
Text("Datenschutzerklärung")
case .nutzungsbedingungen:
Text("Nutzungsbedingungen")
}
}
Divider()
// popOver Version
VStack(alignment: .leading) {
Text("Indem du fortfährst, stimmst du unseren ")
Text("Nutzungsbedingungen")
.underline()
.onTapGesture {
selected = .nutzungsbedingungen
}
Text("und unserer ")
Text("Datenschutzerklärung")
.underline()
.onTapGesture {
selected = .nutzungsbedingungen
}
Text("zu")
}
.popover(item: $selected) { selected in
switch selected {
case .datenschutzerklärung:
Text("Datenschutzerklärung")
case .nutzungsbedingungen:
Text("Nutzungsbedingungen")
}
}
}
}
}

SwiftUI Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift

I have a environment object that is an array, and a panel in my settings page where the user can add/remove values to the array...here is the list of items in the array displayed visually for the user:
ScrollView {
VStack {
ForEach(self.env.numbers.indices, id: \.self) { number in
ZStack {
HStack {
Text("#\(number + 1): ")
.font(Font.custom(K.font.montserratMedium, size: 17.0))
.foregroundColor(Color(K.SettingsTextColor))
// Spacer()
NumberTextField(self.env.numbers[number], text: self.$env.numbers[number])
}
}
}
}
}
and at the bottom of my view is a toggle to change the number of items in the array:
Stepper(onIncrement: {
self.env.numbers.append("12345")
print("default number should be added")
print(self.env.numbers.indices)
print(self.env.numbers)
print("count = \(self.env.numbers.count)")
},
onDecrement: {
if self.env.numbers.count != 0 {
print("number should be removed")
self.env.numbers.removeLast(1)
// also tried self.env.numbers = self.env.numbers.droplast() but this didnt work
print(self.env.numbers.indices)
print(self.env.numbers)
print("count = \(self.env.numbers.count)")
}
the NumberTextField view is a UIKit UITextField wrapper, and seems to work fine. Everything about this works fine until I try to decrease the stepper, and I get the Index out of range error (which surprisingly shows the error happening in the app delegate)...any ideas how to fix this?

Resources