CodeWithChris - CWC+ Courses

Cheatsheets

M1L2 - Xcode Cheatsheet.pdf

SwiftUI Cheatsheet.pdf

Swift Cheatsheet.pdf

LYFA Course - Launch Your First App

Resources:

https://www.dropbox.com/sh/nh92vdepniipw2v/AAAaizYNZvE1_l5MXozzDMgCa?e=1&dl=0

Start: 07. November 2024

Module 1 - The War Card Game + Basic Syntax

  • Lesson 3 - UI Building

    https://github.com/dmkeks1/L3-Demo

  • Lesson 4 - War Card Game UI

    https://github.com/dmkeks1/War-Card-Game

  • Lesson 6 - Swift functions

    func doAdd(a:Int, b:Int) {
        
        var sum = a + b
        print(sum)
    }
     
    doAdd(5, 6)

    Man kann die Funktion auch mit labels versehen was die Arbeit damit vereinfacht:

    func doAdd(firstNum a: Int, secondNum b: Int) -> Int {
        
        var sum = a + b
        return sum
    }
     
    doAdd(firstNum: 5, secondNum: 5)

    Möchte man keine Labels(z.B. a: b: etc.) anzeigen, dann kann man _ bei der Deklaration verwenden:

    func doAdd(_ a: Int, _ b: Int) -> Int {
        
        var sum = a + b
        return sum
    }
     
    doAdd(2,5)

    Wenn die Funktion was returnen soll →

    image.png

    image.png

  • Lesson 7 - Buttons and Properties

    https://github.com/dmkeks1/War-Card-Game

    Button aus image erstellen. Hier wird eine closure benutzt.

    Button {
              deal(). //action oder funktion
                } label: {
                    Image("button") //image des buttons
                }
  • Lesson 8 - War Card Game Logik - randomizer, Punkte, Auswertung, @State properties, conditional Logic (if statement)

    https://github.com/dmkeks1/War-Card-Game

    number randomizer

    Int.random(in: 2...14) // randomizes Nummer zwischen 2 und 14, inkl. 2 & 14

    @State properties

    @State ist ein property wrapper, die variable danach eine property

    @State var playerCard: String = "back" // @State ist ein property wrapper -> danach property
  • Challenge 1 - Number Climb Game

    https://github.com/dmkeks1/NumberClimbGame

