Swift Notes

| 45 minutes

Fresh Start!| Bottom| JSON Explained

🕶️Paul Hudson Hacking With Swift|Antoine Van Der Lee|Code with Chris| XCode Keyboard Shortcuts | Swift with Majid | swiftyplace.com

  • ⌘ = Command
  • ⌥ = Option/Alt
  • ⇧ = Shift
  • ⌃ = Control
  • ←→ ↑↓ = Arrow keys
  • ↩ = Enter

Ok, so per 🕶️Paul Hudson, Hacking With Swift, I’m going to document my path of learning to program in swift. I figured, after watching Apple blast through the universe for the past 20 years, maybe, now is the time to commit and learn how to write iPhone/ipad/iwatch apps.
I started in late spring of 22 reading the manual at swift.org I certainly didn’t understand it all but I got through the manual. Once that was done, I knew I needed to find an on-line course to really get the juices flowing and stumbled into code with chris. He offered a free 3 hour course on-line that provided a lot of “a ha” moments. He quickly was providing answers to questions I had from reading the manual. I was hooked so I bought his year long subscription for $200 and started my first official course, “swift in 90 days”. (He has many more courses and I plan to take all of them.) It was fantastic!

There were challenges after most lessons to re-enforce the information reviewed and it was the key to understanding the material. That being said, as I would complete challenges, I would look all over the internet for help and Paul Hudson, hackingwithswift was a recurring theme. There are several other sites I use as reference and will list those further down. I could play and replay the lessons until I understood them. This new way of learning is much more forgiving. I’m not rushing to scribble notes as the professor talks. Just play, pause, rewind, play again. It’s great.

I liked codewithchris because you could watch him write and debug the programs “live”. He would step through the program, building and testing as he went. The process was really great, even when he made mistakes. By the end of the 90 day course, he was flying through the coding and it was fun being able to keep up. We learned how to hook into other apps like yelp and apple maps. (as an example) It was extremely powerful. He also covered GitHub.com, a fantastic source control tool that is on-line. Everybody uses it and it’s a phenomenal source for other programmers open source swift projects. The resources available to learn swift are wide and deep.

Hackingwithswift is a different approach. He starts with the basics of every type of property, function, method, structure, class, protocol, inheritance, etc. He teaches and re-enforces the “why’s” of swift. Both resources have been great and they complement one another.

For now, I’m switching gears and pursuing hackingwithswift: 🕶️swiftUI in 100 days I’m on day 19. So far it has been mainly review but it has been deeper and more thorough. Paul also has an iPhone app called unwrapped that is free. He gives quick tutorials and then quizzes you. It’s been really good as well. He lists sources here

Ok, so that’s all I have for now. p.s. Ben is going to learn swift as well. He’s started!

Day 28: 13NOV22 Still trucking along, I really enjoy the pace and material! The challenges are small enough and concise enough to cover the new material yet don’t require a lot of setup to progress. 🕶️Day 28

Day 38: 26NOV22 The challenges are definitely getting more challenging and solutions are certainly pushing me into new territory. So the lightbulb went on….when going to another view, you are instantiating a new struct. So all values you pass are initialized. Not sure why I had to piece that together….

learned how to extend a FormatStyle:

extension FormatStyle where Self == FloatingPointFormatStyle<Double>.Currency {
    
    static var localCurrency: Self {
        .currency(code: Locale.current.currency?.identifier ?? "USD")
    }
}

learned how to extend a view:

extension View {
    func style(for item: ExpenseItem) -> some View {
        
        switch  item.valueCategory {
        case .cheap:
            return self
                .foregroundColor(.green)
                .font(.body)
        case .normal:
            return self
                .foregroundColor(.yellow)
                .font(.title3)
        case .extravagent:
            return self
                .foregroundColor(.red)
                .font(.title)
        }
    }
}

And forcing myself to use enums…I think this will pay off.

19DEC22 - Day 49 Complete! 100 days of swiftUI is simply fantastic! I subscribe to HWS+ as well, totally worth it. (Paul provides the solutions to the challenges and so much more) I love Paul’s saying: “Don’t spend all your time sharpening your pencil when you should be drawing” Loading data from a json file, saving to userdefaults, understanding how to pass data from one view to another, understanding structures, extensions…muy excelente!

The next part of the course is all about data. (which I’m pretty excited about) I haven’t read ahead, but I’m hoping to learn about databases. Ultimately, I want to write an app (still not decided) that will leverage a webserver to store data. The fewer changes I have to make at the app store, the better.

I have also used some of Paul Hudson’s books to clarify things. SwiftUI by example was very helpful. I bought all of them and will read all of them.

I kind of feel sorry for book stores. Any books they have are out-of-date as soon as they’re printed. Paul keeps his books current. A very nice feature.

So on to day 50, only 50 more to go!

13AUG23 - yes I have taken a long break. But I am back.

🕶️Day 58 things are getting more complicated

🕶️Dynamically filtering fetchrequests

🕶️Steps to create coredata model by hand

🕶️ one to many relationships coredata

Cored data failed to load model stackoverflow, saved my bacon

let container = NSPersistentContainer(name: “CandyDataModel”)

Examples of NSPredicate usage https://nspredicate.xyz

Notes on enum swiftbysundell

Look at this https://www.objc.io

19AUG23 - Finished wrap up for project 12, very powerful stuff - Generic Object, enum for filtering and sort order passed directly from form

🕶️core-data Review lots to absorb

27AUG23

🕶️Working on the Day 60 challenge, projects 10-12

I’m using the HWS+ to augment this: Both of these go into greater detail (although much quicker discussions) of core data and json.

🕶️7 swifty words

🕶️spacee-out

5 Steps to setup CoreData:

  1. Data Model
  2. Setup Apple Permissions
  3. Create Class for CoreData
  4. Inject into swiftui
  5. Make less annoying by creating computed values that hide the optional values of coredata

Working on steps to quickly setup json structures

🕶️how to download json and decode

Restart top

24NOV23

Command Decision - Starting from scratch. I’m not consistent so every time I sit down, I have to review 3 or 4 lessons. So let’s just start again and let things sink in.

Apple has upgraded so many things AND, Paul Hudson has updated the material to reflect the changes. Why not just start from the beginning to learn/relearn the fundamentals?

Here we go: option + shift + 8 gives the ° symbol. Learn something everyday. Finished Checkpoint 1. OK, this is easy but still learning tidbits of info.

🕶️next up: dictionaries

26NOV23

Finished days 3 - 6 This is a great review.

type inference:

var emptyArrayOfStrings = [String]()
var emptySet = Set<Int>()
var emptyDictionary = [String: String]()

next up Day 7

27NOV23

Finished day 7, 8, and checkpoint 4

Closures tomorrow!

These is definitely easier the second time around. It’s even fun.

28NOV23

I finally understand closures! Use a closure for single usage cases. Otherwise create a function.

29NOV23

Finished Day 10

“Structs are one of the ways Swift lets us create our own data types out of several small types.” - Paul Hudson

tomorrow Day 11

30NOV23

Day 11

Key point that now makes sense the 2nd time around. Creating example data is an important part of creating sample views for you iphone app.

🕶️static properties and methods

01DEC23

Starting on objects.

This is the Key Difference between Structs and Objects from Paul:

“I’ve already said that SwiftUI uses structs extensively for its UI design. Well, it uses classes extensively for its data: when you show data from some object on the screen, or when you pass data between your layouts, you’ll usually be using classes.”

Straight from 🕶️Paul: classes and structs

