100 days of SwiftUI

Table of Contents

My Journey

Day 0, 31st May 2024: Decision

Photo by Jukan Tateisi on Unsplash

I have just completed SXPS288 Remote experiments in physics and space. It was a great experience, and I am looking forward to my next module for my Open University Physics degree starting in October (I am in no hurry to get the qualification, and sign up for a single module each year). I therefore have some space for a summer project.

I would like to add LaTeX processing into my FlashCardGenie iPhone app, and have discovered LaTexSwiftUI  (a SwiftUI view that renders LaTeX equations) available through the Swift Package manager to help me along. However my Swift knowledge and experience is in UIKit. Recognising that SwiftUI is clearly the way forward, I have decided it is time to bite the bullet and migrate FlashCardGenie over to SwiftUI.

It is possible to do this incrementally but my preference is for a clean rewrite so that I can take full advantage of SwiftUI’s modern UI design techniques as well. So earlier on this year I completed the Apple introductory tutorials on SwiftUI. My other commitments meant that I did not consolidate that knowledge properly, other than confirming a conviction that it is the right direction.

But now I have space to execute my plan for this, which is:

  • Learn SwiftUI
  • Redesign FlashCardGenie interface in SwiftUI (from UIKit)
  • Rewrite FlashCardGenie using SwiftUI
  • Upgrade rewritten version to enable LaTeX as an option in any text based note attached to a FlashCard. This is key to supporting Mathematics FlashCards and FlashSets better.

After a little research I have decided to Learn SwiftUI using Paul Hudson@twostraws (Hacking with Swift) 100 days of SwiftUI. This demands a daily commitment of 1 hour on the course itself plus a blog posting on progress for 100 days (happily achievable before the end of October). Posting on this website’s blog is my chosen social media platform, so here goes!

Just completed Day 0. My takeaways and thoughts are:

  • Have I have fallen into the lone wolf trap? I do use StackOverflow and other help forums extensively so not entirely, but I have not generally engaged in any specific subject matter forums. I have therefore joined HackingWithSwifts hackingwthswift.com/slack SwiftUI community as a step towards addressing this risk. Might try out an iOS Dev happy hour as well, though the June date is not yet up on the site.
  • mmm Testing, I have let things slip on that. I actually like writing testing code (I became heavily invested in the java testing framework at one point a few years ago) but am embarrassed to admit that while I have formalised automated testing for some of my training apps in the past, my actual released app (FlashCardGenie) has been acceptance tested mainly through the process of writing the tutorial guides and creating the training videos. I need to address this and will do formally in the SwiftUi rewrite. I need to get more familiar with xcTest in XCode as a start….

Day 1, 1st June 2024 : Variables, constants, strings, and numbers.

Photo by David Lezcano on Unsplash

Although I have been using Swift / UIKit for over 3 years now and have an educational support app released on the Apple store, I always like to consider myself a beginner when learning anything new (in this case SwiftUI) since it never fails to deliver, and this is no exception. Today (and indeed the first 14 days) cover the essential fundamentals of Swift itself as a pre-requisite for getting into SwiftUI which you might think should hold nothing new for me…and yet here is a surprisingly long list of essential basics that I had forgotten about (if I ever knew them!):

  • Running up to any chosen line in the playground.
  • You can put emojis in strings
  • You can have multi line strings using triple inverted commas “””
  • .hasPrefix and .hasSuffix methods of String
  • Use of underscores in large constant integers for readability
  • .isMultiple(of:) method of Int
  • How easy it is to demonstrate double arithmetic errors (just print the result of 0.1 + 0.2)!

Day 2, 2nd June 2024: Booleans, string interpolation, and checkpoint 1.

Photo by Priscilla-du-Preez on Unsplash

Some more essential basics that I had forgotten about (happily a shorter list than yesterday):

  • Booleans are named after the mathematician George Boole (1818-1864)
  • .toggle() is alternative to !
  • inefficiency of + for strings

Day 3, 3rd June 2024: Arrays, dictionaries, sets, and enums.

 