Module 2 - The Menu App

  • Lesson 2 - Arrays

    var b: [Int] = [5, 10, 15, 20, 25]
     
    b[0] = 20
     
    print(b) // Ausgabe ist nun 20
     
  • Lesson 3 - Structures

    // Struct erstellen, vorzugsweise in eigener Datei MenuItem.swift
    struct MenuItem {
        var name: String
        var price: String
        var imageName: String
     
    }
     
    // erstellten Struct "initialisieren" 
    **var menuItems: [MenuItem] = []
     
    // Instance erstellen
     
    var firstItem = MenuItem(name: "Sushi", price: "1,99€", imageName: "sushi")**
  • Lesson 5 - Swift UI List

    https://github.com/dmkeks1/Menu

    image.png

    https://github.com/dmkeks1/Menu/blob/main/Menu/MenuView.swift

    • result

      image.png

  • Lesson 5 - Challenge

    https://github.com/dmkeks1/Lesson5Challenge/blob/main/Lesson5Challenge/ContentView.swift

  • Lesson 7 - Structures Part 2

    image.png

    try to organize your code logically into other files.

    image.png

  • Lesson 8 - State and Updating Views

    Outsourcing Data into another file/struct.

    File of data is called DataService here because it provides data for us.

    https://github.com/dmkeks1/Menu/blob/main/Menu/DataService.swift

    Instance muss danach in dem View indem wir die Data benötigen erstellt werden.

      @State var menuItems: [MenuItem] = [MenuItem]() // @State sagt SwiftUI, dass die variable wichtig ist fürs UI. 
      // Änderungen werden überwacht und View wird bei jeder Änderung neu gedrawed
      
      var dataService = DataService() // creating an instance of DataService
      
      ...
      List(menuItems) {item in
      ...
      }
      .onAppear { // modifier of the List
                // call for the data
                menuItems = dataService.getData()
  • Lesson 9 - Reusing your Views

    https://github.com/dmkeks1/Menu

  • Module 2 Challenge - Movie List

    https://github.com/dmkeks1/Module2ChallengeMovieList

Module 3 - The Restaurant App

https://github.com/dmkeks1/Restaurant

  • Lesson 2 - TabView

    TabView {
                MenuView().tabItem {
                    
                    VStack {
                        Text("Menu")
                        Image(systemName: "menucard")
                    }
                    
                    
                }
                
                AboutView().tabItem {
                    
                    VStack {
                        Text("About")
                        Image(systemName: "info.circle")
                        
                    }
                    
                }
                
                GalleryView().tabItem {
                    VStack{
                        Text("Gallery")
                        Image(systemName: "photo")
                    }
                }
            }

    image.png

  • Lesson 3 - ScrollView

    VStacks mit viel Inhalt verhalten sich komisch was Text und Images angeht da nicht genug Platz vorhanden ist. Sobald man ScrollView definiert verhält es sich wie gewünscht.

    ScrollView {}

    ScrollView {
                VStack (alignment: .leading) {
                    Text("About")
                        .font(.largeTitle)
                        .bold()
                    
                    Image("restaurant")
                        .resizable()
                        .scaledToFit()
                        .padding(.bottom)
                    
                    Text("Sukiyabashi Jiro (すきやばし次郎, Sukiyabashi Jirō) is a sushi restaurant in Ginza, Chūō, Tokyo, owned by Jiro Ono.[2] Ono previously operated as the head chef, but stepped aside in favor of his son Yoshikazu Ono in 2023 due to ill health. Sukiyabashi Jiro was the first sushi restaurant[4] to receive three stars from the Michelin Guide.[5] It was removed from the Michelin Guide in November 2019 as it does not receive reservations from the general public,[6][7] instead requiring reservations to be made through the concierge of a luxury hotel.[8]")
                    
                    Image("map")
                        .resizable()
                        .scaledToFit()
                    
                    Text("""
                         Tsukamoto Sogyo Building
                         Basement Floor 1
                         2-15, Ginza 4-chome
                         Chūō, Tokyo
                         Japan
                         """)
                    
                    
                }
            }
  • Lesson 4 - Dynamically Generating UI Elements using For Each

    ForEach Syntax

    ForEach(wordList) { word in
    		Text(word)
    	}
    	
    	
    	ForEach(range) { _ in // range could be for example 2..5 
    		Text("Hello")
    		}
  • Lesson 5 - LazyVGrids

    LazyVGrid(columns: [GridItem(), GridItem(), GridItem()]) {
     
    }
     

    image.png

    Beispiel:

     
    import SwiftUI
     
    struct ContentView: View {
        
        var photos = ["gallery1", "gallery2", "gallery3", "gallery4", "gallery5", "gallery6", "gallery7", "gallery8", "gallery9", "gallery10", "gallery11"]
        
        
        var body: some View {
            
            ScrollView {
                
                LazyVGrid(columns: [GridItem(), GridItem(), GridItem()]) {
                    ForEach(photos, id: \.self) { p in
                        Image(p)
                            .resizable()
                            .scaledToFit()
                    }
                }
            }
            
        }
        
    }
     
    #Preview {
        ContentView()
    }
     

    image.png

    Beispiel wenn kein Abstand gewünscht: lazyvgrid

    ScrollView {
                
                LazyVGrid(columns: [GridItem(spacing:0), GridItem(spacing:0), GridItem(spacing:0)], spacing: 0) {
                    ForEach(photos, id: \.self) { p in
                        Image(p)
                            .resizable()
                            .scaledToFit()
                    }
                }
            }

    image.png

  • Lesson 6 - Displaying Images in a Grid

    GeometryReader { proxy in
    	ScrollView {
    	LazyVGrid {
    	...
    	}
    	}
    	
    }

    image.png

    https://github.com/dmkeks1/Restaurant/blob/main/Restaurant/GalleryView.swift

  • Lesson 7 - Modal Views Using Sheets

    Sheets | Apple Developer Documentation

  • Lesson 8 - Passing Data with Bindings

    @State var dataArray

    There are two common ways to use bindings:

    1. Textfield listens to changes, but also can write data in the property. (here we are in the same view)

    image.png

    1. able to pass data/variables into another views and back.

    image.png

    https://github.com/dmkeks1/Restaurant/blob/main/Restaurant/PhotoView.swift

  • Lesson 9 - Swift Closures, Initializers, Access Modifiers and More

    Initializer

    Closures

    image.png

    image.png

    Access Modifiers

    Arten von access modifiers:

    public = default

    private = can only be used inside a struct/function. Can’t be accessed outside of a structure/function.

    fileprivate = you can access outside a struct, but only inside the current swift file.

    internal = only inside the same target. this is more advanced and probably wont be used.

  • Module 3 Challenge

    https://github.com/dmkeks1/Module3Challenge

Module 4 - The Guidebook App