Classes and structs give Swift developers the ability to create custom, complex types with properties and methods, but they have five important differences:

  • Classes do not come with synthesized memberwise initializers. Authors of classes must write their own initializer by hand. This way, you can add properties as much as you want without affecting the initializer for your class, and affecting others who inherit from your class. And when you do decide to change your initializer, you’re doing so yourself, and are therefore fully aware of the implications for any inheriting classes.
  • One class can be built upon (“inherit from”) another class, gaining its properties and methods.
  • Copies of structs are always unique, whereas copies of classes actually point to the same shared data.
  • Classes have deinitializers, which are methods that are called when an instance of the class is destroyed, but structs do not.
  • Variable properties in constant classes can be modified freely, but variable properties in constant structs cannot.

04DEC23

I finished checkpoint 7 which is classes, inheritance, and initializers 🕶️This was particularly important - initializer

per Paul: Notice how the method has slightly different naming now: when we return a new value we used trimmed(), but when we modified the string directly we used trim(). This is intentional, and is part of Swift’s design guidelines: if you’re returning a new value rather than changing it in place, you should use word endings like ed or ing, like reversed().

var quote = "   The truth is rarely pure and never simple   "

extension String {
    func trimmed() -> String {
        self.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    mutating func trim() {
        self = self.trimmed()
    }
}

quote.trim()

Learning how to use extensions. When you use an extension, you can access the other methods of the type. example:

extension String {
    var isLong: Bool {
        return count > 25
    }
}
let john = "John Robert Deranian"
print(john.isLong)
print(john.count)

output:
false
20

From Example Above: count is another method of String so I can call it directly…very important point. And to reiterate all returned properties are computed properties

Up Next: 🕶️protocol extensions

7DEC23 Finished Day 13 checkpoint 8. I added an enum to the protocol type that worked perfectly!

protocol Building {
    var rooms: Int {get set}
    var cost: Int {get set}
    var agent: String {get set}
    var type: Building_Type {get}
}
extension Building {
    func saleSummary() {
        print("\(agent) sold this \(type) with \(rooms) rooms for the cost of \(cost)")
    }
}

enum Building_Type {
    case House, Office
}
struct House: Building {
    var type = Building_Type.House
    var rooms: Int
    var cost: Int
    var agent: String
}

struct Office: Building {
    var type = Building_Type.Office
    var rooms: Int
    var cost: Int
    var agent: String
}

var alaska = House(rooms: 6, cost: 640_000, agent: "Missy MoneyBags")
var corporate = Office(rooms: 90, cost: 2_000_000, agent: "Jerry Malone")
alaska.saleSummary()
corporate.saleSummary()
print(alaska.type)
print(corporate.type)

running everything through xcode has been the key to picking up syntax errors and hints to fix syntax.

🕶️Day 14 Optionals

9DEC23 Optionals are a key design feature of swift. Any data type can be optional.

🕶️Summary of Optionals

From Paul Hudson:

In these chapters we’ve covered one of Swift’s most important features, and although most people find optionals hard to understand at first almost everyone agrees they are useful in practice.

Let’s recap what we learned:

  • Optionals let us represent the absence of data, which means we’re able to say “this integer has no value” – that’s different from a fixed number such as 0.
  • As a result, everything that isn’t optional definitely has a value inside, even if that’s just an empty string.
  • Unwrapping an optional is the process of looking inside a box to see what it contains: if there’s a value inside it’s sent back for use, otherwise there will be nil inside.
  • We can use if let to run some code if the optional has a value, or guard let to run some code if the optional doesn’t have a value – but with guard we must always exit the function afterwards.
  • The nil coalescing operator, ??, unwraps and returns an optional’s value, or uses a default value instead.
  • Optional chaining lets us read an optional inside another optional with a convenient syntax.
  • If a function might throw errors, you can convert it into an optional using try? – you’ll either get back the function’s return value, or nil if an error is thrown.
  • Optionals are second only to closures when it comes to language features folks struggle to learn, but I promise after a few months you’ll wonder how you could live without them!

10DEC23 Swfit Review. Don’t blink or you’ll miss something!

🕶️All 14 days in 1 hour!

Paul’s Glossary of Terms reference page

Day 16

26DEC23

Finished Day 19, first challenge complete. And now I’m finally running into a version issue. My old mac pro won’t take the latest os so it won’t take the latest version of xcode.

I have been waiting to buy a new computer as long as possible. Oh well.

Day 20

28DEC23

🕶️This is an excellent review of stacks, colors, frames gradients, buttons and alerts

🕶️Gradients

Very handy background notes

struct ContentView: View {
    var body: some View {
        VStack {
            RadialGradient(colors: [.blue, .black], center: .center, 
             startRadius: 20, endRadius: 200)
            
            AngularGradient(colors: [.red, .yellow, .green, .blue, 
               .purple, .red], center: .center)
            
            Text("Your content")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .foregroundStyle(.white)
                .background(.indigo.gradient)
            
        }
    }
}

// https://www.hackingwithswift.com/books/ios-swiftui/buttons-and-images

// https://www.hackingwithswift.com/books/ios-swiftui/showing-alert-messages

Button("Show Alert") {
    showingAlert = true
}
.alert("Important message", isPresented: $showingAlert) {
    Button("OK", role: .cancel) { }
} message: {
    Text("Please read this.")
}

29DEC23

🕶️Finished guess the flag

nice addition: static let myArray = [ 1,2,3,etc] this is constant that is not part of structure instance.

🕶️Static properties and methods

you assign this with myTempArray = Self.myArray

and how to remove a value from and array: countries.remove(at: correctAnswer) where correctAnswer was an index

Day 23

Today is our first technique project, and we’re focusing on two fundamental components of SwiftUI: views and modifiers. We’ve been using these already, but we’ve kind of glossed over exactly how they work. Well, that ends today: we’ll be going through lots of details about what they are, how they work, and why they work as they do.

30DEC23 This is very very powerful!

🕶️Custom modifiers

To create a custom modifier, create a new struct that conforms to the ViewModifier protocol. This has only one requirement, which is a method called body that accepts whatever content it’s being given to work with, and must return some View.

Key Point: Tip: Often folks wonder when it’s better to add a custom view modifier versus just adding a new method to View, and really it comes down to one main reason: custom view modifiers can have their own stored properties, whereas extensions to View cannot.

struct Watermark: ViewModifier {
    var text: String

