I just noticed something in the Lunch Card app: when adding another card after adding the first, it autofills all the text fields (newCard variable) with the previous info. For a normal customer, I think that would be pretty annoying to have to delete all the field data and replace it every time.
If you need reference, check out previous expansions of this. Here's the most recent code for CardsView and AddView for version 1.2:
CardsView - https://developer.apple.com/forums/content/attachment/dcdbf40d-93ee-46fe-851b-54cf36e4498fNote: Not really important, but instructions now exist.
AddView - https://developer.apple.com/forums/content/attachment/eb2260d0-6848-4cca-b94c-e488963b0139Note: I added colors as a variable as well.
Here's Info.swift. It's not too much different but there are now colors:
Info.swift - https://developer.apple.com/forums/content/attachment/83be0d99-62db-4242-a20b-72f66e78bb02
Post
Replies
Boosts
Views
Activity
Please read the last thread: Call a Class into views - https://developer.apple.com/forums/thread/671482
The right code for the XML parser is almost set in stone, just I need a way to retrieve article data (not just through loadArticles()) like title, author, etc.
I've decided, and was suggested, that I go the same route as my other app, Lunch Card, where there's a function where you use a dataUrl and data to call ArticlesParser(). Thing is, since the code for the XML parser is a bit more complicated, it's harder to pass variables around just like with Lunch Card. There's another class I have to deal with. So I either have to (1) redefine articles the same way with an Array;
@Published var articles: [ArticleInfo] = []
...or (2), find some other way to call articles from the ObservableObject, maybe in a StateObject or ObservedObject.
I want to try and borrow as much code from Lunch Card as possible, but for an XML parser with multiple classes and extensions, it a bit easier said than done. Here's full code for ArticleInfo and ArticlesView.
ArticleInfo.swift - https://developer.apple.com/forums/content/attachment/d16688a9-f420-4dee-b1f4-ed255e325a3eArticlesView - https://developer.apple.com/forums/content/attachment/fbfb99f1-87a7-448b-ad04-54aef6abe61c
Not sure how ArticleInfo is an integer, so I have no idea what's going on or why it's not working as well. Also, I'm trying to experiment with borrowing the JSON decoder/encoder code, so tell me if that's the wrong way to go.
Please read the last thread: Parse XML in SwiftUI - https://developer.apple.com/forums/thread/670984
I've got the code to parse articles from XML courtesy of OOPer...but I need to actually execute it in ArticlesView. How would I call ArticlesParser into ArticlesView and related views and run the parser onto an XML file (THS.xml)?
Here's ArticleInfo.swift and ArticlesView.
ArticleInfo.swift:
//
// ArticleInfo.swift
// Hair Society Go
//
// Created by Joshua Srery on 1/13/21.
// Code provided by OOPer on Apple Developer Forums
//
import Foundation
struct Article {
var title: String = ""
var date: Date?
var author: String?
var img: URL?
/// content in HTML
var content: String = ""
}
class ArticlesParser: XMLParser {
// Public property to hold the result
var articles: [Article] = []
var dateTimeZone = TimeZone(abbreviation: "GMT-6")
lazy var dateFormater: DateFormatter = {
let df = DateFormatter()
//Please set up this DateFormatter for the entry `date`
df.locale = Locale(identifier: "en_US_POSIX")
df.dateFormat = "MMM dd, yyyy"
df.timeZone = dateTimeZone
return df
}()
private var textBuffer: String = ""
private var nextArticle: Article? = nil
override init(data: Data) {
super.init(data: data)
self.delegate = self
}
}
extension ArticlesParser: XMLParserDelegate {
// Called when opening tag (`<elementName>`) is found
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "posts":
nextArticle = Article()
case "title":
textBuffer = ""
case "date":
textBuffer = ""
case "author":
textBuffer = ""
case "img":
textBuffer = ""
case "content":
textBuffer = ""
default:
print("Ignoring \(elementName)")
break
}
}
// Called when closing tag (`</elementName>`) is found
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "posts":
if let article = nextArticle {
articles.append(article)
}
case "title":
nextArticle?.title = textBuffer
case "date":
print("date: \(textBuffer)")
nextArticle?.date = dateFormater.date(from: textBuffer)
case "author":
nextArticle?.author = textBuffer
case "img":
print("img: \(textBuffer)")
nextArticle?.img = URL(string: textBuffer)
case "content":
nextArticle?.content = textBuffer
default:
print("Ignoring \(elementName)")
break
}
}
// Called when a character sequence is found
// This may be called multiple times in a single element
func parser(_ parser: XMLParser, foundCharacters string: String) {
textBuffer += string
}
// Called when a CDATA block is found
func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
guard let string = String(data: CDATABlock, encoding: .utf8) else {
print("CDATA contains non-textual data, ignored")
return
}
textBuffer += string
}
// For debugging
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError)
print("on:", parser.lineNumber, "at:", parser.columnNumber)
}
}
ArticlesView:
//
// ArticlesView.swift
// Hair Society Go
//
// Created by Joshua Srery on 11/29/20.
//
import SwiftUI
struct ArticlesView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 5) { item in
NavigationLink(destination: ArticleView(title: "Replace with Title var", image: "Replace with Img var", content: "Replace with Content var", author: "Replace with Author var", date: "Replace with Date var")) {
ArticleRow(image: "Replace with Img var", title: "Replace with Title var", author: "Replace with Author var", date: "Replace with Date var")
}
}
}
.navigationTitle("Articles")
.toolbar(content: {
Menu {
Button("Date", action: {})
Button("Title", action: {})
Button("Customize…", action: {})
} label: {
Label("Filter", systemImage: "line.horizontal.3.decrease.circle")
}
})
// Ignore the filter Menu for right now
}
}
}
struct ArticleRow: View {
let image: String
let title: String
let author: String
let date: String
var body: some View {
HStack {
Image(image)
.resizable()
.frame(minWidth: 75, maxWidth: 100, maxHeight: 75)
.cornerRadius(12)
VStack(alignment: .leading, content: {
Text(title)
.font(.headline)
Text("\(author) • \(date)")
.font(.subheadline)
})
}
}
}
So...I lost most of the Lunch Card files to a factory reset. There was an issue with Disk Utility - it's a whole thing. But I've managed to recover most of the files. But there's something that isn't working anymore within the code.
In SettingsView, where the color scheme ("Appearance") picker is located, you choose between light, dark, and system default.
But when calling the sheet for AddView in CardsView (using SheetInfo() from Info.swift), it just defaults to whatever the system is set at. When I previewed it, it was light mode when the app setting was dark.
Thing is, before I lost all my files, it worked perfectly normal, and consumer versions reflected that. I recovered (and rewrote some of) SettingsView and AddView (thank goodness I showed the full code for AddView here). I may have missed something, so here's my code for both, plus ContentView, CardsView, and Info.swift.
SettingsView.swift - https://developer.apple.com/forums/content/attachment/91ff1182-42d3-48b8-9f8d-be765ee32c43AddView - https://developer.apple.com/forums/content/attachment/06f1b340-4770-49f4-b530-6f27c1b50490CardsView - https://developer.apple.com/forums/content/attachment/4291deb9-ce02-4769-8ccf-d02d9d6908b8ContentView - https://developer.apple.com/forums/content/attachment/12cc3378-8769-4cee-8faa-63af72cc7ab3Info.swift - https://developer.apple.com/forums/content/attachment/89c6710c-faed-4b93-8152-a0f9a95806c8
Lunch Card - https://apps.apple.com/us/app/lunch-card-id-card-manager/id1546901593 is finally out. But while in App Store Connect, I misclicked while trying to create a macOS version of the app and clicked tvOS. Now, I can't delete it. It gives me the option, but gives me an error. It literally just says:
An error has occurred. Try again later.
After trying to Google this problem, it didn't come to any avail.
Now I'm stuck with a tvOS version of my app that I have no plans to develop (because who needs to scan an ID card on a TV?).
So this is an interesting problem.
In my Lunch Card app, AddView, which I'm already having a problem with - https://developer.apple.com/forums/thread/672848, has an unused variable:
@Environment(\.colorScheme) var colorScheme
The app works fine with it, even if the color scheme doesn't match. I thought a good solution for the color scheme problem would be to get rid of this variable in AddView itself.
Apparently, that's not the right thing to do, and when getting rid of it, I get this message:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
Like I said, the app works fine with it. It's not used in AddView at all yet still gives me an error basically saying - and correct me if I'm wrong - that there's not enough memory to build body, even though the unused variable reverses that.
You can find the latest code for AddView here. - https://developer.apple.com/forums/thread/672848
While I ponder my problem with the tvOS version of Lunch Card, I have another one on my hands.
I'm creating an app clip version of Lunch Card, and it works perfectly.
But, when archiving this build, it gives me this error:
The com.apple.developer.parent-application-identifiers entitlement [...] of an App Clip must match the application-identifier entitlement [...] of its containing parent app.
I've recreated the app clip target, and looked at the entitlements, but I don't see anything wrong. The error, of course, gives me the two different identifiers, but I don't know how I would revert that.
I already have the APIs and keys in, but how do I put in the permission popup on a SwiftUI view? I'd really love it if somebody could help, because the only thing I see is a documentation about the whole thing, but it's telling me where to put the code. Everywhere I put it, it just gives me an error.
This is about a different app now.
I'm developing a companion app for an online magazine called The Hair Society (thehairsociety.org). Thing is, I need to retrieve the existing articles for the app to actually make sense.
I've tried many different solutions (NewsAPI, SwiftyJSON, Kanna, etc.) but none of which have worked - which is probably due to my lack of sense of where to put things.
Here's some code:
ArticlesView + ArticleRow:
//
// ArticlesView.swift
// Hair Society Go
//
// Created by Joshua Srery on 11/29/20.
//
import SwiftUI
struct ArticlesView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 5) { item in
NavigationLink(destination: ArticleView(title: "Replace with Title var", image: "Replace with Img var", content: "Replace with Content var", author: "Replace with Author var", date: "Replace with Date var")) {
ArticleRow(image: "Replace with Img var", title: "Replace with Title var", author: "Replace with Author var", date: "Replace with Date var")
}
}
}
.navigationTitle("Articles")
.toolbar(content: {
Menu {
Button("Date", action: {})
Button("Title", action: {})
Button("Customize…", action: {})
} label: {
Label("Filter", systemImage: "line.horizontal.3.decrease.circle")
}
})
}
}
}
struct ArticleRow: View {
let image: String
let title: String
let author: String
let date: String
var body: some View {
HStack {
Image(image)
.resizable()
.frame(minWidth: 75, maxWidth: 100, maxHeight: 75)
.cornerRadius(12)
VStack(alignment: .leading, content: {
Text(title)
.font(.headline)
Text("\(author) • \(date)")
.font(.subheadline)
})
}
}
}
That filter option will probably show in a later thread.
ArticleView:
//
// ArticleView.swift
// Hair Society Go
//
// Created by Joshua Srery on 12/16/20.
//
import SwiftUI
struct ArticleView: View {
let title: String
let image: String
let content: String
let author: String
let date: String
var body: some View {
ScrollView {
GeometryReader { geometry in
ZStack {
if geometry.frame(in: .global).minY <= 0 {
Image(image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(y: geometry.frame(in: .global).minY/9)
.clipped()
} else {
Image(image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width, height: geometry.size.height + geometry.frame(in: .global).minY)
.clipped()
.offset(y: -geometry.frame(in: .global).minY)
}
}
}
.frame(height: 400)
VStack(alignment: .leading) {
Text(title)
.font(.largeTitle)
.bold()
.lineLimit(nil)
.padding(.top, 10)
Text("\(author) • \(date)")
.foregroundColor(.gray)
.padding(.top, 10)
AdvertisementView(ad: "Replace with Ad var")
.padding(.top)
.frame(width: 350)
Text(content)
.lineLimit(nil)
.padding(.top, 15)
}
.frame(minWidth: 350)
.padding(.horizontal, 15)
}
.edgesIgnoringSafeArea(.top)
}
}
struct AdvertisementView: View {
let ad: String
var body: some View {
VStack {
Text("Advertisement")
.font(Font.system(.body).smallCaps())
.foregroundColor(.secondary)
.bold()
.tracking(1.0)
Image(ad)
.resizable()
.frame(height: 37.5)
Text("Advertisement")
.font(Font.system(.body).smallCaps())
.foregroundColor(.secondary)
.bold()
.tracking(1.0)
}.padding(.vertical, 3)
.background(Color.white)
.cornerRadius(25)
.shadow(radius: 10)
}
}
I want to use the WordPress REST API (which is the only API I think WordPress offers) to grab from my website: Posts
Forum Posts
Listings
Log in Info
How would I implement these things into an Xcode project using the SwiftUI lifecycle (no App/SceneDelegate)?
By the way, this is the last expansion before the app reaches its first finished build. Almost there!
I've seen many forums about this problem, but they've never solved my problem. Especially with the new .navigationTitle modifier instead of the - what seems to be - deprecated .navigationBarTitle.
This problem is evident in two views: CardFullView - which is just a detail view for the cards - and ColophonView - which is basically the Lunch Card version of Widgetsmith's colophon.
There's a huge gap above the view. For CardFullView, it's an inline navigation bar. ColophonView doesn't have one - it has a custom title which is basically just a ZStack rectangle.
Here's the full code for each view:
CardFullView: Note: CardView has split into two views, CardViewV and CardViewH. These are for features I still need to develop. Please don't mind them.
import SwiftUI
struct CardFullView: View {
let cname: String
let name: String
let id: String
// let code: String
var body: some View {
NavigationView {
CardViewV(name: name, id: id)
.navigationBarTitle(Text(cname), displayMode: .inline)
}
}
}
ColophonView:
struct ColophonView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
ScrollView {
VStack {
ZStack {
Rectangle()
.frame(maxWidth: .infinity, maxHeight: 75)
.foregroundColor(colorScheme == .light ? .white : .black)
.overlay(LinearGradient(gradient: Gradient(colors: colorScheme == .light ? [Color.white, Color("Light")] : [Color.black, Color("Light")]), startPoint: .topLeading, endPoint: .bottomTrailing))
HStack {
Text("Colophon")
.font(.system(.headline, design: .monospaced))
Text("by Joshua Srery")
.font(.system(.body, design: .monospaced))
.foregroundColor(.secondary)
}
}
.cornerRadius(25)
.padding()
ScrollView {
Text("Lunch Card was built in Xcode 12.3+ on a 15 inch, 2019 Macbook Pro running macOS Big Sur.\n\nI'd like to thank the LPS school district for letting the idea spark. Lunch Card wouldn't exist without the new precautions.\n\nI would also like to thank Apple Developer Forums user OOPer for providing most of the code to power the card system. This app would still be in development (probably for more than a year) without their help.")
.padding()
.font(.system(.body, design: .monospaced))
}
}.edgesIgnoringSafeArea(.all)
}
}
}
Remember, these views work as expected. The only issue is the hierarchy that is adding to that gap.
Please look at previous threads for other code samples.
Another expansion of the Lunch Card app. I figured out my problem in the last thread so I started a new one.
Please see my last thread for code references.
To save List data without using Core Data — because that would be a bit of a hassle — I was told to use Codable to convert the array to Data and export that into a file.
Thing is; I don’t know how I’d convert cardsInfo into Data, furthermore, convert to JSON.
Once I figure out how to convert that to JSON and into writable data, I’ll pretty much be good to go.
Lunch Card is almost finished, though there's one major part that will need to be addressed for it to go out of development.
As from last threads, I've implemented the Cards system, made it so you can change colors, include multiple cards, and the cards stay...until you quit the app. Restarting the app (dismissing it in App Switcher and opening it back up) doesn't save all your cards. That would be a major inconvenience, as it'd be more like an App Clip than an actual App.
I've considered Core Data, writing to a text file, etc., but (1) I can't decide which one would be more convenient and (2) I don't know how I would implement it (ex. CoreData would be hard to implement especially after almost finishing it without).
Please tell me if I need to provide more code than I have in previous threads. Here they are from most recent to earliest:
Force color scheme at the press of a button - https://developer.apple.com/forums/thread/670593
Variable overriding itself when adding to a list - https://developer.apple.com/forums/thread/670617
I have a function in one view, but a button that should call it in another - https://developer.apple.com/forums/thread/670536
How to pass data from a TextField to a List (SwiftUI) - https://developer.apple.com/forums/thread/669852
The original How to pass data... post code is pretty outdated, so I would refer to the more recent threads.
Homestretch...!
Note: CardsInfo.swift and SheetInfo.swift merged to make Info.swift:
//
// Info.swift
// Lunch Card (iOS)
//
// Created by Joshua Srery on 12/21/20.
// Additional code by OOPer on Apple Developer Forums
//
import Foundation
struct CardInfo: Identifiable {
var name: String = ""
var id: String = ""
var cname: String = ""
var code: String = ""
}
class CardsInfo: ObservableObject {
@Published var newCard: CardInfo = CardInfo()
@Published var cards: [CardInfo] = []
func add() {
cards.append(newCard)
}
}
class SheetInfo: ObservableObject {
@Published var showSheetView = false
}
This is once again about that Lunch Card app.
There's a section in my app's settings that controls the appearance - color scheme, if you will - of the whole app. The three options are System Default
Light
Dark
The picker is set up, and it works as normal, but what would I specify the action of these buttons to be to force a certain color scheme, whether that be dark, light, or auto.
Here's the code for where the picker is:
struct SettingsView: View {
@State private var selectedAppearance = 1
var body: some View {
// ...
Picker(selection: $selectedAppearance, label: Text("Appearance")) {
Button(action: {
// Change app color scheme to be auto
}) {
Text("System Default")
}.tag(1)
Button(action: {
// Change app color scheme to be light
}) {
Text("Light")
}.tag(2)
Button(action: {
// Change app color scheme to be dark
}) {
Text("Dark")
}.tag(3)
}
// ...
}
This is yet another expansion of my TextField and function questions.
The code for my Lunch Card app is pretty much set in stone, except for a little bug with a variable. This bug happens when you create a new card. Here's the code for both CardsView and AddView.
CardView:
//
// CardsView.swift
// Lunch Card (iOS)
//
// Created by Joshua Srery on 12/17/20.
// Additional code by OOPer on Apple Developer Forums
//
import SwiftUI
struct Card: Identifiable {
let id = UUID()
let title: String
}
struct CardsView: View {
@StateObject var cardsInfo = CardsInfo()
@StateObject var sheetInfo = SheetInfo()
@State private var editMode = EditMode.inactive
var body: some View {
NavigationView {
List {
ForEach(cardsInfo.cards) { cards in
NavigationLink(destination: CardFullView(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)) {
CardRow(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
}
}
.onDelete(perform: onDelete)
.onMove(perform: onMove)
}.listStyle(PlainListStyle())
.navigationTitle("Cards")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
self.sheetInfo.showSheetView.toggle()
}) {
Image(systemName: "plus")
}
}
}
.environment(\.editMode, $editMode)
.sheet(isPresented: $sheetInfo.showSheetView) {
AddView(cardsInfo: cardsInfo, sheetInfo: sheetInfo)
}
}
}
private func onDelete(offsets: IndexSet) {
cardsInfo.cards.remove(atOffsets: offsets)
}
private func onMove(source: IndexSet, destination: Int) {
cardsInfo.cards.move(fromOffsets: source, toOffset: destination)
}
}
AddView:
//
// AddView.swift
// Lunch Card (iOS)
//
// Created by Joshua Srery on 12/18/20.
// Additional code by OOPer on Apple Developer Forums
//
import SwiftUI
struct AddView: View {
@ObservedObject var cardsInfo: CardsInfo
@ObservedObject var sheetInfo: SheetInfo
var body: some View {
NavigationView {
VStack {
CardView(name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
TextField("Name", text: $cardsInfo.newCard.name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.shadow(radius: 10)
TextField("ID", text: $cardsInfo.newCard.id)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
.shadow(radius: 10)
TextField("Card Name", text: $cardsInfo.newCard.cname)
.textFieldStyle(RoundedBorderTextFieldStyle())
.shadow(radius: 10)
Button(action: {
cardsInfo.add()
sheetInfo.showSheetView = false
}) {
Text("Create")
.bold()
}
.disabled(self.cardsInfo.newCard.name.isEmpty		self.cardsInfo.newCard.id.isEmpty		self.cardsInfo.newCard.cname.isEmpty)
.foregroundColor(.white)
.padding()
.padding(.horizontal, 100)
.background(Color.accentColor)
.cornerRadius(10)
}.padding()
.navigationTitle(cardsInfo.newCard.cname)
.navigationBarTitleDisplayMode(.inline)
}
}
}
AddView shows as a sheet.
When looking, you can see that cardsInfo.newCard is being used to signify - if not obvious - a new card. But, the bug here is that when you add multiple cards, the previous cards are being overridden.
A solution I'm thinking of is adding the already-added cards into some kind of database or text file, but (1) I have literally no idea how I would approach that, and (2) that would probably mean rewriting about 3/4 of the existing code, which is a path that I am trying to avoid.