https://github.com/dmkeks1/Guidebook

  • Lesson 03 - NavigationStack and NavigationLink

    image.png

    NavigationStack{
                
                ScrollView {
                    
                    VStack {
                        
                        ForEach(cities) { city in
                            NavigationLink {
                                AttractionView()
                            } label: {
                                Text(city.name)
                            }
                        }
                    }
                }
                .padding()
            }
            
            .onAppear() {
                cities = dataService.getData()
            }
    NavigationLink {
                            DetailView(attraction: attraction)
                        } label: {
                            Text(attraction.name)
                        }
  • Lesson 04 - Building the Guidebook UI

    https://github.com/dmkeks1/Guidebook

  • Lesson 05 - Debugging in Xcode

    image.png

  • Lesson 06 - Swift Optionals

    image.png

    To prevent crashin you can user either

    1. if statement
    if selectedFood != nil {
    	Text(selectedFood!)
    }
     
    OR 
     
    if selectedFood == nil {
    	Text("Ice Cream")
    }
    else {
    	Text(selectedFood!)
    }
    1. optional binding
    if let food = selectedFood {
    	Text(food)
    }
     
    // if value is nil, this check just bypasses the lines -> no error
    1. nil coalescent operator
    Text(selectedFood ?? "Sushi")
     
    // if the value is valid, it will display selectedFood. If its nil
    // it will display Sushi.
     
    //can also be used for optional chaining:
     
    Text(selectedMenuItem?.name ?? "Pizza")

    image.png

  • Lesson 07 - Swift Dictionaries

    image.png

    image.png

    import UIKit
     
    var myArray: [String] = ["Orange", "Apple","Banana"] // Array
    var myEmptyArray = [String]()
     
    var myDictionary: [String:String] = ["OR":"Orange", "AP":"Apple", "BA":"Banana"] //Dictionary
    var myEmptyDictionary = [String:String]()
     
    myArray.append("Pear")
     
    myDictionary["PR"] = "Pear" // create key value pair
    myDictionary["PR"] = "Prune" //updates value for key "PR"
     
    myDictionary["PR"] = nil // deletes value from the dictionary
     
    myArray[1] // read from array - ORDER MATTERS
     
    //
    // if a Dictionary entry is not there, its nil, so you need to check:
    //
     
    if myDictionary["OR"] != nil {
        myDictionary["OR"] // read from array - WILL OUTPUT ORANGE
    }
     
    print(myDictionary["JDF"] ?? "Peach")
     
  • Lesson 08 - Swift Loops

    image.png

    For In

    var myArray: [String] = ["Orange", "Apple","Banana"] // Array
    var myDictionary: [String:String] = ["OR":"Orange", "AP":"Apple", "BA":"Banana"] //Dictionary
     
    for item in myArray { // going through an array
        print(item)
    }
     
    for k in myDictionary.keys { // going through the dictionary and prints out the keys
        print(k)
    }
     
    for v in myDictionary.values { // going through the dictionary and prints out the values
        print(v)
    }
     
    for (k, v) in myDictionary {//  going through the dictionary and prints out the values and keys.
                                // (k,v) -> is called a tuple, a group of variables
        print(k)
        print(v)
    }

    While

    import UIKit
     
    var myArray: [String] = ["Orange", "Apple","Banana"] // Array
    var myDictionary: [String:String] = ["OR":"Orange", "AP":"Apple", "BA":"Banana"] //Dictionary
     
    var flag = false
     
    while flag == false { // checks initially and may not run even once
        print("Hello")
        
        flag = true
    }
     
    repeat { // run guaranteed once, and checks condition afterwards
        print("Repeat")
    } while flag == false
     
  • Lesson 09 - JSON Data Format

    image.png

    image.png

    https://github.com/dmkeks1/Guidebook/blob/main/Guidebook/DemoData.json

  • Lesson 10 - How to Parse JSON in Swift - November 12, 2024

    Steps to get JSON Data into the APP:

        		// get filepath to DemoData.json
            
            // Read the file and turn into Data
            
            // parse Data into Swift Instances
            

    Code of working JSON import for the Guidebook App (DataService file, rest is in the GitHub)

    func getFileData() -> [City] {
            
            // get filepath to DemoData.json
            if let url = Bundle.main.url(forResource: "DemoData", withExtension: "json") {
                
                do {
                    // Read the file and turn into Data
                        let data = try Data(contentsOf: url) // try if it throws an error
                    
                            // parse Data into Swift Instances
                    let decoder = JSONDecoder()
                    
                    
                    
                    do {
                        let cities = try decoder.decode([City].self, from: data) //.self means that we try to create the type of City data and not an array or sth.
                        return cities
                    }
                    catch {
                        print("Couldn't parse the JSON: \(error.localizedDescription)")
                    }
                    
                    
                    
                    
                }
                catch  {
                    print("Couldn't read file: \(error.localizedDescription)")
                }
            
                
            
                
                
            }
            
            
            return [City]()
            
        }
  • Lesson 11 - Opening other Apps November 13, 2024

    Url Schemes

    image.png

    Map Links

    Complete List of iOS URL Schemes for Apple Apps and Services (Always-Updated)

    Button {
                            
                            // CReate URL Instance vbased on URL Scheme
                            if let url = URL(string: "maps://?q=\(attraction.name.replacingOccurrences(of: " ", with: "+"))&s11=\(attraction.latLong)&z=10&t=s")
                            {
                                // Test if url can be opened, is an app present?
                                if UIApplication.shared.canOpenURL(url) {
                                    // open url
                                    UIApplication.shared.open(url)
                                }
                                
                            }
                            
                            
                        } label: {
                            
                            ZStack {
                                RoundedRectangle(cornerRadius: 15)
                                    .foregroundColor(.blue)
                                    .frame(height: 40)
                                Text("Get Directions")
                                    .foregroundColor(.white)
                            }
                        }

    https://github.com/dmkeks1/Guidebook/blob/main/Guidebook/DetailView.swift

  • Challenge - Pokemon Guide November 13, 2024

    https://github.com/dmkeks1/PokemonChallenge

  • Assignment for Certificate November 13, 2024

    https://github.com/dmkeks1/PokemonTCGPacks