    func body(content: Content) -> some View {
        ZStack(alignment: .bottomTrailing) {
            content
            Text(text)
                .font(.caption)
                .foregroundStyle(.white)
                .padding(5)
                .background(.black)
        }
    }
}

extension View {
    func watermarked(with text: String) -> some View {
        modifier(Watermark(text: text))
    }
}

Add a watermark to any view:

Color.blue
    .frame(width: 300, height: 200)
    .watermarked(with: "Hacking with Swift")

finished the challenge Day 24

It is definitely much easier the second time around. It’s making a lot more sense. Views within views, very cool

🕶️Consolidation Day Projects 1-3

31DEC23

🕶️very good review

and This: Key points

01JAN24 Happy New Year Everyone! Ok, let’s get to work

learned a new shortcut: ctrl + cmd + space gives emoji menu in xcode

Paul used emoji instead of system images for this challenge. There has to be a way to increase the size of the system images besides .large which isn’t.

I also tried to use enums with raw values but that didn’t work. I wanted to pick a random value from the enum that would pull the name and the image to use. The case logic wasn’t any better.

The simple array of emoji’s worked perfectly, one array with the symbols built in.

I also tried to use a dictionay, but the randomElement was not helping without more code.

Day 26

Debugging in Xcode 11

04JAN24

A Date is independent of a particular calendar or time zone. To represent a Date to a user, you must interpret it in the context of a Calendar. Review this:

static var defaultWakeTime: Date {
    var components = DateComponents()
    components.hour = 7
    components.minute = 0
    return Calendar.current.date(from: components) ?? .now
}

Note the use of static var. Another var was using this as the struct was initializing. By making defaultWakeTime a static variable, it belongs to the ContentView struct itself rather than a single instance of that struct. This in turn means defaultWakeTime can be read whenever we want, because it doesn’t rely on the existence of any other properties. Great Review of Static Properties and Methods

Very Handy way to pluralize a prompt based on value

Stepper("^[\(coffeeAmount) cup](inflect: true)", value: $coffeeAmount, in: 1...20)

From Paul:

This is odd syntax, I know, but it’s actually a specialized form of Markdown, which is a common text-format. This syntax tells SwiftUI that the word “cup” needs to be inflected to match whatever is in the coffeeAmount variable, which in this case means it will automatically be converted from “cup” to “cups” as appropriate.

For the challenge, 🕶️Day 28 BetterRest Paul used a computed property instead of 3 onChanges. Much cleaner. I still like my layout though.

Day 29

06JAN24

Per Paul

The job of List is to provide a scrolling table of data. In fact, it’s pretty much identical to Form, except it’s used for presentation of data rather than requesting user input. Don’t get me wrong: you’ll use Form quite a lot too, but really it’s just a specialized type of List.

🕶️List

🕶️Tips and Tricks

🕶️Word Scramble

Adding to bottom of screen like this: (score, format: .number) is mine, I used a double for scoring :)

.safeAreaInset(edge: .bottom) {
                Text("Score \(score, format: .number)")
                    .frame(maxWidth: .infinity)
                    .padding()
                    .background(.blue)
                    .foregroundStyle(.white)
                    .font(.title)
            }

Paul kept the guard theme going:

guard  answer != rootWord  else {
            wordError(title: "That's the word already", message: "Try Again")
            return
        }

I was just going to use:

if answer == rootWord { wordError…. }

🕶️next up: Animations

My own thoughts of the day: vim, organizing your thoughts, json files, README.md

use vim in xcode!

xcode shortcuts another one

XCode Keyboard Shortcuts - Local Copy

Organizing your thoughts // MARK: - // TODO: // FIXME:

By pressing Control + 6, you can browse the contents of the file using the jump bar at the top of the editor. The dash or hyphen in the MARK comment adds a horizontal line or separator line above the title. By using this technique, you split the contents of the source file up into sections that are easier to browse and manage.

What is the jumpbar?

You could take it one step further by adding a build phase that generates a warning for every TODO and FIXME that hasn’t been addressed. This is very useful since you probably don’t want to ship an application that has one or more TODO’s or FIXME’s.

You can also use SwiftLint for this. SwiftLint is easy to add to a project using CocoaPods. After installing SwiftLint, create a build phase for SwiftLint and add the todo rule to the SwiftLint configuration file at the root of the project. When you build the project, SwiftLint automatically generates warnings for any TODO’s and FIXME’s.

json in swift avanderlee

README.md Professional Readme Guide

07JAN24

Comments on Animation Challenge

.rotation3DEffect(.degrees(picked == number ? 360 : 0), axis: (x: 0 , y: 1, z: 0))
.animation(.default, value: picked)

.animation is looking for a change in the value picked in example above. It fires as soon as it does. To make objects behave differently, e.g. a vstack of buttons, you can use a ternary operator (wtf) to control how the animation behaves for each button. The animation is still firing on the change of picked in this case, but the behaviour has changed for each button. Very clever and very important to understand.

🕶️on Day 35!

review swiftdata preview appcoda tips on optionals

navigationstack: https://sarunw.com/posts/how-to-pop-view-from-navigation-stack-in-swiftui/ This Was good! : https://stackoverflow.com/questions/57130866/how-to-show-navigationlink-as-a-button-in-swiftui Changed to this: https://stackoverflow.com/questions/75146794/how-to-replace-initdestinationtagselectionlabel-with-navigationlinkvalue

Animating views and transitions 🍏

https://sarunw.com/posts/swiftui-form-styling/

https://github.com/amosgyamfi/open-swiftui-animations

13JAN24 I have finished the challenge: https://www.hackingwithswift.com/guide/ios-swiftui/3/3/challenge

Since this was my second time, I was able to use tools that you don’t learn until later, namely: navigationDestination to another view. The logic of passing all your data to the new view/struct was finally ingrained in my head. Even setting up the previews with dummy data makes sense now.

Paul’s technique of using static values didn’t work for me on the preview. I need to find out why.

Animations? What is there to say except there is a lot a material out there. There are 3rd party libraries for this. We’ll see when/if Paul has a day on linking to external libraries.

14JAN24

🕶️[Day 36](Starting https://www.hackingwithswift.com/100/swiftui/36)

Learning how to use JSONEncoder to store data in Userdefaults with Codable, which allows you to archive more than simple strings i.e. structs into Userdefaults.

🕶️Next up: building a list we can delete from

I had to upgrade my old macpro (black trashcan) to a mac studio, because it would take the latest version of xcode so I couldn’t work in ios17. It’s pretty hard to build apps for the iphone if you can’t build to the latest version.

In apple’s defense, I have been using my old macpro for 7 years. It’s been a great box and will now have more mundane uses like a timemachine server.

15JAN24

https://www.hackingwithswift.com/100/swiftui/37

We’re going to write an app with @Observable, sheet(), Codable, Userdefaults, onDelete()

Everything is intuitive here except for IndexSet and the ability to remove with offsets. I’m going to find a better explanation of IndexSet and offsets

struct ContentView: View {
    @State private var expenses = Expenses()
    var body: some View {
        NavigationStack {
            List {
                ForEach(expenses.items, id: \.name) { item in
                    Text(item.name)
                }
                .onDelete(perform: removeItems)
            }
            .navigationTitle("iExpense")
            .toolbar {
                Button("Add Expense", systemImage: "plus") {
                    let expense = ExpenseItem(name: "Test", type: "Personal", amount: 5)
                    expenses.items.append(expense)
                }
                EditButton()
            }
        }
        
    }
    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }
}

use uuidgen from the command line to see one in action

Made the expenses unique by adding id = UUID(), and adding the Identifiable protocol to the struct like this:

struct ExpenseItem: Identifiable {
    let id = UUID()
    let name: String
    let type: String
    let amount: Double
}

Now in a ForEach loop, no need to specify the id. Swift knows

//ForEach(expenses.items, id: \.id) { item in // REMOVED replaced with line below
ForEach(expenses.items) { item in

dimisss takes two pieces:

@Environment(\.dismiss) private var dismiss

and in a button: dismiss()

🕶️Making Changes Permanent with userdefaults Below is a very important piece of code that I will probably see time and time again: didSet sees a change in items, re-encodes, and updates UserDefaults, On startup of app, init looks for UserDefaults and decodes, otherwise returns an empty array

items updated

.toolbar {
    Button("Save") {
        let item = ExpenseItem(name: name, type: type, amount: amount)
        expenses.items.append(item)
    }
}

Userdefaults updated

@Observable
class Expenses{
    var items = [ExpenseItem]() {
        didSet {
            if let encoded = try? JSONEncoder().encode(items){
                UserDefaults.standard.setValue(encoded, forKey: "Items")
            }
        }
    }
    
    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "Items") {
            if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
                items = decodedItems
                return
            }
        }

        items = []
    }
}