Photo by Aaron Burden on Unsplash

In How to store ordered data in arrays

  • Optimisation advantage of Array<T>.reversed() held as a new type ReversedCollection<Array<String>

In How to store and find data in dictionaries

  • Use of default value return if lookup does not exist

In How to use sets for fast data lookup

  • The reason for Set.insert() rather than Set.append() is because Sets are unordered.
  • Although I have used Sets (primarily when I wanted to ensure no duplicates), I was not conscious of the high performance benefit of Set.contains() vs Array<T>.contains() – hence exploit when can.

In How to create and use enums

  • Each enum is its own type hence no need to state the type explicitly for a defined variable due to type safe rules
  • Although I have used enums, I was not conscious of its highly optimised storage method – hence as for Sets, exploit when can.

 

Day 4, 4th June 2024: Type annotations and checkpoint 2

 

Photo by Nick Fewings on Unsplash

Straightforward today – but was pleased I remembered the syntax of how to convert arrays to sets when coding the checkpoint exercise.

Day 5, 5th June 2024: If, switch and ternary operator

 

Photo by Wesley Tingey on Unsplash

Key takeaways:

  • Inefficiency of string comparison vs numbers
  • Inefficiency of .count in Swift
  • Efficiency of .isEmpty
  • Efficiency of not including boolean result == true (I have often done this thinking this was more readable…more fool me)
  • Use of comparable in enums
  • Readability of enums especially when used in conjunction with switch
  • The existence of fall-through in switch statements
  • When the ternary conditional operator are needed (ie when you need to include conditional processing within a statement (like print)
  • The fact that the ternary conditional operator is going to be essential for use in SwiftUI

Day 6, 6th June 2024: Loops

 

Photo by Kateryna Hliznitsova on Unsplash

Key takeaways:

  • Range is its own type in Swift
  • Through 1…12 and up to 1..<12 options
  • array[1…] section of array defined b one sided range (every element from array[1] to end of array)
  • Int.random[in: 1…1000]
  • Double.random[in: 0…1]
  • continue (to break ust the current iteration
  • continue and break to a labelled statement (useful for eg breaking more than one level of loop

 

Day 7, 7th June 2024: Functions

 

Photo by Jametlene Reskp on Unsplash

Key takeaways:

  • Distinction between a parameter (the placeholder label for an input into a function) and an argument (the value passed into the function identified by the placeholder label).
  • A single line function that returns a value does not need the keyword
  • Use tuples to return multiple values (de-structuring to read multiple returns using tuples)
  • Can distinguish a function by its parameter labels (not just its parameter types)
  • When to stop using a parameter label name (when the function is a verb and the parameter is a noun that the verb acts upon) to avoid repeated parameter label and name of what is being passed.

 

Day 8, 13th June 2024: Functions part 2 default parameters and throwing errors

 

Photo by Philippa Rose-Tite on Unsplash

Missed a couple of days away on holiday in Cornwall. Still there actually but a wet rainy day .. good opportunity to get back to 100 days of SwiftUI!

Key takeaways:

  • Optimisation option for .removeAll(keepingCapacity)
  • throws using own eNums
  • try! (when do not care  about a fatal error occurring)
  • try? (when prepared to lose error information)
  • rethrows (for functions that do not throw errors but exist within functions that do)

 

Day 9, 17th June 2024: Closures

 

Photo by Getty Images on Unsplash

Back from holiday in Cornwall! Great tutorial today demystifying Closures. I have used closures quite extensively and indeed successfully written my own functions with functions as parameters, but always with a underlying confusion prevailing in every case.

Paul has sorted that for me at last! I really think I can now improve the readability of the closures I have written to date with this deeper understanding under my belt.

Key takeaways:

  • “in” as a marker separating type definition from code.
  • When to use shorthand syntax and when not.
  • When trailing closures work best
  • I was confusing the syntax associated with the definition of function parameters with the syntax options associated with executing them. 

Day 10, 20th June 2024: Structs, computed properties and property observers

 

Photo by Daniel Lerman on Unsplash

I continue to be amazed about how much I am gaining from going through all the basics again. Can anyone guess the link between the photo I have chosen today and the subject matter? The earlier ones are obvious but going up to 100 in the same vein was clearly a non starter….

Key takeaways:

  • Clarity on rationale for the mutating keyword and its associated constraints. 
  • Clarity on the init function for structs.
  • Clarity on the implication of assigning default values to constants (let) and variable (var) properties on the required signature when initialising a new struct.
  • A useful comparison of tuples with structs
  • An understanding of the difference between a function and a method.
  •  willSet and oldValue
  • The use of an extension retain automatic struct initialisers

On the slightly negative side I discovered that playground does not handle function variables the same way as in a project (printed output is not displayed).  

Day 11, 22nd June 2024: Structs part 2: access control, static properties and methods

 

Photo by Mathurin NAPOLY on Unsplash

Key takeaways:

  • private(set) for write only access control
  • use of static properties for example data
  • self vs Self (self is current value of this instance of a struct while Self is the current type of this instance of a struct)

Day 12, 28th June 2024: Classes

 

Photo by KOBU Agency on Unsplash

Key takeaways:

  • 5 differences between classes and structs: inheritance, non auto init, copies points to same data, de-init, constant classes let… can have variable properties no need for the mutating keyword on its updating functions
  • use of final to prevent inheritance
  • super.init required to initialise parent class
  • add manual copy to create unique copies
  • using deinit – no parameters

Day 13, 29th June 2024: Protocols and extensions

 

 

Photo by Getty images on Unsplash

Lots of both reminders and genuinely new things learnt today!

  • Properties in protocols can be finely restricted by {get set}
  • Opaque return types effective in broadening applicability using the “some” keyword.
  • Add String.trimmingcharacters(in: whiteSpacesAndNewLines) into a String extension
  • Naming guidelines for something returning a new value: end in something like “…ed” or “…ing” eg trimmed. But if returning in place have no extended end:  eg trim
  • Rationale for restriction of only addition of computed properties within protocols
  • In order to retain auto init for structs, add init as an extension
  • Add default implementations in extensions to protocols (I have actually used this feature before without truly appreciating what I was doing) 
  • Use of Self to represent current type to return for example either an Int or a Double.

Day 14, 30th June 2024: Optionals

 

 

Photo by Nick Smith on Unsplash

The last day covering material that I should already know…here are todays surprises!

  • Can do if let number = number (ie use the same variable name – I am forever creating new names for these statements)!
  • nil – sometimes I mistakenly think of null rather than nil as the value held by an empty optional
  • Rationale for Swift preventing fall through option when using guard
  • Can use guard without an optional….
  • Dictionary default values: let new = captains[“serenity: default: “N/A”]
  • try? useful when do not care about the error just success

Day 15, 01 July 2024: Review

 

 

Photo by Nathan Andersen on Unsplash

Hurrah! At last no surprises…..

Day 16, 04 July 2024: WeSplit Introduction

 

 

Photo by Mateo Vella Unsplash

Yes very different from UIKit – key takeaways at this early stage:

  • Refresh preview Option Command P
  • Creating a form:
    • Forms: scrolling list of static controls like texts and images plus UI controls like textField, toggle, button
    • Section: Group related items on a form
    • NavigationBar, Navigation stack
    • Modifying state: @State as a keyword on var to enable it to automatically mutate using $ binding state to UI controls,  designed for simple properties stored in one view 

Day 17, 05 July 2024: WeSplit Part 2

 

 

Screenshot of my iPhone running WeSplit!

My first SwiftUI app! I enjoyed experimenting a little with some minor improvements:

  • Rather than defaulting the amount to checkAmount £0.00  I set it as an optional. This saves the need to delete the initial zero value before entering.
  • Rather than describe numberOfPersons as given (when in truth it is not) I set it as numberOfPersonsIndex (which is what it is) and then created a computed property called numberOfPersons which added 2 to the value. This keeps the code telling the truth for improved readability.
  • I replaced the picker for selecting the number of people with a pickerStyle(.wheel). I think this is nicer.
  • I added a text display for the Grand Total (including tip) as well as Each to pay. I really wanted to know what the total was including the tip.

Day 18, 08 July 2024: WeSplit Part 3

 

 

Photo in collaboration with Phạm Nhật on Unsplash

Ah! I had anticipated some of the challenges set by Paul and made a few additional ones of my own.

  • Noted that the solution to challenge 3 send you to a page that is not automatically scrolled to the selected value if it is further down the long list than one pages presentation.
  • Explored the Forum for 100 days of SwiftUI to see if anyone else had raised this – they had not and I am still awaiting an answer on whether there is a way to invoke the desired behaviour.
  • Noticed another post about this app that highlighted some limitation in the display management when using the currency formatter with a SwiftUI TextField and had some fun discussing different possible solutions and their respective implications.

Day 19, 10 July 2024: Challenge: LengthConverter

 

 

Screenshot of my iPhone running LengthConverter

Challenge successfully completed! – my first SwiftUI app from scratch!

Day 20 21 and 22, 14 – 16 July 2024: Challenge: Guess the flag

 

 

Screenshot of my iPhone running Guess the flag when just openned

Screenshot of my iPhone running Guess the Flag answering correctly

Screenshot of my iPhone running Guess the Flag last question incorrectly answered

Challenge successfully completed! – my second SwiftUI app!

Loads of new learning in these three days. It was great to get what felt like a complete app up and running and make a number of changes to it on the challenge day including a few enhancements of my own.

It made more sense to me to combine the three days into one in order to reach a logical end before posting. Here is what stood out for me over these three days:

  • Rationale for .leading and .trailing rather than .left and .right (how easy is it to forget that not everyone writes from left to right), so for some .leading is on the right!
  • Spacers: These are great vs the equivalent tools in UIKit
  • ZStack – brilliantly useful. SwiftUI has really made the design process so much cleaner vs UIKit.
  • Semantic colours. What a lovely concept.
  • .ignoresSafeArea
  • Materials – lovely effects.
  • Easy application of gradients to add vibrancy to the designs. I found myself compromising on designs in UIKit at times due to the associated implementation effort. I can see myself spending more time now really thinking about the design and presentation aspects without as much concern about adding in implementation overhead.
  • Decorative keyword to assist in accessibility. Apple have really thought this issue through.
  • Alerts on state…I need a bit more practice for this to feel natural.

I enjoyed identifying and implementing my own enhancements to the design and implementation as follows:

  • Use of .capsule with ultraThinMaterial for highlighting the flags area.
  • Use of descriptive nouns ie german flag vs flag of Germany in messaging
  • Avoidance of repetition in message and summary at bottom
  • Handling of error on final question to receive the Wrong …message as well as the game over / new game variation.

 

Day 23 &24, 20-24 July 2024: Views and modifiers & wrap up

 

 

Photo by Linus Sandvide on Unsplash

These are the style of chapters I personally enjoy the most – lightening the darkness with insight and understanding.

My key takeaways:

  • SwiftUI uses structs to enforce simplicity through isolating state and guaranteeing performance through reduced overhead from layers of inheritance
  • There is nothing behind the body that we can / should access. The view must be specified to fill the screen if that is what we wish.
  • Conceptually modifiers increment the previous view creating a view of a new type containing the previous view of the previous type. As a result the sequence of modifiers matters.
  • some View saves us the overhead of precisely defining the types of the views we are creating. Under the covers, each View has a property called associatedType within it that Swift creates for us automatically.
  • Use the ternary operator to create conditional modifiers on views to minimise overheads on SwiftUI. If else creates two views, ternary (syntax WTF!) creates just one
  • Environment modifiers are modifiers that, when applied to a View container (a View that contains other Views), are overridden by the child Views within the containing View. Regular modifiers are not overwritten. You can apply either type of modifiers to View containers – see the documentation for which modifiers act as environment and which act as regular modifiers.
  • You can create Views as properties to help structure code. These properties can be computed to include multiple views. Beware not to overdo this, it is an indicator of complexity implying the need to break things down further (see below).
  • Break up complex views by creating custom Views. These are separate structs containing their own properties and body view to which the body view in the SwiftUI ContentView refers. Look to do this extensively to encapsulate and simplify.
  • For greater flexibility can create own custom modifiers. These are  structs of type ViewModifer with func body(content: Content) -> some View that then enhances / extends modifies the content in some way. Add these as an extension to View for easy use with a func that calls modifer(modifierName(paras)) as required.
  • Can create own custom View containers eg like VStack. These are structs of type View that take in a closure that returns an object of type Content (itself a View).

Day 25, 03 August 2024: BrainTrain Challenge

 

 

Screen shots of my iPhone running the Braintrain challenge

 

Challenge successfully completed!

I spent quite a lot of time on this one, working out some standard structures for organising code and common elements required for all apps such as:

  • Breaking down Views into minimum components
  • Struct for all constants
  • Colour extension to enable identification by hex code
  • Organisation of custom modifiers
  • How to pass state information from one View to another using @Binding

 

Day 26, 12 August 2024: Stepper, DatePicker, Dates and Intro to Machine Learning

 

 

Photo by Getty Images on Unsplash

Slightly odd mixture of subjects today. Stepper, DatePicker and using dates fitted easily under a general classification of input controls and associated functions, while machine learning is just a completely different topic. I would have split the latter into its own day but then the first three would have been too short a day, so I would have added those onto a previous day.

I needed to view the video CreateML for Everyone to make sense of the Machine Learning section but having done that it certainly took some of the mystery out machine learning and I could see the potential.

Day 27 & 28, 13-14 August 2024: BetterRest Challenge

 

 

Screen shots of my iPhone running the completed BetterRest challenge

 

Another challenge successfully completed!

Key points arising:

  • Extra control achieved through Section header closure  
  • Form impact vs VStack
  • Use of .labelsHidden() to ensure accessibility

 

Day 29 – 31, 15-19 August 2024: WordScramble Project 5

 

 

Screen shots of my iPhone running the completed WordScramble challenge

 

In addition to actual challenge:

  • Maintained the focus on the input textfield

 

Day 32 – 34, 20-30 August 2024: Animations Project 6

 

 

Screen shots of my iPhone running the completed Animations challenge

 

This project was more involved and I changed my approach. Now I listen to the videos all the way through then I redo each section by reading through the text and working everything through using XCode trying to recall the code initially without reference then looking up to verify as required. Each section then corresponds to a source controlled repository state for easy future reference. I particuallry enjoyed the snake effect custom animation. Very satisfying!

 

Day 35 10 September 2024: Animations Project Challenge

 

 

TestyTimes in action

Day 36, 11th September 2024: iExpense: Project 7 Part 1

Photo by John Vid on Unsplash

Key takeaways are

  • Showing and dismissing screens: You can show and hide views as sheets by adding a sheet modifier, Use @Environment to get access to the dismiss environment function
  • Saving data: Add compliance to codable to enable archive and unarchiving of structs and classes via JSONEncoder() and JSONDecoder() by try? to encode the UserDefaults.standard.set()
  • deleting rows from a list: To delete items using .onDelete must be attached to ForEach
  • The implications of using @State with a class is that you need to mark the class as @Observable to ensure views are redrawn when a property inside it changes.

 

 

 

 

 

The nanoworld…

I have enjoyed discussing infinity in earlier blogs, today I am moving to the other end of the scale. The complexity and detail that occurs in the nanoworld is stunning. Click on the link below to reveal the relative size of the fundamental objects of life on an interactive webpage created by Learn.genetics, the genetic science learning centre. Advance the slider to fall deeper into the rabbit hole!

Some infinities are bigger than others

Flying pig to illustrate unbelievable concept of some inifinities being larger than others

Pigs can fly!

Another apparently ridiculous assertion! After all, did I not state in my last post that infinity is a concept that describes something unbounded? So how can anything be greater than something that is unbounded? The answer is that it depends on the way it is unbounded. Lets consider the infinite list of all the positive numbers in sequence 1,2,3 etc. You know that the list is complete because you have not missed any integer. After all, by definition you have listed them in order (2 definitely follows 1 and 3 definitely follows 2 and so on). 

Now think of the infinite list of even numbers listed in sequence 2,4,6 etc. Again the infinite list of these numbers is complete since by definition you have listed them in order. You can now map each number on the first list to each number on the second (1 maps to 2, 2 maps to 4, 3 maps to 6 etc). There is no number on the first list that you cannot map to a number on the second list since any integer can be multiplied by 2 and every number that is divisible by 2 is on the second list. These lists are both unbounded but they are unbounded in the same way (because of this one to one mapping) and are known as “countable” lists. 

Now try and do the same thing with the positive real numbers. You write the first positive real number down ….umm, what are you going to write? Perhaps you think of 0.1 but then you know that 0.01 is less than that. So where are you going to start?  You literally cannot write down a list of all the real numbers in sequence as whatever number you start with you have missed out a number smaller than it! As a result you cannot possibly map each integer on our list of integers to a corresponding number on the list of real numbers because whatever real number you choose to map to the integer 1, there is another (actually an infinity of other) smaller real numbers that you could choose to map it to. So you see that the list of all real numbers is not even countable and therefore it is a bigger infinity than the list of all positive integers (or the infinite list of any other countable things). You think that must be it, but what is even more astonishing is that mathematicians have identified lists that are even more unbounded than the list of real numbers. Good grief, my head aches…time to stop!

Is infinity + 1 > infinity?

Light display image of the mathematical symbol for infinity to illustrate a post about inifinity

Photo by Freddie Marriage on Unsplash

What about infinity plus a million? You guessed it:  they are equal. Infinity + 1 = infinity plus a million = infinity! This does seem counter intuitive until you recognise that you are only lured into thinking that infinity plus something is bigger than infinity because you are unconsciously treating infinity as a number, which it is not. Infinity is an expression used to describe a concept of something that has no bound. This is not something that we are used to handling within our highly bounded 3 dimensional physical  world! 

Indeed “infinity plus one” is virtually the same as saying “imagination plus one” and only appears to make slightly more sense because we know that the number line is also unbounded and we are usually first exposed to infinity in that mathematical context, which understandably establishes a connection between the two in our minds. Notwithstanding, the connection is certainly not that they are both numbers!

Infinity….

Photo by Gabriel Manlake from Unsplash

I am fascinated by infinity, mainly because it's a wonderland of provably bizarre but true counter intuitive concepts (like Schrodingers cat or Einsteins time dilation effect) and such things are always interesting.  So, I plan to embark on a short series of infinity related blogs.

I will start with a wonderfully evocative description of eternity (the "time" manifestation of infinity). Unfortunately I do not know who came up with it so I cannot give a proper credit. An internet search appears to credit different people but none with any apparent authority so I will put it down to that well known genius "anon"!

"Think of a ball of steel as large as the world, and a fly alighting on it once every million years. When the ball of steel is rubbed away by the friction, eternity will not even have begun."

This description of a minute fraction of eternity is so large that we can hardly take it in. Here's a great article titled Putting time into perspective that nicely presents some ideas about why.

 

 

Revision!

Navigating revision in tutorials

Revision can be seen as a chore, a thankless trudge through familiar (or frustratingly unfamiliar!) territory. I seek to turn that perspective on its head in this article for The Access Project (TAP) tutors. Just click on the frustrated student below (photo with thanks to Jeshoots.com, Unsplash) to discover my approach to helping students navigate revision in tutorials.

Woman biting pencil while sitting on chair in front of computer

Maths and music

There has long been a suspected link between mathematics and music with some suggesting that learning an instrument promotes mathematical ability and others convinced that it works the other way around (check out this article from the Scientific American). In keeping with that theme here is Brazilian pianist Elaine Rodrigues demonstrating the type of resilience under pressure that would serve any budding mathematician well as they grapple with some unforgiving algebra!