Mobile App Development 2022W Lecture 5

From Soma-notes
Jump to navigation Jump to search

Video

Video from the lecture given on January 26, 2022 is now available:

Video is also available through Brightspace (Resources->Zoom Meetings (Recordings, etc.)->Cloud Recordings tab). Note that here you'll also see chat messages.

Notes

Lecture 5
---------

recall:
 @State variables update their view (and child views) when they are changed
        automatically

 @Binding variables allow access to the @State variables in a parent view
 (so, kind of like a pointer/reference)


GeometryReader gives us the geometry of the enclosing view
  - what are the dimensions

Note that child views are given a box for where they should draw
their stuff by their parent view.   But the child views doesn't have to follow
these guidelines, it can draw itself wherever it wants


When we include one view inside another, the parent view can pass data to a child view
 - By default, data is passed by value, like a function call
 - we can pass by reference (so changes in the parent are reflected in the child), that's what we do with @State/@Binding/$

General programming language terminology
 - call by value: you call a function with parameters,
    changes to those parameter values won't be seen by the caller
 - call by reference: changes to parameter values in the function *will*
    be seen by the caller, because both are actually accessing the same
    underlying variable

In C, all functions are call by value, but you can pass in pointers to get call by reference semantics

In python, dictionaries/objects are always call by reference (I believe),
but otherwise it is call by value

What we're really talking about here is, how can a function call affect the caller.
  - do we just care about the return value?
  - or will there be side effects on the data passed in?

In other words, will the function mutate our data, or is data immutable?

Older programming languages were all based on mutable state
  - it more closely matches how the underlying hardware works
  - can be very efficient
  - but, it can lead to errors/hard to debug code,
    because you have to consider *who* had access to the data,
    as any of them could have messed it up

Languages like Swift, and frameworks like SwiftUI, are focused on immutable data
 - less error prone, a value never changes
 - but, can be inefficient
 - magic of Swift and SwiftUI, though, is it is actually very efficient
   (they achieved this through a lot of low-level complexity that they
   try to hide but can be exposed)
 - if you want something mutable, you have to take extra steps
   and if you don't do it right the system will fight you

Remember:
 "let" makes an immutable binding between a name and a value
 "var" creates a variable that can take on different values at differen times

So let's inside of a View are fine, because they are immutable
But assignments to variables are *not* allowed, because they are mutable
but Views are immutable

Another way of thinking of SwiftUI views is that they are written in a domain specific language (the SwiftUI view language)
 - so the rules for it are different from the rest of Swift

(To be specific, Views are created using ViewBuilder, which is based on ResultBuilder, a way to create domain-specific languages in Swift)

What is a domain-specific language?
 - it is just a language designed to solve specific kinds of problems
   (problems in a given "domain")
 - idea is the syntax and semantics of the language are designed to
   match the problem, and so make it easier to solve the problem
   using the least amount of code

Say I wanted to create solutions to automatically putting grocery items on shelves
 - I could just make an API with specific functions to do the operations
 - or, I could create a little language for describing the problem of putting grocery items on shelves (or anything on shelves)

(Really, custom APIs are DSLs where we can't really mess with the syntax)

The rules of SwiftUI don't match those of Swift, even though SwiftUI is implemented in Swift
 - because Swift lets you do weird things

So, what are we really doing when we make GUI applications?

We have a few key things:

frame buffer:   memory representation of what's on screen
                (change values in the frame buffer,
	        what's on the screen changes)

input devices:  user does an action, program gets the input
(mouse,
keyboard,
touchscreen)


our code:       handles input, does output by changing the contents of the
                frame buffer


Classic way we organize our code is as an event driven system
 - user input generates an event
 - our code processes the event and updates the screen (and internal data)
 - our code then waits for more user input

If we want to do other things while waiting for input we do it in
parallel or in small chunks so we can always be ready to handle user
input
 - if we aren't ready to handle user input in a timely fashion
   we have input lag/unresponsive interfaces and users get annoyed


But how does this map onto SwiftUI?
 - our code doesn't directly update the screen
 - instead, we describe what the screen should look like *at all times*
   through views
     - the screen should be a function of some data
     - data can either be constant or variable
     - if variable, it should be put into a state variable
     
The views are automatically redrawn (i.e., the framebuffer is updated)
whenever a state variable is changed
 - it is evaluating *all the views*
 - this could be very inefficient, except SwiftUI is very clever in
   how it does updates

When we move to Android development (and later still see iOS Storyboard development), we'll see a genuine event loop
 - we'll have event handlers that will update the screen

And you'll see the code gets much longer and much more complex

When we say views are immutable, we just mean they are immutable
while they are being updated - that's all.
 - and that's why we can't change variables in views,
   it would make it impossible for SwiftUI to calculate deterministically
   how the screen should change while evaluating the views
   (the views can't change while it is being drawn)

 - we *can* change state inside of closures that are called
   when events happen
     - because that is outside of the View updates

So we can't change things dynamically inside of views, state updates
must happen as a result of specific events (and thus in the callbacks
associated with those events)