🕶️Tips and Tricks

Computed properties in swift swiftbysundell

🕶️Useful swift extensions

7 ways to organize SwiftUI Code

getting enums to work in a picker sarunw

my solution works, now to understand 🕶️Paul’s iexpense

I used enums to separate the lists. I did not rebuild the IndexSet() and push the itemsToBeDeleted onto a stack. It worked without this step. I’m working on the subview with my code. One more day and I’ll move on. Subviews will explained more without a doubt.

How to filter an array….much better than a foreach and pushing onto another array. Filter just does it! I did use my enum

    var personalItems: [ExpenseItem] {
        items.filter { $0.type == .Personal }
    }

    var businessItems: [ExpenseItem] {
        items.filter { $0.type == .Business }
    }

In the end, I used Paul solution mainly to understand how he did it. As always, very clever

18JAN24

🕶️For a change today what’s new in ios -17

This is part of the Hacking with Swift+ Membership that I highly highly recommend.

Big item I learned, you can add an if statement to a section on a list to change views… I thought I could only do this with bool state properties which are the chicken and egg problem. I try to initialize those from other values that are initializing and it fails…..

Trick to initialize a var after view is loaded

swiftui handbook conditional modifer

conditional views in swiftui medium

if and if let in viewbuilder

Very Good Conversation of view modifiers

🕶️ternary conditional operator

struct ContentView: View {
    @State private var viewIsBold = false
    var body: some View {
        Section{
            Text("Hello, World!")
            Button("toggle", action: {viewIsBold.toggle()})
        }
        //.modifier(ConditionalModifier(isBold : viewIsBold))
        //or
        .conditionalView(viewIsBold)
	// or ternary conditional operator works too: 
	// .font(viewIsBold ? .custom("HelveticaNeue-bold", size: 14) : 
            .custom("HelveticaNeue", size: 14))
    }
}

extension View {
    func conditionalView(_ value: Bool) -> some View {
        self.modifier(ConditionalModifier(isBold: value))
    }
}

struct ConditionalModifier: ViewModifier {
    var isBold: Bool
    func body(content: Content) -> some View {
        Group {
            if self.isBold {
                content.font(.custom("HelveticaNeue-Bold", size: 14))
            }else{
                content.font(.custom("HelveticaNeue", size: 14))
            }
        }
    }
}

All about adding comments in swiftui, particularly swift Markup This is the bomb!

https://www.advancedswift.com/comments-documentation-swift/

local copy of swift commenting

Xcode markup formatting ref 🍏

deep dive into previews

🕶️access and change data from another view

🕶️make a scrollview snap with paging

19JAN24

🕶️Finished the what’s new in ios17

20JAN24

Initializing a var and then populating

I learned how to use the new scroll features. More importantly, however, was the application’s use of JSON and a datacontroller. In one view, a struct was created with no values, and then populated with .onAppear. I was banging my head on this one.

@State private var unusedActivities = [Activity]() // empty array of structs created here

// Populated here!

.onAppear {
                unusedActivities = dataController.unusedActivities
            }

// Here's the magic!
 var unusedActivities: [Activity] {
        allActivities.filter { item in
            activityProgress.contains { other in
                item.id == other.id
            } == false
        }
    }

21JAN24

🕶️NavigationLinks and Stacks

🍏 Apple does a pretty good job of explaining it

mastering navigationstack majid

This one is particularly good navigationstack

A great simple explanation of JSON for swift:

🕶️Working with hierarchical codable data

🕶️Great One on generics

Now we’re down to formatting the views. You can specify the date formats from JSON, very powerful. Pay close attention to where you modify views e.g. scrollview, lazyvstack, image, etc. Paul used computed properities to format strings for the views, very clever.

Added an extension to Color. Color is part of Shapestyle, this extension was specific to Color

extension ShapeStyle where Self == Color {
    static var darkBackground: Color {
        Color(red: 0.1, green: 0.1, blue: 0.2)
    }

    static var lightBackground: Color {
        Color(red: 0.2, green: 0.2, blue: 0.3)
    }
}

🕶️jumping ahead how to make navigationstack return to root

READ THIS: https://dev.to/kuncans/the-new-navigationstack-navigationpath-for-swiftui-5cpa

22JAN24

Here’s some of Paul’s magic. Looking for items in another array: Using the crewname in mission.crew and looking for it in astronauts[crewname]

listed the crewmembers of a mission with mission.crew.map then searched the Dictionary: 🍏 astronauts, using the crewmember as the key

init(mission: Mission, astronauts: [String: Astronaut]) {
        self.mission = mission

        self.crew = mission.crew.map { member in
            if let astronaut = astronauts[member.name] {
                return CrewMember(role: member.role, astronaut: astronaut)
            } else {
                fatalError("Missing \(member.name)")
            }
        }
    }

🕶️There was a lot to this challenge: Moonshot

  • breaking up views,
  • I moved the initializers (Paul didn’t)
  • @AppStorage("showingGrid") private var viewGrid = true keeps state between sessions
  • formatting dates
  • Grouping views with state vars
  • initing the previews correctly: note the preferredColorScheme being readded
#Preview {
    
    let missions: [Mission] = Bundle.main.decode("missions.json")
    let astronauts: [String: Astronaut] = Bundle.main.decode("astronauts.json")
    
    
    return ListLayoutView(astronauts: astronauts, missions: missions)
       .preferredColorScheme(.dark)
}

My initial problem of the toolbar not working was simply because I had it in the wrong place. toolbar goes inside the navigationStack, not on it! (I knew this.)

Things are making sense. swiftui is really cool.

And now onto a subject that completely changed since I first started last year: Navigation

The Key difference is the use of NavgationPath

https://www.hackingwithswift.com/100/swiftui/43

How to track your navigationpath with an external class. I assume this is how you will always do it. One way uses a simple Int array, the other uses a navigationPath. The navigationPath requires extra care and feeding to store and retrieve the path. Pay attention.

🕶️This is money! How to save navigationstack paths using codable

Tomorrow: https://www.hackingwithswift.com/100/swiftui/45

23JAN24 Day 46

Part of the 🕶️navigation challenge is to replace a sheet with and navigationlink.

This helped greatly: https://stackoverflow.com/questions/74409227/toolbar-navigation-link

You can put a navigationlink in a toolbar. Path was not needed because this was the end of the line.

24JAN24 Finished the 🕶️navigation challenge

This helped swiftyplace navigationstack

Paul did not use the navigationPath at all for this challenge. ContentView had a NavigationStack. The subviews with the lists, only had the navigationlinks. This makes sense they’re really part of the overall ContentView. The navigationDestination does not have to be on the same view as the navigationStack, rather closer to the navigationLinks. I need to look more into this. But the big unexpected part was no mention of the navigationPath. I had too many navigationStacks but they worked.

🍏 From Apple - This Explains why:

Use a NavigationStack to present a stack of views over a root view. People can add views to the top of the stack by clicking or tapping a NavigationLink, and remove views using built-in, platform-appropriate controls, like a Back button or a swipe gesture. The stack always displays the most recently added view that hasn’t been removed, and doesn’t allow the root view to be removed.

So once you have created the navigationStack, you just add to the stack via the path directly or via navigationDestination.