iOS Networking November 13, 2024

https://www.dropbox.com/scl/fo/3snpkc49at92dl1yg5lmi/h?rlkey=2o0510ofjl9cqke91wixhq364&e=1&dl=0

Module 1 - What is Networking? November 13, 2024

  • Lesson 02 - Finding and Using APIs

    image.png

    image.png

  • Lesson 03 - How to use API endpoints in Swift

    image.png

  • Lesson 04 - What is a REST API

    image.png

  • Lesson 05 - iOS Concurrency, Await and Async

    Concurrency - several Threads to work on things

    image.png

    image.png

    func longTask() async {
    		print("Long Task"
    }
    Task {
    	await longTask()
    }
  • Lesson 06 - How to make a Network request in Swift

    https://learn.codewithchris.com/courses/take/networking/lessons/47392329-lesson-06-how-to-make-a-network-request-in-swift

    image.png

     
    // the following line of code already takes care of the optinal check.
    // if the url is nil, it skips it.
    if let actualUrl = URL(string: "https://api.pexels.com/v1/search?query=nature") {
     
    	// create url request and pass in actualUrl
     
    }

    error handling:

    image.png

    Request

    ...
    .onAppear {
                
                Task {
                    await apiCall()
                }
                
            }
            ...
     
    func apiCall() async{
            
            // 1. URL
            if let url = URL(string: "https://api.pexels.com/v1/search?query=nature&per_page=1") {
                // 2. URL request
                var request = URLRequest(url: url)
                request.addValue("\(api_key)", forHTTPHeaderField: "Authorization") // adds key to request
                // 3. URLSession
                do { // we dont ne a task block here because the function in the code is called
                     // in a task block already
                    let (data, response) = try await URLSession.shared.data(for: request) // returns in a tuple, so we need to put the answer in a tuple aswell
                    
                    print(data) // actual requested data
                    print(response) // status code, other info
                    
                } catch {
                    print(error)
                }
            }
        }

    https://github.com/dmkeks1/M1L6HowToMakeANetworkRequest

  • Lesson 07 - Debugging Network Requests

    https://proxyman.io/

    1. install certs
    2. enabled domain
    3. remove app and reinstall

    https://learn.codewithchris.com/courses/take/networking/lessons/47857956-lesson-07-debugging-network-requests-with-proxyman

  • Lesson 08 - Parsing Network Repsonses

    Complete call with response and parsing:

    https://github.com/dmkeks1/M1L6HowToMakeANetworkRequest/blob/main/M1L6HowToMakeANetworkRequest/Model/SearchResponse.swift

    func apiCall() async{
            
            // 1. URL
            if let url = URL(string: "https://api.pexels.com/v1/search?query=nature&per_page=1") {
                // 2. URL request
                var request = URLRequest(url: url)
                request.addValue("\(api_key)", forHTTPHeaderField: "Authorization") // adds key to request
                // 3. URLSession
                do { // we dont ne a task block here because the function in the code is called
                     // in a task block already
                    let (data, response) = try await URLSession.shared.data(for: request) // returns in a tuple, so we need to put the answer in a tuple aswell
                    
                    // 4. Parse the JSON
                    
                    let decoder = JSONDecoder()
                    do {
                        let searchResponse = try decoder.decode(SearchResponse.self, from: data)
                        
                        for photo in searchResponse.photos {
                            print(photo)
                        }
                        
                    } catch {
                        print(error)
                    }
     
                } catch {
                    print(error)
                }
            }
        }

    https://github.com/dmkeks1/M1L6HowToMakeANetworkRequest/blob/main/M1L6HowToMakeANetworkRequest/ContentView.swift

  • Challenge - Jokes App

    https://github.com/dmkeks1/ChallengeJokesApp

    • Instructions

      In this challenge, I'd like you to try doing the same thing, except with a different API.

      As usual, to get the most out of this challenge, try to complete it by reviewing the training in this module before looking at the solution or asking for help.

      Getting stuck, looking things up, reviewing previous lessons.. these are all steps in the skill building process!

      Setup:

      • API: [https://jokeapi.dev/](https://jokeapi.dev/)
      • Authentication: None
      • Endpoint: https://v2.jokeapi.dev/joke/Any?type=single

      Challenge:

      • Build a UI that has a Text label in the center of the screen. At the bottom, put a button.
      • When the button is tapped, it sends the API request
      • When the response returns, parse the data and display the joke in the Text label
      • You should be able to keep tapping the button to show a different joke in the Text label
      • Make sure you use "async" and "await" as you learned in Lesson 5.

      Getting Help:

      • Check the project that you worked on in this module which is very similar.
      • Dedicated discussion thread for this challenge [here](https://codecrew.codewithchris.com/t/ios-networking-module-1-jokes-app-challenge/25336). (If you need access to the CWC+ students section of the community, fill out [this form](https://codewithchris.typeform.com/to/NlKfo8FS))
      • Reach out to one of our coaches with the chat widget in the lower right corner.
      • Check out the solution below.

      Solution:

      • Solution can be found [here](https://www.dropbox.com/scl/fi/xopy0eu436bplb2w4cp0u/Networking-Module-1-Challenge-1-Project.zip?rlkey=9ggl7pudef796v3sik5qu7id4&dl=1).

Module 2 - The Citysights App November 16, 2024

  • Module 01 - Source Control

    image.png

    image.png

    image.png

  • Module 05 - Adding the API key to Xcode Securely

    CleanShot 2024-11-16 at 19.05.09@2x.png

    • create config file from template in xcode

      CleanShot 2024-11-16 at 19.06.39.png

      target needs to be unchecked

      CleanShot 2024-11-16 at 19.07.33.png

      choose created config file in xcode project for it to be used

      CleanShot 2024-11-16 at 19.13.08.png

      add api_key to info.plist

      CleanShot 2024-11-16 at 19.15.54.png

      CleanShot 2024-11-16 at 19.18.57.png

      To test if the app can access the key you can print

      .onAppear {
                  print(Bundle.main.infoDictionary?["API_KEY"] as? String)
                  }

      create .gitignore file to ignore the config file

      navigate to the project folder an press SHIFT + COMMAND + . to reveal invis folders like .git

      open terminal and navigate to the project folder by dragging the folder into the terminal.

      in the terminal enter: touch .gitignore touch creates, . means hidden

      for creating gitignore we can use https://www.toptal.com/developers/gitignore

      enter xcode and swift and copy the content in the .gitignore file we created.

      CleanShot 2024-11-16 at 19.36.00.png

      CleanShot 2024-11-16 at 19.36.56.png

      make sure to unstage config.xcconfig in xcode so it doenst commit the api_key if we created that file after settings everyhting up

      CleanShot 2024-11-16 at 19.37.35.png

      now we can commit.

      if we used another branch we can merge into main after and delete the used branch (chris used that in his tutorial)

  • Module 06 - making API request for yelp api

    import Foundation
     
    struct DataService  {
        
        
        let api_key = Bundle.main.infoDictionary?["API_KEY"] as? String
        
        
        func businessSearch() async {
            
            
            //check if api key exists
            guard  api_key != nil  else {
                return
            }
            
            // 1. Create URL
            if let url = URL(string: "https://api.yelp.com/v3/businesses/search?latitude=35.665517&longitude=139.770398&categories=restaurants&limit=10") {
                
                
                
                
                // 2. Create request
                var request = URLRequest(url: url)
                
                request.addValue("Bearer \(api_key!)", forHTTPHeaderField: "Authorization")
                request.addValue("application/json", forHTTPHeaderField: "accept")
                
                // 3. Send request
                
                do {
                    let (data, response) = try await URLSession.shared.data(for: request)
                    
                    print(data)
                    print(response)
                }
                catch {
                    print(error)
                }
            }

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/DataService.swift

  • Module 07 - Parsing Yelp API + CodingKeys

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/Model/Business.swift

    struct Business: Decodable, Identifiable {
     
        
        var id: String?
        var alias: String?
        var categories: [Category]?
        var coordinates: Coordinate?
        var displayPhone: String?
        var distance: Double?
        var imageUrl: String?
        var isClosed: Bool?
        var location: Location?
        var name: String?
        var phone: String?
        var price: String?
        var rating: Double?
        var reviewCount: Int?
        var url: String?
        
        enum CodingKeys: String, CodingKey {
           
            
            //properties die geändert werden müssen weil die bezeichnung nicht swift conform ist
            case displayPhone = "display_phone"
            case isClosed = "is_closed"
            case imageUrl = "image_url"
            case reviewCount = "review_count"
            
            
            //wenn properties geändert werden müssen, dann müssen trotzdem alle anderen nochmal im codingkeys enum aufgezählt werden
            case id
            case alias
            case categories
            case coordinates
            case distance
            case location
            case name
            case phone
            case price
            case rating
            case url
        }
        
    }
  • Module 09 - Styling the list

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/ContentView.swift

    struct TextHelper {
        
        static func distanceAwayText(meters: Double) -> String {
            
            if meters > 1000 {
                return "\(Int(round(meters/meters/1000))) km away"
            }
            else {
                return "\(Int(round(meters))) m away"
            }
        }
    }
  • Module 10 - Business Detail Using SwiftUI Sheets

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/BusinessDetailView.swift

    sheets, lists, sf symbols,

  • Challenge - Football Stats

Module 3 - Data Flow and Map November 17, 2024

  • Lesson 01 - The Observation Data Flow Pattern - MVVM

    CleanShot 2024-11-17 at 19.52.59@2x.png

    CleanShot 2024-11-17 at 19.58.25@2x.png

    CleanShot 2024-11-17 at 19.59.14@2x.png

    CleanShot 2024-11-17 at 20.00.24@2x.png

  • Lesson 02 - Swift Classes November 18, 2024 and Structures Part 1

    CleanShot 2024-11-18 at 16.18.28@2x.png

    CleanShot 2024-11-18 at 16.21.55@2x.png

    CleanShot 2024-11-18 at 16.22.40@2x.png

  • Lesson 03 - Swift Classes and Structures Part 2

    Verhalten von Structs:

    creates copy and modifies copy

    CleanShot 2024-11-18 at 17.12.33@2x.png

    verhalten von class:

    referenziert nur auf instance und erstellt keine copy. ändert werte der instance

    CleanShot 2024-11-18 at 17.15.46@2x.png

    Mutability:

    // 2. Default initializers
    struct StructPerson {
        var name: String
    }
     
    var struct1 = StructPerson(name: "Dominik")
     
    class ClassPerson {
        var name: String
        
        init(name: String) {
            self.name = name
        }
    }
     
    var class1 = ClassPerson(name: "Dominik")
     
    // 3. Assignment of Structs and Classes
     
    var struct2 = struct1
    struct1.name = "Mike" // Does this statement change struct2's name property?
    print(struct2.name)
     
    var class2 = class1
    class1.name = "Mike" // Does this statement change class2's name property?
    print(class2.name)
     
    // 4. Mutability differences
    let struct3 = StructPerson(name: "Sara")
    let class3 = ClassPerson(name: "Sara")
     
    //struct3 = StructPerson(name: "Jen")
    //class3 = ClassPerson(name: "Jen")
     
    struct3.name = "Jen"
    class3.name = "Jen"

    CleanShot 2024-11-18 at 17.21.45@2x.png

  • Lesson 04 - Swift Class Inheritance November 19, 2024

    CleanShot 2024-11-19 at 21.27.58@2x.png

    CleanShot 2024-11-19 at 21.28.12@2x.png

    Syntax

    class Animal {
     
    		var name: String
    		func talk() {
    		print("woop"
    		}
    init(name: String) {
      self.name = name
    		}
    }
     
    class Cat: Animal { //declaring Cat as subclass from animal
     
    }
     
    let c = Cat(name:"Bob")
    print(c.name) // has accesss to property from class Animal
    c.talk() // has accesss to method/function from class Animal

    Overriding

    class Cat: Animal { //declaring Cat as subclass from animal
     override func talk() { // overriding method from main class
       print("meow"
     }
     
    }

    CleanShot 2024-11-19 at 21.33.53@2x.png

    Super

    CleanShot 2024-11-19 at 21.36.18@2x.png

    Polymorphism

    CleanShot 2024-11-19 at 21.40.15@2x.png

  • Lesson 05 - How to implement the Observaion pattern

    Syntax Observable

    @Observable // ohter views, where this class is instanced will
    //notice when the value change
    class ViewModel { //@Observable only works with classes
      var wordDb = ["Potato", "Cat", "Sunshin", "Raindrop"]
      var currentWord = "Cactus"
      
      
      func randomizeWord() { // randomizes word and puts into currentWord
    		  currentWord = wordDb[Int.random(in: wordDb.indices)]
      }
    }

    Syntax Environment

    @main
    struct ObservationDemoApp: App {
    	@State var viewModel = ViewModel()
    	
    	var body: some Scene {
    		WindowGroup {
    			ContentView()
    				.environment(viewModel) // declaring it as an environmental
    				// will make it available for all subvies of contentview
    		
    		}
    	}
    }

    In the ContentView you need to get the reference by adding:

    @Environment(ViewModel.self) var viewModel

  • Lesson 06 - Observation Pattern For City Sights

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/BusinessModel.swift

  • Lesson 07 - SwiftUIMaps & Picker

    Map

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/MapView.swift

    ...
    import MapKit //neede for map funcitonality
     
    ...
     
    Map() //creates map
    ...

    Picker

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/HomeView.swift

    Picker("", selection: $selectedTab) {
                    Text("List")
                        .tag(0) //legt reihenfolge fest
                    
                    Text("Map")
                        .tag(1) //legt reihenfolge fest
                }
                .pickerStyle(SegmentedPickerStyle()) // ändert den style
                // siehe screenshot

    CleanShot 2024-11-20 at 22.05.14.png

  • Challenge Crypto Coin November 30, 2024

    https://github.com/dmkeks1/CryptoCoin

Module 4 - Building more App Features

  • Lesson 01 - Map Markers

    https://github.com/dmkeks1/CitySights-App

    Map Markers

    To use Map markers we use Markers(). In this example we can go through our businesses with a foreach and iterate through every entry and display all the markers on the map:

    Map() {
                ForEach(model.businesses, id: \.id) { b in
                    Marker(b.name ?? "Restaurant", coordinate: CLLocationCoordinate2D(latitude: b.coordinates?.latitude ?? 0, longitude: b.coordinates?.longitude ?? 0))
                }

    Marker Selection

    To select a marker we have to set a tag with a unique ID for the selected item.

    Also we need to create a @State var to recognize changes

     
    @State var selectedBusinessId: String?
     
    Map()
    ...
     
    .tag(b.id ?? "None") //sets to None if is nil
    ...
     
    .onChange(of: selectedBusinessId) {
    ...
    }
     

    Displaying Business Details

    .onChange(of: selectedBusinessId) { oldValue, newValue in
                // Find matching business
                
                
                // get the first business where this is true and assign in to the constance
                let business = model.businesses.first { business in
                    business.id == selectedBusinessId
                }
                
                // if business is foujnd set it as selected one
                if business != nil {
                    model.selectedBusiness = business!
                }
  • Lesson 02 - Onboarding View

    https://github.com/dmkeks1/CitySights-App

    To create an onboarding view we can use a tabview with a special modifier and set the entry point to this view:

    TabView {
               // CODE
            }
            .tabViewStyle(.page)
            
            
            
            
     //in app entry point file:
     
     .fullScreenCover(isPresented: Binding.constant(true)) {
                        //TODO on dismiss
                    } content: {
                        OnboardingView()
                    }
  • Lesson 03 - Saving Data with AppStorage

    https://github.com/dmkeks1/CitySights-App

    Dismiss Onboarding

    There is an environment functionality for dismissing screens:

    @Environment(\.dismiss) var dismiss
     
    //where we want to dismiss we need to call the dismiss function
    ...
    dismiss()

    Also in the app entry we need to have a “switch” to change the ispresented

    ...
    @State var needsOnboarding = true
    ...
    .fullScreenCover(isPresented: $needsOnboarding) {
                        needsOnboarding = false
                    } content: {
                        OnboardingView()
                    }

    App Storage

    @AppStorage("onboarding") var needsOnboarding = true

    onboarding is the key which is used to store the data on the device

    if in property

    Circle()
                            .frame(width: 10)
                            .foregroundStyle(selectedViewIndex == 1 ? .white : .gray)
                            // if condition in a property
  • Lesson 04 - Location the User with CoreLocation

    CLLocationManager delegate protocol

    image.png

    To ask for permissions for location services:

    image.png

     func getUserLocation() {
            // Check if we have permission
            
            if locationManager.authorizationStatus == .authorizedWhenInUse {
                locationManager.requestLocation()
            }
            else {
            // requests permission
                locationManager.requestWhenInUseAuthorization()
            }
            
        }

    we need to set something (class) as a location manger, see

  • Lesson 06 - Downloading Images

    see:

    https://github.com/dmkeks1/CitySights-App/blob/main/CitySights%20App/ListView.swift

    if let imageUrl = b.imageUrl {
                                // Display the business image
                                AsyncImage(url: URL(string: imageUrl)!) { image in
                                    image
                                        .resizable()
                                        .frame(width:50, height:50)
                                        .scaledToFill()
                                        .clipShape(RoundedRectangle(cornerRadius: 6))
                                        .padding(.trailing, 16)
                                } placeholder: {
                                    ProgressView()
                                        .frame(width: 50, height: 50)
                                }
     
                            }
                            else {
                                Image("list-placeholder-image")
                                    .padding(.trailing, 16)
                            }

    to cache images:

  • Lesson 07 - Enhancing the Yelp Query

    https://github.com/dmkeks1/CitySights-App/tree/main

    .focused($queryBoxFocus) // only shows when textbox is focused
  • Lesson 08 - Launching other Apps URLSchemes

    https://github.com/dmkeks1/CitySights-App

    if let url = URL(string:"tel:\(business?.phone ?? "")") {
                            Link(destination: url) {
                                Text(business?.phone ?? "")
                            }
                        }
                        else {
                            Text(business?.phone ?? "")
                        }

    Settings APp:

    if model.locationAuthStatus == .denied {
                    Text("Please allow location services for this app to see sights near you")
                        .padding(.horizontal)
                    Button {
                        if let url = URL(string: UIApplication.openSettingsURLString) {
                            UIApplication.shared.open(url)
                        }
                    } label: {
                        Text("Open Privacy Settings")
                    }
                    .buttonStyle(.bordered)
     
                }

iOS Databases and Basic Figma December 2, 2024

https://www.dropbox.com/scl/fo/0y5bxp06mvnoysq8cyfjd/h?rlkey=zlhlx0iqp3ihg519kg39evl9m&dl=0

  • Lesson 01 - Databases Overview

    Local Database

    image.png

    Remote Databases

    image.png

    image.png

  • Lesson 02 - What is SwiftData

    image.png

    @Model

    **@Model** // needed for SwiftData
    class DataItem: Identifiable {
        var id: String
        var creationDate: Date
        
        init() {
            id = UUID().uuidString //every instance will get UUID
            creationDate = Date() // and date
        }
    }

    https://github.com/dmkeks1/swiftdata-demo/blob/main/swiftdata-demo/DataItem.swift

    modelContainer in entry point

    @main
    struct swiftdata_demoApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    **.modelContainer(**for: DataItem.self) // if you have more datatypes you can put an array here
            }
        }
    }
     

    https://github.com/dmkeks1/swiftdata-demo/blob/main/swiftdata-demo/swiftdata_demoApp.swift

    @Environment Instance where we need it

     **@Environment(\.modelContext) private var context**

    https://github.com/dmkeks1/swiftdata-demo/blob/main/swiftdata-demo/ContentView.swift

  • Lesson 03 - SwiftData Operations - CRUD

    see add, update, create in source code

    https://github.com/dmkeks1/swiftdata-demo/blob/main/swiftdata-demo/ContentView.swift

  • Lesson 04 - SwiftData Queries December 3, 2024

    image.png

    https://developer.apple.com/documentation/foundation/predicate

    Filter with @Query predicate

    image.png

    @Query(filter: #Predicate<Person> { person in
    	person.age > 50 || person.age < 20
     
    }, sort: \Person.age, order: .reverse)  //order is adjustable

    Example for changing filters while using a picker:

    we are passing the selection to the initializer:

    import SwiftUI
    import SwiftData
     
    struct PeopleListView: View {
        
        @Environment(\.modelContext) private var context
        
        @Query(filter: #Predicate<Person> { person in
            person.age < 20
        }, sort: \Person.name, order: .reverse)
        private var people: [Person]
        
        var body: some View {
            List(people) { p in
                
                HStack {
                    Text(p.name)
                    Spacer()
                    Text("Age: \(String(p.age))")
                }
                    .swipeActions {
                        Button("Delete") {
                            context.delete(p)
                        }
                    }
            }
        }
        
        init(filter: Filter) {
            // Based on the filter, set the predicate
            if filter == Filter.kids {
                _people = Query(filter: #Predicate<Person> { person in
                    person.age < 20
                }, sort: \Person.age)
            }
            else if filter == Filter.adults {
                _people = Query(filter: #Predicate<Person> { person in
                    person.age >= 20
                }, sort: \Person.age)
            }
            
        }
    }