https://ionic.io/blog/deploying-to-a-device-without-an-apple-developer-account

25JAN24 🕶️What we’ve learned up to this point

Links below are to the review material for each topic.

🕶️Reviewed What’s the difference between @ObservedObject, @State, and @EnvironmentObject?

🕶️sheet() and dismiss()

🕶️How to enable editing on a list using EditButton

🕶️Reading and writing basics: UserDefaults

🕶️Codable Cheat Sheet

🕶️Working with Identifiable

🕶️containerRelativeFrame

🕶️How ScrollView lets us work with scrolling data

🕶️Using Generics to load any kind of Codable data

NavigationLink and NavigationPath - review notes on 24JAN24

🕶️Customizing the navigationbar apperance

🕶️Placing toolbar buttons in exact locations based on function or personal choice

🕶️Key Points

Github Notes:

🕶️[Challenge](And now the challenge: https://www.hackingwithswift.com/guide/ios-swiftui/4/3/challenge)

26JAN

🕶️Complete Guide to sf symbols

list of all sf symbol names

effective debugging medium.com

debugging

27JAN24

BEST YET, navigationlink, path and enums betterprogramming

I have used the enum to control the path. The extra trick of adding values detailActivityView(Exercise) to an Enum (I’ll look up the technical jargon later) is key to making this work. Otherwise you’re defining the path and losing user values

Passing here: case .detailActivityView(let Exercise): DetailActivityView(exercise: Exercise, savedExercises: $savedExercises, homeNavigtionStack: $homeNavigtionStack) The whole point of the DetailActivityView was to show the selected Exercise. The enum was able to handle it. Awesome!

enum EnumNavigation: Hashable {
    case addActivityView, detailActivityView(Exercise), sfsymbolView, loggingView
}

.navigationDestination(for: EnumNavigation.self) { screen in
                switch screen {
                case .sfsymbolView: SfSymbolView()
                case .addActivityView: AddActivityView(exercises: exercises, 
                     savedExercises: $savedExercises, homeNavigtionStack: $homeNavigtionStack)
                case .detailActivityView(let Exercise): DetailActivityView(exercise: Exercise, 
                     savedExercises: $savedExercises, homeNavigtionStack: $homeNavigtionStack)
                case .loggingView: LoggingView(savedExercises: $savedExercises, 
                     homeNavigtionStack: $homeNavigtionStack, activityLogs: $activityLogs, 
                     checkedExercises: $checkedExercies)
                }
            }

struct Exercise: Codable, Hashable {
    var name: String
    let force: String?
    let level: String
    let mechanic: String?
    let equipment: String?
    let primaryMuscles: [String]
    let secondaryMuscles: [String]
    let instructions: [String]
    let category: String
    
    var displayedForce: String {
        force ?? "n/a"
    }
    
    var displayedMechanic: String {
        mechanic ?? "none"
    }
    
    var displayedEquipment: String {
        equipment ?? "none"
    }
    
}

Huge Thank You to Akshay Mahajan

https://www.appcoda.com/swiftui-togglestyle/

https://www.appcoda.com/swiftui-checkbox/

I could not for the life of me, init my dictionary in the view. I finally found this BUT IT DIDN’T WORK. I have yet to be successful doing anything with a dictionary. I’m ready to give up on those.

So building a class with two values: Exercise:Exercise and a checked:Bool.

bindable swiftui list elements swiftbysundell Highlights from Article:

struct NoteListView: View {
    @ObservedObject var list: NoteList

    var body: some View {
        List {
            ForEach($list.notes) { $note in
                NavigationLink(note.title,
                    destination: NoteEditView(
                        note: $note
                    )
                )
            }
        }
    }
}

Note that we can reference our closure’s $note input either with or without its dollar prefix, depending on whether we want to access the underlying value directly, or the binding that encapsulates it. Something that’s really nice about the above new syntax is that it’s actually fully backward compatible with all previous operating systems on which SwiftUI is supported — so on iOS, that means as far back as iOS 13. The only requirement is that we have to build our app using the compiler and SDK that’s included in Xcode 13.

Next UP: Write the log to disk. Sort/Filter exercises on difficulty, muscleGroup. Create Search Engine as well. Then, I think I’ll be done. You can add eyecandy all day, but that can come later.

31JAN24

https://www.appcoda.com/swiftui-confetti-animation/

https://blog.stackademic.com/swiftui-textfield-clear-button-a3070c5b587e

adding to json file with jq: https://gist.github.com/joar/776b7d176196592ed5d8

https://stackoverflow.com/questions/34543829/jq-cannot-index-array-with-string

jq insert

find and convert dates vims

remove new line from results

Today, I spent way too much time trying to add a field to my exercises.json file so that the exercises would be unique and the same everytime I loaded the app.

These 2 lines did it:

cat exercises.json | jq ‘.[] += {“uuid” : “Replace” }’

.,$ s/replace/=trim(system(‘uuidgen’))/g # need the trim to get rid of the /n

GOOD ONE how to print debugging swift

bindable property wrapper

migrating to observable protocol 🍏

02FEB24

random access collection

ForEach requires an ordered data source, a Set is unordered by definition.

A simple solution is to sort the Set for example by exercisename

ForEach(selectedItems.sorted{$0.exercisename < $1.exercisename})
The result of sorted is an array which is 'RandomAccessCollection compliant.

how to clear userdefaults

Search bar best practices swiftyplace

05FEB24 Maybe this will finally sink in.

How to get another view with button click

I’m trying to make a view searchable that is not at the root of the navigation stack. Adding the .searchable to stack displays the search window where I don’t want it and I’m trying not to change the way the app is setup. So I’m going to display sheeet with a new naviationstack and see where that takes me.

I imported json file with a uuid: instead of id:. swift is complaining about not “Identifiable”, so now I need to figure out how to make make my uuid: field identifiable.

I have been banging on this for several days: How to add searchable to a view that does not “have” a navigtionStack. Well, mine was embedded, so it turns out I could just add .searchable to the Vstack along with my search filter and Voila!, it worked.

Also, I happened to discover that you must have an id: field in your struct for Identifiable to work. In my case, my uuid field was uuid: so by putting in a computed property, swift stopped complaining

var id:UUID {
        return uuid.self
    }

🕶️Or, I could have remapped uuid: into id: with this

To make that happen we need to declare a CodingKeys enum: a mapping that Codable can use to convert JSON names into properties for our struct. This is a regular enum that uses strings for its raw values so that we can specify both our property name (the enum case) and the JSON name (the enum value) at the same time. It also needs to conform to the CodingKey protocol, which is what makes this work with the Codable protocol.

enum CodingKeys: String, CodingKey {
    case firstName = "user_first_name"
    case lastName = "user_last_name"
    case age
}

🕶️Common swiftui erros and to fix them

06FEB24

How to cite paper github markdown

🕶️Looking at the solution to the challenge habit tracking

Recurring theme: putting a systemImage in a Button. Here’s the syntax:

.toolbar {
	Button("Add", systemImage: "plus") {
		addingNewActivity.toggle() // changing state fires a sheet view
	}
}

Finally on to Day 50. I pretty much melted the last challenge and learned a lot of new stuff.

🕶️Loading a remote image

AsyncImage(url: URL(string: "https://drano.net/images/woodyharoldson_cartel.jpg")) { phase in
                if let image = phase.image {
                    image
                        .resizable()
                        .scaledToFit()
                } else if phase.error != nil {
                    Text("There was an error loading the image.")
                } else {
                    ProgressView()
                }
            }

🕶️Codable Cheat sheet

07FEB24

Finished Project 10 minus the challenge. The extra work I did on observable really paid off. The extra bits in this project:

  • enum CodingKeys: String, CodingKey { case _name =“name ….} to make the struct match with the json data
  • haptic, interesting, but not needed yet
  • one class was used in project to track all data throughout the views, very very handy
  • validating fields with one computed state property
  • 🕶️sending json data to a server, very big

🕶️Challenge tomorrow: Cupcake Corner

08FEB24

ios14 vs 15 alerts

Running your app in simulator or device 🍏

🕶️Warnings and Errors

clever extension:

extension String {
    var isReallyEmpty: Bool {
        self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }
}

var hasValidAddress: Bool {
            if name.isReallyEmpty || streetAddress.isReallyEmpty || 
               city.isReallyEmpty || zip.isReallyEmpty { // Paul's
            return false
        }

        return true
    }

VERY IMPORTANT!🕶️Swiftdata

09FEB24 Steps to use swiftData

  1. Create Class that will be the database:
import Foundation
import SwiftData

@Model // NOT @Observable
class Student {
    var id: UUID
    var name: String

    init(id: UUID, name: String) {
        self.id = id
        self.name = name
    }
}
  1. Appstruct add .modelContainer(for: Student.self)
import SwiftUI
import SwiftData

@main
struct Sandbox2App: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Student.self)
    }
}
  1. Add @Query and @Environmnet(.modelContext) var modelContext to main View (ContentView) This loads the database into RAM with @Query used
struct ContentView: View {
    @Query var students: [Student] // This queries all entries, you can filter as you like here
    @Environment(\.modelContext) var modelContext

To setup a datamodel in preview: many steps

#Preview {
    do {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: Book.self, configurations: config)
        let example = Book(title: "Test Book", author: "Test Author", genre: "Fantasy", 
                      review: "This was a great book; I really enjoyed it.", rating: 4)

        return DetailView(book: example)
            .modelContainer(container)
    } catch {
        return Text("Failed to create preview: \(error.localizedDescription)")
    }
}

Handy Trick: note the use of .constant…prevents user editing eventhough original view allows it

RatingView(rating: .constant(book.rating))
                .font(.largeTitle)

https://peterfriese.dev/blog/2019/xcode-shortcuts/

Xcode shortcuts 🍏

10FEB24

How to use macOS’s Character Viewer to type emoji and other symbols ⌘ Command ⌃ Control Space to pull emjoi viewer (once set in keyboard settings)

Part of the Challenge is formatting a date:

format date to string

More clever: Swift date formatting swiftyplace

Mine was more direct:

Text(formatDate(book.date))
  .font(.footnote)

func formatDate(_ date: Date) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "ddMMMyyyy hh:mm"
        let dateString = dateFormatter.string(from: date)
        return dateString.uppercased()
    }

Key point of working with a database, if you change the structure, you need to delete the app in your simulator and start fresh.

🕶️Onto SwiftData in more detail

Oh, And my comment was published!

Another keyboard shortcut tip: Going Back and Forth (⌃ + ⌘ + ← and ⌃ + ⌘ + →)

Xcode tracks your entire movement history as soon as you open or create a project. Any file you go to, any symbol you look up - it all is appended to the IDE’s navigation stack. Use ⌃ + ⌘ + ← and ⌃ + ⌘ + → to go back and forth between the source code locations you visited.

IMPORTANT QUERY NOTES

🕶️dynamically sorting and filtering query

To make queries dynamic, Paul used a separate view to display the query results and passed the query parameters in as well.

This is best done by passing a value into the view using an initializer, then using that to create the query. The UsersView initializer also accepts some kind of sort descriptor for our User class. This uses Swift’s generics again: the SortDescriptor type needs to know what it’s sorting, so we need to specify User inside angle brackets.

init(minimumJoinDate: Date, sortOrder: [SortDescriptor<User>]) {
    _users = Query(filter: #Predicate<User> { user in
        user.joinDate >= minimumJoinDate
    }, sort: sortOrder)
}

From the ContentView: Building a pickable sort order which is then passed to the queryView

Menu("Sort", systemImage: "arrow.up.arrow.down"){
   Picker("Sort", selection: $sortOrder) {
       Text("Sort by Name")
           .tag([
               SortDescriptor(\User.name),
               SortDescriptor(\User.joinDate),
            ])
                           
       Text("Sort by Join Date")
           .tag([
               SortDescriptor(\User.joinDate),
               SortDescriptor(\User.name)
           ])
   }
}

11FEB24

Bought my developer license today!

Distributing your app to registered devices 🍏

To Sync with iCloud: https://www.hackingwithswift.com/books/ios-swiftui/syncing-swiftdata-with-cloudkit

SwiftData with iCloud has a requirement that local SwiftData does not: all properties must be optional or have default values, and all relationship must be optional. The first of those is a small annoyance, but the second is a much bigger annoyance – it can be quite disruptive for your code.

What are app ids and bundle identifiers

I’m not a particularly big fan of scattering that kind of code everywhere around a project, so if I’m using jobs regularly I’d much rather create a read-only computed property called unwrappedJobs or similar – something that either returns jobs if it has a value, or an empty array otherwise, like this:

var unwrappedJobs: [Job] { jobs ?? [] }

🕶️5 Steps to better swiftui views

swiftui protocols fivestars.blog

If your toolbar is getting messy, you can separate if off

12FEB24

Very good example of building predicates and sortorders dynamically

textfield formatter

Filtering and sorting persistent data 🍏

🕶️swiftdata trouble with predicates

Separating the predicate:

@Query private var homes: [Home]

init(session: Session) {

    let id = session.persistentModelID
    let predicate = #Predicate<Home> { home in
        home.session?.persistentModelID == id
    }

    _homes = Query(filter: predicate, sort: [SortDescriptor(\.timestamp)] )
}

Foundation predicate 🍏

Fetch descriptor 🍏

🕶️database store

🕶️swiftdata quick start

swiftyplace enum raw values

Swiftdata from apple 🍏

So I have discovered what others have discovered. The queries with enums don’t work.

I need to change my database and remove the enums. Not a big deal except enums are great for forcing logic and I plan on using them extensively. Let’s hope Apple fixes this soon.

defining data relationships with enums 🍏

13FEB24

change a property in Expense and now it won’t build, trying to just delete it, but it won’t start to let me delete it…pretty silly

migration with swiftdata

swiftui sf symbols swiftyplace

14FEB24

changed out the enum to a string and now the query works. I do still use the enum to build the views and flip to string as I add to the swiftdata.

Another Big shoutout the swiftyplace. I used the same structure as: https://www.swiftyplace.com/blog/modeling-data-in-swiftdata

enum TagSorting: String, Identifiable, CaseIterable {
    case byName
    case byAmount
    
    var title: String {
        switch self {
            case .byName:
                return "Sort by Name"
            case .byAmount:
                return "Sort by Amount"
        }
    }
    
    var sortDescriptor: [SortDescriptor<Expense1>] {
        switch self {
            case .byName:
              [
                SortDescriptor(\Expense1.name),
                SortDescriptor(\Expense1.amount)
            ]
        case .byAmount:
            
              [
                SortDescriptor(\Expense1.amount),
                SortDescriptor(\Expense1.name)
            ]
        }
       
    }
    
    var id: Self { return self }
}

which turned my menu into:

Menu {
  Picker(selection: $tagSorting.animation()) {
       ForEach(TagSorting.allCases) { tag in
            Text(tag.title)
       }
       } label: {
            Text("Sort Tags by")
       }
                      
       } label: {
            Label("Sorting", systemImage: "slider.horizontal.3")
       }

The animation is an added bonus. Notice how new vars are defined in the enum using switch self. VERY COOL!

my first attempt to use icloud: “Failed to setup cloudKit integration for store”

Pushing background updates to your app 🍏

15FEB24

🕶️Very good notes on reading JSON files that have different keys or property types

🕶️watch all of these

swift-urlsession

Even Better!

adding the download of the json file to the app struct

🕶️pre-populate swiftdata

🕶️how to preload an app with json data

type does not conform to protocol decodable

🕶️How to make swiftdata models conform to codable

16FEB24

ignoring invalid json elements swiftbysundell

this might be worth a read swiftui model review update

date formatter cheatsheet

5 things you should know optionals avanderlee

json parsing decoding

When you accidently corrupt the data container because you’re playing with the structure. Hint, delete before changing. possible way to get rid of “error Source URL” has 🕶️disappeared

Just give it a new name and restart I have had to do this several times, but at least this is quick and easy to do. Found better answer on 23FEB - How to delete the files so the app regens on recompile

@main
struct MyFaceBookApp: App {
    var container: ModelContainer
    
    init() {
        do {
 ----->     let storeURL = URL.documentsDirectory
               .appending(path: "myfacbook_v4.sqlite")
            let config = ModelConfiguration(url: storeURL)
            container = try ModelContainer(for: User.self, 
                configurations: config)
        } catch {
            fatalError("Could not initialize ModelContainer")
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

17FEB24

READ THIS guide to building swiftdata applications

how to parse iso8601 date sarunw

I have been banging on this for several days looking for the silver bullet but never found one:

I finally figured out how to parse a date from json that the decoder wouldn’t read by default. I had to save as a string and once the init was complete, changed my date field from .now to the parsed string….

reading the date field as .iso8601 did not work eventhough Paul said it would.

You need a real date field if you wish to have real queries, computed properties are never part of a query.

    func updateRegisteredDate(for stringDate: String) -> Date {
        
        let RFC3339DateFormatter = DateFormatter()
        RFC3339DateFormatter.locale = Locale(identifier: "en_US_POSIX")
        RFC3339DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
        RFC3339DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)

        if let date = RFC3339DateFormatter.date(from: stringDate) {
            return date
        }
           
        return .now
    }
}

The key piece:

func loadMessages() async {
        
      do {
          try? modelContext.delete(model: User.self)
          let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json")!
          let (data, _) = try await URLSession.shared.data(from: url)
          var users = [User]()
          users = try JSONDecoder().decode([User].self, from: data)
          for user in users {
----->    user.registeredDate = updateRegisteredDate(for: user.registered)
            modelContext.insert(user)
         }
            
        } catch {
            //let _ = print("Failed to load url.")
            let _ = print("Download error: \(error)")
            
        }
    }

18FEB

maybe use this for the contentview: filtering and sorting 🍏

Finally found the right answer to formatting dates that apple doesn’t handle by default

extension JSONDecoder.DateDecodingStrategy {
  static func anyFormatter(in formatters: [DateFormatter]) -> Self {
    return .custom { decoder in
      guard formatters.count > 0 else {
        throw DecodingError.dataCorrupted
         (DecodingError.Context(codingPath: decoder.codingPath, 
        debugDescription: "No date formatter provided"))
      }
      
      guard let dateString = try? decoder.singleValueContainer().
         decode(String.self) else { throw DecodingError.dataCorrupted(
            DecodingError.Context(codingPath: decoder.codingPath, 
        debugDescription: "Could not decode date string"))
      }
      
      let successfullyFormattedDates = formatters.lazy.compactMap { 
        $0.date(from: dateString) }
      guard let date = successfullyFormattedDates.first else {
        throw DecodingError.dataCorrupted(DecodingError.Context(
          codingPath: decoder.codingPath, debugDescription: 
          "Date string \"\(dateString)\" does not match any of the expected 
        formats (\(formatters.compactMap(\.dateFormat).joined(separator: " or ")))"))
      }
      
      return date
    }
  }
}

extension KeyedDecodingContainer {
    func decodeDate(forKey key: KeyedDecodingContainer<K>.Key, 
          withPossible formats: 
          [DateFormatter]) throws -> Date? {
        
        for format in formats {
            if let date = format.date(from: try 
               self.decode(String.self, forKey: key)) {
                return date
            }
        }
        throw DecodingError.dataCorruptedError(
           forKey: key, in: self, debugDescription: 
        "Date string does not match format expected by formatter.")
    }
}

extension DateFormatter {
    static let iso8601Full: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
    
    static let yyyyMMdd: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
}

From my class:

var registered: Date? example: 2014-07-05T04:25:04-01:00 Apple does not read this out of the box…crazy

how I used the above to bypass the defaults:

required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        address = try container.decode(String.self, forKey: .address)
        about = try container.decode(String.self, forKey: .about)
        // MARK: note the call to decodeDate not decode
        registered = try container.decodeDate(forKey: .registered, 
        withPossible: [.iso8601Full, .yyyyMMdd])

}

iso8601 fails miserably

extension URLSession {
    func decode<T: Decodable>(
        _ type: T.Type = T.self,
        from url: URL,
        keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys,
        dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .deferredToData,
        dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .iso8601
        //https://stackoverflow.com/questions/44682626/
          swifts-jsondecoder-with-multiple-date-formats-in-a-json-string
    ) async throws  -> T {
        let (data, _) = try await data(from: url)
        
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = keyDecodingStrategy
        decoder.dataDecodingStrategy = dataDecodingStrategy
        decoder.dateDecodingStrategy = dateDecodingStrategy
        let decoded = try decoder.decode(T.self, from: data)
        return decoded
    }
}

Populating Data

19FEB24

Trying to have contentView had a dynamic query refresh:

swiftdata query with dynamic properties

23FEB24

There is probably a better way to do this, and actually migrate the old data. But what I’ve been doing since I have just been working with new apps in simulators so far is this.

In your main app file, add an initializer like this…

Need help with swiftdata

import SwiftData
import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: MyDataType.self)
    }

    init() {
        print(URL.applicationSupportDirectory.path(percentEncoded: false))
    }
}

Then, when you run your app, it will print a line that tells you the location where your database files are stored. You can go to that location in finder, and you should find 3 files there. (default.store, default.store-shm, and default.store-wal)

If you delete all 3 of those files, they will automatically regenerate when you run your app again. However, your data will be completely deleted, and you will start with a fresh empty database. But it has allowed me to work around errors like this while I’m still in the beginning stages of making my app.

swift shortcut unwrapping an array of optionals

26FEB Well it turns out that the date extension was mangling the date. iso8601 worked after all. I learned alot about making extensions and customizing date formats so all was not lost.

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
users = try decoder.decode([User].self, from: data)

Finished with Day 61

Onto Days 62-76: Filters, maps, and more

Big shift today… 🕶️watch this again integrating core image with swiftui

02MAR24

How to call a function from a subview

map flatmap and compactmap

Just finished Day 67

🕶️instafilter

I’ve slowed down and having much more fun. swift is a great language.

I did try to use subviews but it got messsy calling methods not in the child view. The calls were still in the parent view so it complained. This project did not use a class to maintain a centralized place to keep functions.

This project highlighted the reason to do so. (at least for me)

notes from apple 🍏 on processing an image using buil-in filters

CoreImaging by 🍏

03MAR24

How to sort multiple properties sarunw

Working with files and folders swiftbysundell

swiftui views vs modifiers

Just look at how SwiftUI’s built-in API was designed — containers (such as HStack and VStack) are views, while styling APIs (such as padding and foregroundColor) are implemented as modifiers. So, if we follow that same approach as much as possible within our own projects, then we’ll likely end up with UI code that feels consistent and inline with SwiftUI itself.

I have seen the same with Paul Hudson’s approach.

markdown emojis

filemanager reading,writing, deleting swiftyplace

URL ResourceKeys - part of the Filemanager contentsofDirectory call explained

05MAR24

Day 69, Touch ID, Face ID, yesterday was integrating Mapkit into swift. Pretty Cool. Not sure if I’ll ever use it.

https://www.hackingwithswift.com/books/ios-swiftui/using-touch-id-and-face-id-with-swiftui

Tomorrow, I think I’ll get my old iphone 12 mini working with all of this…

07MAR Day 72

Reading json data from wikipedia, storing map locations, and MVVM. All very good!

extension ContentView {
    @Observable
    class ViewModel {
    }
}

Tip: I get lots of questions about why I place my view models into view extensions, so I’d like to take a moment to explain why. This is a small app, but think about how this would look when you have 10 views, or 50 views, or even 500 views. If you use extensions like this, the view model for your current view is always just called ViewModel, and not EditMapLocationViewModel or similar – it’s much shorter, and avoids cluttering up your code with lots of different class names!

one rule of customizing comparable, must provide a < func

static func < (lhs: Page, rhs: Page) -> Bool {
        lhs.title < rhs.title
    }

From Paul:

That took quite a bit of code in total, but the end result is that we have loading and saving done really well:

  • All the logic is handled outside the view, so later on when you learn to write tests you’ll find the view model is much easier to work with.
  • When we write data we’re making iOS encrypt it so the file can’t be read or written until the user unlocks their device.
  • The load and save process is almost transparent – our view has no idea it’s even happening.

Sometimes people ask me why I didn’t introduce MVVM earlier in the course, and there are two primary answers:

  1. It works really badly with SwiftData, at least right now. This might improve in the future, but right now using SwiftData is basically impossible with MVVM.
  2. There are lots of ways of structuring projects, with MVVM being just one of many. Spend some time experimenting rather than locking yourself into the first idea that comes along.

Tomorrow, Locking our UI behind FaceID

11MAR24

🕶️Day 73 Challenge

🕶️How to handle authentication errors/NSErrors

🕶️Confirmation Dialog vs alert

Visually alerts and confirmation dialogs are very different: on iPhones, alerts appear in the center of the screen and must actively be dismissed by choosing a button, whereas confirmation dialogs slide up from the bottom, can contain lots of buttons, and can be dismissed by tapping on Cancel or by tapping outside of the options.

Opening the Settings app from another app

UIApplication.open(_:options:completionHandler:) must be used from main thread only

18MAR24

https://www.hackingwithswift.com/books/ios-swiftui/identifying-views-with-useful-labels

onTapGesture()
.accessibilityLabel()
.accessibilityHint()
.accessibilityAddTraits(.isButton)
.accessibilityRemoveTraits(.isImage)

Image(decorative: "character")
    .accessibilityHidden(true)
VStack {
    Text("Your score is")
    Text("1000")
        .font(.title)
}
.accessibilityElement(children: .combine)

OR

VStack {
    Text("Your score is")
    Text("1000")
        .font(.title)
}
.accessibilityElement(children: .ignore) ### .ignore is the default so you can simply write .accessibiltiyElement()
.accessibilityLabel("Your score is 1000")

https://www.hackingwithswift.com/books/ios-swiftui/reading-the-value-of-controls

To fix this we can give iOS specific instructions for how to handle adjustment, by grouping our VStack together using accessibilityElement() and accessibilityLabel(), then by adding the accessibilityValue() and accessibilityAdjustableAction() modifiers to respond to swipes with custom code.

Adjustable actions hand us the direction the user swiped, and we can respond however we want. There is one proviso: yes, we can choose between increment and decrement swipes, but we also need a special default case to handle unknown future values – Apple has reserved the right to add other kinds of adjustments in the future.

VStack {
    Text("Value: \(value)")

    Button("Increment") {
        value += 1
    }

    Button("Decrement") {
        value -= 1
    }
}
.accessibilityElement()
.accessibilityLabel("Value")
.accessibilityValue(String(value))    
.accessibilityAdjustableAction { direction in
    switch direction {
    case .increment:
        value += 1
    case .decrement:
        value -= 1
    default:
        print("Not handled.")
    }
}

https://www.hackingwithswift.com/books/ios-swiftui/handling-voice-input-in-swiftui

Button("John Fitzgerald Kennedy") {
    print("Button tapped")
}
.accessibilityInputLabels(["John Fitzgerald Kennedy", "Kennedy", "JFK"])

https://mfaani.com/posts/why-cant-xcode-show-caller/

21MAR24 Milestone Projects 13-15

All swiftui property wrappers explained

confirmation dialogs

Adding comparable

Operation Overloading & Custom Property Wrappers

Challenge

25MAR

https://www.hackingwithswift.com/quick-start/swiftui/how-to-show-a-menu-when-a-button-is-pressed

https://www.dabblingbadger.com/blog/2021/4/6/tips-and-tricks-for-making-the-most-of-textfield-in-swiftui

https://rectangleapp.com - handy app to allow mac to align windows

https://stackoverflow.com/questions/29462953/how-to-add-an-optional-string-extension

https://www.hackingwithswift.com/books/ios-swiftui/using-alert-and-sheet-with-optionals

https://www.hackingwithswift.com/books/ios-swiftui/customizing-our-filter-using-confirmationdialog

28MAR

I need to init the database when the contentview opens…. Once I know if the db is empty, I’ll show a different list view with a prompt to create an entry

https://www.swiftbysundell.com/articles/handling-loading-states-in-swiftui/

29MAR https://www.hackingwithswift.com/articles/226/5-steps-to-better-swiftui-views

@19:20 explains why you call somefunction for a button action instead of somefunction()

31MAR24

Setting up swiftdata to use mvvm - Key Post

review:

https://www.hackingwithswift.com/quick-start/swiftdata/how-to-create-a-document-based-app-with-swiftdata

https://www.hackingwithswift.com/quick-start/swiftdata

I need to convert my sample pic into a data file for inserting into swiftdata.

I just discovered this: https://www.youtube.com/watch?v=y3LofRLPUM8

Just convert image into data by loadTransferable and define image as data in your class

04APR straightforward way to use MVVM with swiftdata….not using Paul’s view extension for this(yet)

https://blog.devgenius.io/swiftdata-with-mvvm-and-swiftui-fdf0fc1b2c4f

06APR Splitting SwiftData and SwiftUI via MVVM

https://www.avanderlee.com/swift/mainactor-dispatch-main-thread/

11APR24

https://developer.apple.com/tutorials/develop-in-swift/save-data

https://www.donnywals.com/an-introduction-to-synchronizing-access-with-swifts-actors/

24APR24

Looks like this project is tying everything together…tabs, navigationstack swiftdata, enums

https://www.hackingwithswift.com/books/ios-swiftui/building-our-tab-bar

27APR

https://www.hackingwithswift.com/quick-start/swiftui/displaying-a-detail-screen-with-navigationlink

Bottom top