0% found this document useful (0 votes)
6 views

iOS App Dev - iOS W - Big Nerd Branch

Uploaded by

yanichik20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

iOS App Dev - iOS W - Big Nerd Branch

Uploaded by

yanichik20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 112

Interface Builder

1. The rectangle that you see in the Interface Builder canvas is called a scene and
represents the only “screen,” or view, your application has at this time.
2. IB lets you drag objects from a library onto the canvas to create instances and also lets
you establish connections between those objects and your code. These connections can
result in code being called by a user interaction.
3. A crucial feature of Interface Builder is that it is not a graphical representation of code
contained in other files. Interface Builder is an object editor that can create instances of
objects and manipulate their properties. When you are done editing an interface, it does
not generate code that corresponds to the work you have done. A .storyboard file is an
archive of object instances to be loaded into memory when necessary.
4. Canvas is the entire view shown inside the IB, which may include many screens/views
5. Auto Layout
a. Works by specifying position and size constraints for each view object in a scene.
These constraints can be relative to neighboring views or to container views.
b. Container view
i. View object that contains another view

c.
i. You can see in the document outline that the labels and buttons you
added are indented with respect to a View object. This view object is the
container of the labels and buttons, and the objects can be positioned and
sized relative to this view.
d. Auto Layout Options

i.
ii. Update Frames, Align, Add New Constraints, Resolve Auto Layout
Issues, Embed In
e. Horizontal in Container
i. guarantees that on any size screen, in any orientation, the label will be
centered horizontally.

ii.
f. NOTE: You are going to position the other views horizontally by aligning their
centers with the center of the top label. The effect will be that all the views will be
centered horizontally on the screen. Why not align them with the container, as
you did for the top label? If that position ever needs to change, you will only need
to modify the constraints on the top label and the rest of the labels will follow suit.
g. Horizontal Centers
i. Takes the horizontal center of the first selected label and aligns (with
whatever offset) the other labels horizontal centers
ii. Troubleshoot
1. Horizontal centers constraints being set on label1 instead of the
other selected items -> select the constraints and choose
"reverse first and second item" inside attributes inspector
6. Constraints
a. Control-Drag Constraints
i. Constraints MUST be between views within the same hierarchy. CANNOT
be from different controllers
ii. Can ctrl-drag between views inside the canvas, inside the document
outline, and/or between the two
iii. Sometimes difficult to do within the canvas for example for a super view,
so can use document outline
b. Pinning Constraints
i. Can create multiple constraints at same time

ii. Constrain to Margins


1. when creating a constraint to superview, can do so to the view
itself OR to its margins. This option will constrain to super view's
margins
2. NOTE: each view has margins that define distances from edge for
its sub-views
iii. Mismatch of Constraints to Frames
1. If there is a mismatch between the added constraints and the
current frames, then UPDATE FRAMES options allows you to
update frames to match the constraints

2. Can update ONLY for views that are being updated OR for ALL
c. Alignment Constraints
i. to align between views based on their: centers, edges, baselines, etc
d. Editing Constraints
i. Can edit individual constraints by clicking on item and viewing the Size
Inspector

ii. If you edit a constraint and there are no warnings, the frame will auto
update. If there are warnings, it WILL NOT update
iii. Constraint Math

1.
2. Multiplier of <1 MUST be entered with leading 0 -> 0.5 NOT .5
iv. Build Time vs Run Time Constraint Editing
1. Build Time: done in the Interface Builder
2. Run Time: done in View Controller
v. Constraint Properties
1. Constraint Label: Give constraints a label name so that you can
identify inside the document outline and constraints which item
constraints are referring to
2. Constraint Identifier: allows to identify constraint at runtime
3. Deleting Constraints: if done inside size inspector it will only
deselect it and still can view in document outline. To really delete,
need to delete from editor or in document outline
vi. Constraint Outlet
1. Can create IBOutlet for constraint so it can be edited in runtime

vii. Constraint Priorities


1. Range: 1 - 1000
2. Default 1000: Required
3. Determines order that each constraint is considered - When two
constraints CONFLICT
viii. Constraint Relations
1. Can set constraint that is NOT exact. Instead for it to be >= or <=
to some constant
a. NOTE: for this type there IS NO preference to be closer to
the number. THEREFORE if you do want xcode to get as
close as possible to the number you want, ADD a lower
priority constraint that is exact.
2. Example: set button to disappear on button click and then
auto-adjust other button to adjust its size to the trailing edge to
safe view. Here we set the trailing edge to safe view to 0 BUT set
up the priority to 999 instead of 1000. So that resolves any
potential constraint conflict, and now the constraint is shown with
dotted line to indicate that it IS NOT a required priority

ix. Intrinsic Content Size


1. Content Compression Resistance: priority of this deals with how
much view DOES NOT want to shrink smaller than size of its
content
2. Content Hugging: priority of this deals with how much view
DOES NOT want to grow larger than size of its content
a. 250 default: when set in code
b. 251 default: when set in IB
3. Separate for horizontal and vertical components of view
x.
e. Constraints created on the view that is common between the two elements of the
constraint
i. Here the "View" constraints folder holds all constraints whose common
superview is "View". And the constraints folder inside "First Name Text
Field" holds a width constraint, which is in a under that specific field
because that is the view that is common between the text field and itself is
itself (which is what's relevant for a width constraint)

f. Visual Format Language


i. "‹orientation>:?<superview»<connection>?[<view>]?(<connecti
on><view>)*<connection><superview>?"
1. H:-[find]-[findNext (100@500)]-[findField(>=20)]-|
2. H:, V: -> horizontal, vertical
3. <=, >= -> less than, greater than
4. | -> superview
5. @ -> priority
6. - -> standard space
7. == -> equal width
8. ? -> not required
9. * -> may appear 0 or more times
ii. Options
1. NSLayoutConstraint.constraintsWithVisualFormat("V:|-2
0-[label]-[textField]", options: [], metrics:
metrics, views: views)
2. AlignAIlLeft
3. AlignAllRight
4. AlignAlITop
5. AlignAllBottom
6. AlignAllLeading
7. AlignAllTrailing
8. AlignAllCenterX
9. AlignAllCenterY
10. AlignAllBaseline
11. AlignAllLastBaseline
12. AlignAllFirstBaseline
13. DirectionLeadingToTrailing
14. DirectionLeftToRight
15. DirectionRightToLeft
iii. Kodeco Tutorial Link
7. Editor Preview
a. to enable in xcode 14.3 go to editor assistant then Editor -> Preview
i. shortcut: Cmd+Opt+Enter ONLY WHEN already in editor assistant

b. link to guide
8. Connections
a. Lets one object know where another object is in memory so that the two objects
can communicate.
b. Outlets and Actions: 2 kinds of connections that you can make in IB
i. Outlet: reference to an object
ii. Action: method that gets triggered by a button or some other view that
the user can interact with, like a slider or a picker
iii. @IBOutlet: has no special meaning other than "this is connected to
something in Interface Builder.
iv. @IBAction is similar to @IBOutlet, but goes the other way: @IBOutlet is
a way of connecting code to storyboard layouts, and @IBAction is a way
of making storyboard layouts trigger code
1. @IBAction automatically implies @objc
2. Because @IBAction means you're connecting storyboards
(Objective-C code) to Swift code, it always implies @objc as well
v. Adding Outlet
class ViewController: UIViewController {
@IBOutlet var questionLabel: UILabel!
@IBOutlet var answerLabel: UILabel!
}
1. This code gives every instance of ViewController an outlet named
questionLabel and an outlet named answerLabel. The view
controller can use each outlet to reference a particular UILabel
object (that is, one of the labels in your view). The @IBOutlet
keyword tells Xcode that you will connect these outlets to label
objects using Interface Builder.
2.
vi. Setting Outlets
1. In the document outline, find the View Controller Scene section
and the View Controller object within it. In this case, the View
Controller stands in for an instance of ViewController, which is the
object responsible for managing the interface defined in
Main.storyboard.Control-drag (or right-click and drag) from the
View Controller in the document outline to the top label in the
scene. When the label is highlighted, release the mouse and
keyboard; a black panel will appear, as shown in Figure 1.22.
Select questionLabel to set the outlet.
2.
vii. Defining Actions
1. Target: When a UIButton is tapped, it calls a method on another
object. That object is called the target.
2. Action: method that is triggered, and it contains the code to be
executed in response to the button being tapped.
a. In your application, the target for both buttons will be the
instance of ViewController. Each button will have its own
action.

3. @IBAction func showNextQuestion(_ sender: UIButton){


}
@IBAction func showAnser(_ sender: UIButton){
}
a. The @IBAction keyword tells Xcode that you will be
making these connections in Interface Builder.
b.
viii. Setting Actions
1. Switch back to Main.storyboard. Let’s start with the Next Question
button. You want its target to be ViewController and its action to be
showNextQuestion(_:).
2. Set Target: To set an object’s target, you Control-drag from the
object to its target. When you release the mouse, the target is set,
and a panel appears that lets you select an action.
3. Set Action: When the View Controller is highlighted, release the
mouse button and choose showNextQuestion: under Sent Events
in the connections panel, shown in Figure 1.24.
4.
5.
9. Shortcuts
a. Hold down Option when dragging from Library to canvas/IB/scene to keep Library
open until you manually close it

Simulator
1. Active-scheme: selects the iPhone type for simulator to run
2.

View & View Hierarchy

1. UIView
a. view object is instance of UIView or a subclass, can draw itself, handle events
(like touches), exists w/in hierarchy of views whose root is the application window
2. View Hierarchy
a. each app has single instance of UIWindow
b. example of window hierarchy

c. Screen
i. Once view hierarchy created, it will be drawn to the screen
1. Two Steps:
a. Each view in the hierarchy, including the window, draws
itself. It renders itself to its layer, which you can think of
as a bitmap image. (The layer is an instance of
CALayer.)
b. The layers of all the views are composited together on the
screen.
ii. Screen architecture

iii. UIWindow is the root level UIView component. It draws the phone's
maximal layer and contains an array of UIView
iv. UIView array contains the components of the screen, each drawing
themselves on a layer which they define
3. UIWindow
a. container for all views in the application
b. subclass of UIView
4. Views & Frames
a. view's frame specs a view's size and position relative to superview
b. can build views and hierarchy programmatically or via the interface builder (IB)
c. initialize a view programmatically: init(frame:)
i. frame takes single arg: var frame: CGRect
1. frame is property of UIView
ii. CGRect
1. contains origin and size
a. CGPoint
i. origin: structure of type CGPoint and contains two
CGFloat properties: x and y
b. CGSize
i. size: structure of type CGSize and has two
CGFloat properties: width and height
2.
iii. Point vs Pixel
1. A point is a relative unit of a measure; it will be a different number
of pixels depending on how many pixels are in the display. Sizes,
positions, lines, and curves are always described in points to allow
for differences in display resolution.
d. Interface Builder to Customize View
i. bounds rectangles: gives each view in Interface Builder a blue outline
1. Editor -> Canvas -> Bounds Rectangle

ii. absolute frames: DO NOT use b/c view does not look equally good on
different sizes of screens
1. INSTEAD: use Auto Layout to flexibly compute the frames for you
based on constraints that you specify for each view.
iii. Auto Layout
1. Absolute coordinates make your layout fragile because they
assume that you know the size of the screen ahead of time.
Using Auto Layout, you can describe the layout of your views
in a relative way that enables their frames to be determined at
runtime so that the frames’ definitions can take into account the
screen size of the device that the application is running on.
iv. View's Alignment Rectangle & Layout Attributes

1.
2. First vs Last Baseline: These values are the same as the bottom
attribute for most, but not all, views. For example, UITextField
defines its baselines as the bottom of the text it displays rather
than the bottom of the alignment rectangle. This keeps
“descenders” (the parts of letters like “g” and “p” that descend
below the baseline) from being obscured by a view right below the
text field. For multiline text labels and text views, the first and last
baseline refer to the first and last line of text. In all other situations,
the first and last baseline are the same.
3. Default: By default, every view has an alignment rectangle, and
every view hierarchy uses Auto Layout.
e. Constraints
i. Menus
1. Align

a.
2. Add New Constraint

a.
3. Update Frames

a.
ii. Nearest neighbor - in specific direction
1. ​If a view does not have any siblings in the specified direction, then
the nearest neighbor is its superview, also known as its container.
2. constraints made to a superview are made to its safe area.
iii. Safe Area
1. An alignment rectangle that represents the visible portion of your
interface
iv. Set at least 2 constraints to solidify item in view
1. don't need to define all constraints b/c some will be calculated
v. Finalizing constraints

1.
a. Red edges on left and right side INDICATE that the
alignment rectangle IS NOT completely defined -
specifically that there is some problem along the
horizontal axis.

2.
3. Red right arrow in document outline -> indicates potential issue
with interface. Click this icon to reveal the Auto Layout issues
outline
4.
a. Localization Issue
i. associated with the labels that do not yet have
constraints
ii. Fixed width constraints may cause clipping -
when size (W & H) of object is constrained
explicitly, and the size needs to change (i.e. due to
font) than the frame would not hug to the content
and content would be clipped

1. ->
5. Intrinsic content size - natural size of view based on its content
6. Colors
a. Blue - Alignment rectangle IS FULLY defined
b. Orange - Misplaced View
c. Red - Alignment rectangle IS NOT fully defined
7. Misplaced View - frame for the view in IB is different than the
frame that Auto Layout has computed
a.
b. NOTE: orange dotted line that indicates what the runtime
frame will be
c.

View Controllers
1. View controllers are instances of a subclass of UIViewController. A view controller
manages a view hierarchy. It is responsible for creating the view objects that make up
the hierarchy and for handling events associated with the view objects in its hierarchy.
2. there can only be one initial view controller associated with a given storyboard.
3. UIWindow has property called rootViewController
a. When a view controller is set as the window’s rootViewController, that view
controller’s view is added to the window’s view hierarchy. When this
property is set, any existing subviews on the window are removed and the
view controller’s view is added to the window with the appropriate Auto Layout
constraints.
b. Each application has one main interface, a reference to a storyboard. When
the application launches, the initial view controller for the main interface is
set as the rootViewController of the window
i. The main interface for an application is set in the project settings
4. View controllers become more interesting when the user has a way to switch between
them
5. Add association between VCs in Interface Builder to actual code VC
a. Select the VC in canvas or IB, go to custom class in attributes inspector, and
add/edit the class
6. UITabBarController
a. keeps an array of view controllers
b. It also maintains a tab bar at the bottom of the screen with a tab for each view
controller in its array
c. Tapping on a tab presents the view of the view controller associated with that tab
d. Embed in "Tab Bar Controller" to add VC into the array of VCs of the Tab Bar
Controller - IB represents this embedding as below, and it automatically knows to
make the tab bar controller the initial view controller for the storyboard

i. NOTE: once you embed VC into tab bar controller, that VC turns into
"Item Scene" in the document outline
e.
f. UITabBarController is itself a subclass of UIViewController. A
UITabBarController’s view is a UIView with two primary subviews: the tab bar
and the view of the selected view controller

7. Tab Bar Items


a. Each tab on the tab bar can display a title and an image, and each view
controller maintains a tabBarItem property for this purpose
b.
8. Properties
a. view: As subclasses of UIViewController, all view controllers inherit this important
property. It points to a UIView instance that is the root of the view controller’s
view hierarchy. When the view of a view controller is added as a subview of the
window, the view controller’s entire view hierarchy is added, as shown in Figure
4.2.

i. lazy loading: A view controller’s view is not created until it needs to


appear on the screen. This optimization is called lazy loading, and it can
conserve memory and improve performance.
ii. Two ways to create view:
• in Interface Builder, by using an interface file such as a storyboard
• programmatically, by overriding the UIViewController method loadView()
iii. By default, view set as type UIView. BUT can set as another type. For
example: MKMapView
9. Lifecycle Methods
a. Lifecycle Explanation Article 1
b.
c. NOTE: To preserve the benefits of lazy loading, you should never access the
view property of a view controller in or init(nibName:bundle:)
init(coder:). Asking for the view in the initializer will cause the view controller
to load its view prematurely
d. init(coder:)
i. initializer for UIViewController instances created from a storyboard
ii. When a view controller instance is created from a storyboard, its
init(coder:) is called once
e. init(nibName:bundle:)
i. designated initializer for UIViewController
ii. When a view controller instance is created without the use of a
storyboard, its init(nibName:bundle:) is called once. Note that in
some apps, you may end up creating several instances of the same view
controller class. This method is called once on each view controller as it is
created
f. loadView()
i. is overridden to create a view controller’s view programmatically
g. viewDidLoad()
i. called after the view controller’s interface file is loaded, at which point all
the view controller’s outlets will reference the appropriate objects
ii. overridden to configure views created by loading an interface file. This
method is called after the view of a view controller is created
h. viewWillAppear()
i. called just before a view controller’s view is added to the window
ii. is overridden to configure the view controller’s view each time it appears
on screen
iii. This method and viewDidAppear(_:) are called every time your view
controller is moved onscreen
i. viewDidAppear()
j. viewWillDisappear()
i. viewWillDisappear(_:) and viewDidDisappear(_:) are called every
time your view controller is moved offscreen
k. viewDidDisappear()
10. Accessing SubViews - can access inside viewDidLoad (if need added conf done upon
loading) and viewWillAppear (if need added conf done each time VCs view appears
onscreen)

Programmatic Views

1. Create View Programmatically


a. override func loadView(): create view programmatically by overriding
loadView. Create instance of MKMapView and set the controller's view to equal
that instance
class MapViewController: UIViewController {
var mapView: MKMapView!
override func loadView() {
super.loadView()
mapView = MKMapView()
view = mapView
}
...
b. When a view controller is created, its view property is nil. If a view controller is
asked for its view and its view is nil, then the loadView() method is called.
c. Override Methods
i. The default implementation is to load the layout from the storyboard
THEREFORE we need to override the default implementation and
implement our own
2. Programmatic Constraints
a. UISegmentedControl
i. A segmented control allows the user to choose among a discrete set of
options; you will allow the user to switch between standard, hybrid, and
satellite map types.
override func loadView() {
super.loadView()
mapView = MKMapView()
let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid",
"Satellite"])
segmentedControl.backgroundColor = UIColor.systemBackground
segmentedControl.selectedSegmentIndex = 2
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
view = mapView
view.addSubview(segmentedControl)
}
ii. translatesAutoresizingMaskIntoConstraints: The line of code
regarding translating constraints has to do with an older system for
scaling interfaces – autoresizing masks. Before Auto Layout was
introduced, iOS applications used autoresizing masks to allow views to
scale for different-sized screens at runtime. Every view still has an
autoresizing mask. By default, iOS creates constraints that match the
autoresizing mask and adds them to the view. These translated
constraints will often conflict with explicit constraints in the layout and
cause an unsatisfiable constraints problem. The fix is to turn off this
default translation by setting the property
translatesAutoresizingMaskIntoConstraints to false
iii. UISegmentedControl is a subclass of UIControl
iv. Controls are responsible for calling methods on their target in response to
some event. Control events are of type UIControl.Event
v. UIControl.Event
1. UIControl.Event.touchDown: A touch down on the control
2. UIControl.Event.touchUpInside: A touch down followed by a
touch up while still within the bounds of the control
3. UIControl.Event.valueChanged: A touch that causes the value
of the control to change
4. UIControl.Event.valueChanged: A touch that causes an editing
change for a UITextField
a. For the segmented control, you will use the .valueChanged
event
5. Attaching a target-action pair to the segmented control
a.
b. Anchors & Activating Constraints
i. Anchors are properties on a view that correspond to attributes that you
might want to constrain to an anchor on another view. When you work
with Auto Layout programmatically, you use anchors to create your
constraints. For example, you might constrain the leading anchor of one
view to the leading anchor of another view. This would have the effect of
the two views’ leading edges being aligned.
ii. Constraints need to be activated using isActive method
let topConstraint = segmentedControl.topAnchor.constraint(equalTo: view.topAnchor)
let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo:
view.leadingAnchor)
let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo:
view.trailingAnchor)
topConstraint.isActive = true
leadingConstraint.isActive = true
trailingConstraint.isActive = true
iii. When the isActive property on a constraint is true, the constraint will
work its way up the hierarchy for the items to find the common ancestor to
add the constraint to. It will then call the method addConstraint(_:) on
the appropriate view. Setting the isActive property is preferable to calling
addConstraint(_:) or removeConstraint(_:) yourself.
iv. Constraints need to be added to the nearest common ancestor of the
views associated with the constraint

v.
vi. If a constraint is related to just one view (such as a width or height
constraint), then that view is considered the common ancestor
vii. Segmented control is there but not looking great - behind sensor housing
and status bar

viii. UILayoutGuide
1. layoutMarginsGuide:
2. readableContentGuide:
ix. NSLayoutAnchor
1. added in iOS 9 as new way of creating constraints on views
2. instances NOT created directly
3. properties on UIView & UILayoutGuide:
a. top, bottom, left, right, center x & y, first & last baseline,
width & height, leading & trailing
b. no margins anchors on UIView, use layoutMarginsGuide
4. new way to add constraint:
button1.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAn
chor,constant:20).active=true
5. old way to add constraint:
NSLayoutConstraint(item: buttoni, attribute: .Top, relatedBy: .Equal,
toltem: topLayoutGuide, attribute: .Bottom, multiplier: 1, constant:
20).active = true
6.
c. Layout Guides
i. Safe Area
1. an alignment rectangle that represents the visible portion of your
interface
2. safeAreaLayoutGuide: Programmatically, you access the safe
area through a property on view instances:
safeAreaLayoutGuide. Using safeAreaLayoutGuide will allow
your content to not underlap the status bar at the top of the screen
or the tab bar at the bottom of the screen.
3. Layout guides like safeAreaLayoutGuide expose anchors that
you can use to add constraints, such as: topAnchor,
bottomAnchor, heightAnchor, and widthAnchor
4. Set topAnchor constraint using the layout guide:
let topConstraint =
segmentedControl.topAnchor.constraint(equalTo:
view.safeAreaLayoutGuide.topAnchor, constant: 8)
d. Margins
i. Although you could inset the segmented control using a constant on the
constraint, it is much better to use the margins of the view controller’s
view.
ii. layoutMargins: view property that denotes the default spacing to use
when laying out content. This property is an instance of UIEdgeInsets,
which you can think of as a type of frame
iii. UIEdgeInsets:
iv. layoutMarginsGuide: exposes anchors that are tied to the edges of the
layoutMargins
v. Primary Advantage of using the margins is that the margins can change
depending on the device type (iPad or iPhone) as well as the size of the
device. Using the margins will help your layout look good on any device
e. Explicit Constraints
i. NSLayoutConstraint
1. This initializer creates a single constraint using two layout
attributes of two view objects. The multiplier is the key to creating
a constraint based on a ratio. The constant is a fixed number of
points, similar to what you used in your spacing constraints.
2. You relate a layout attribute of one view to the layout attribute of
another view using a multiplier and a constant to define a single
constraint
3. Initializer:

4. Constants: NSLayoutAttribute.left, NSLayoutAttribute.leading,


NSLayoutAttribute.top, NSLayoutAttribute.width,
NSLayoutAttribute.centerX, NSLayoutAttribute.firstBaseline,
NSLayoutAttribute.right, NSLayoutAttribute.trailing,
NSLayoutAttribute.bottom, NSLayoutAttribute.height,
NSLayoutAttribute.centerY, NSLayoutAttribute.lastBaseline
5.
Text Input & Delegation

1. UITextField
a. Properties
i. UITextInputTraits

1.
b. Text fields are another control and can send an event when the text changes
c. Methods
i. becomeFirstResponder
1. When the text field is tapped, the method
becomeFirstResponder() is called on it
2. this causes the keyboard to appear
ii. resignFirstResponder
1. Call this method to dismiss the keyboard
2. Gesture Recognizer
a. UIGestureRecognizer that detects a specific touch sequence and calls an
action on its target when that sequence is detected. There are gesture
recognizers that detect taps, swipes, long presses, and more
b. UITapGestureRecognizer : detects tap
i. when placed in background, it detects tap on background and can trigger
any function call
ii. adding to background

iii. Connecting gesture recognizer to function call

c. Property Observor
i. Chunk of code that is called whenever a property’s value changes. A
property observer is declared using curly braces immediately after the
property declaration. Inside
ii. willSet: called immediately prior to setting prop
iii. didSet: called immediately after setting prop
iv. Example:
1. var fahrenheitValue: Measurement<UnitTemperature>? {
didSet {
updateCelsiusLabel()
}
}
3. NumberFormatter
a. Foundation's dynamic number formatter - detailed explanation here
b. Properties
i. locale: knows how different regions display symbols, dates, and
decimals and whether they use the metric system
1. an instance of Locale represents one region’s settings for these
variables
2. automatically set to device's current locale
ii. current: instance of Locale that represents the user’s region setting is
returned.
iii. Once you have that instance of Locale, you can ask it questions, like,
“Does this region use the metric system?” or, “What is the currency
symbol for this region?”
let currentLocale = Locale.current
let isMetric = currentLocale.usesMetricSystem
let currencySymbol = currentLocale.currencySymbol
c. Instance of NumberFormatter knows the locale
i. Example code:
//update fahrenheitValue which calls the updateCelsiusLabel method
@IBAction func fahrenheitFieldEditingChanged(_ textField: UITextField){
if let text = textField.text, let value =
numberFormatter.number(from: text){
fahrenheitValue = Measurement(value: value.doubleValue, unit:
.fahrenheit)
} else {
fahrenheitValue = nil
}
}
ii. If the string contains a valid number, the method returns an instance of
NSNumber. NSNumber is a class that can represent a variety of number
types, including Int, Float, Double, and more
iii.
4. Delegation
a. Control events – such as .touchUpInside and .valueChanged – provide controls
with a convenient, predefined list of triggers to call their action methods on their
targets. But sometimes you want a control to report something that is not one of
its predefined events. To have a control send a custom message to a listening
object, you can use the delegation pattern
b. Delegation is object-oriented way to trigger callbacks
c. Delegation is a programmatic pattern where the delegate acts in place of
something else such that when assigned as delegate it will perform the assigned
duties of the thing who's place it is replacing. Example: when a delegate is set for
a UIViewController that means that delegate will now perform the default
duties of the UIViewController class
i. In order to be delegate for certain classes, you MUST conform to certain
required protocol that were set by that class For Example, to be delegate
of an instance of a MKWebView you MUST conform to
WKNavigationDelegate
d. Callback is a method triggered upon occurrence of certain event
e. IMPORTANT: Some objects need to make callbacks for multiple events.
HOWEVER there is no built-in way for those callbacks to coordinate & share info.
This is the problem addressed by delegation – you supply a single delegate
to receive all the event-related callbacks for a particular object. This
delegate object can then store, manipulate, act on, and relay the
information from the callbacks as needed
f. When the user types into a text field, that text field will ask its delegate if it wants
to accept the changes that the user has made. For WorldTrotter, you want to
deny that change if the user attempts to enter a second decimal separator.
5. Conforming to a protocol
a. naming convention for a delegate protocol is the name of the delegating class
plus the word Delegate
i. Example: UITextFieldDelegate
b. can create custom protocols
c. can have optional methods as part of protocol
optional func textFieldShouldBeginEditing(_ textField:
UITextField) -> Bool
d. Two Method Types
i. 1) methods that handle information updates
1. textFieldDidBeginEditing(_:) method if it wants to know
when the user taps on the text field
ii. 2) methods that handle requests for input
1. textField(_:shouldChangeCharactersIn:replacementStrin
g:) is a request for input. A text field calls this method on its
delegate to ask whether the replacement string should be
accepted or rejected. The method returns a Bool, which is the
delegate’s answer
6.

Internationalization & Localization


1. Internationalization: is making sure your native cultural information (like language,
currency, date format, number format, etc.) is not hardcoded into your application.
a. Software Slang: i18n
2. Localization: is the process of providing the appropriate data in your application based
on the user’s Language and Region settings.
a. Software Slang: L10n
3. Localization Steps
a. Check English in Main.storyboard

b. Select project in navigator & go to Info tab

c. Click the button under Localizations and select Spanish (es). In the dialog,
uncheck the LaunchScreen.storyboard file; keep the Main.storyboard file
checked. Make sure that the reference language is Base and the file type is
Localizable Strings. Click Finish. This creates an es.lproj folder and generates
the Main.strings file in it that contains all the strings from the base interface file

d. In project navigator will now have strings files for English and Spanish

e. Look in the project navigator. Click the disclosure button next to Main.storyboard
(Figure 7.17). Xcode moved the Main.storyboard file to the Base.lproj directory
and created the Main.strings file in the es.lproj directory.
f. Click on the Spanish version of Main.strings. You will notice that it includes
references to all the text elements in Main.storyboard. You will also notice that
the text is not in Spanish. You have to translate localized files yourself
g. Open the active scheme pop- up and select Edit Scheme. Make sure Run is
selected on the lefthand side and open the Options tab. Open the Application
Language pop-up and select Spanish (Figure 7.18). Finally, confirm that Spain is
still selected in the Application Region pop-up. Close the window.

4. lproj directories: used to store localized resources for different languages. The name
"lproj" is derived from "language projects." These directories contain localized versions
of user interface files, such as storyboards, nib files, and strings files.The structure of
these directories is typically like this:

a. Base.lproj: This directory contains the base (default) versions of the


resources.
b. en.lproj, fr.lproj, etc.: These directories contain language-specific
versions of the resources, such as translated strings files or modified user
interface files.
c. When an app runs on a device, the system automatically loads the appropriate
localized resources based on the user's language preferences. This allows
developers to provide a localized user experience for different regions and
languages.
d. The Base.lproj is used when a specific localization is not available.
5. Bundle knows how to search through localization directories for every type of resource
using the instance method url(forResource:withExtension:)
let path = Bundle.main.url(forResource:"Boo", withExtension: "png")
a. When attempting to locate the resource, the bundle first checks to see whether
the resource exists at the top level of the application bundle. If so, it returns the
full URL to that file. If not, the bundle gets the device’s language and region
settings and looks in the appropriate lproj directories to construct the URL. If it
still does not find it, it looks within the Base.lproj directory. Finally, if no file is
found, it returns nil
b. In the application bundle shown in Figure 7.21, if the user’s language is set to
Spanish, Bundle will find Boo.png at the top level, Tom.png in es.lproj, and
Hat.png in Base.lproj.
c. When you add a new localization to your project, Xcode does not
automatically remove the resources from the top-level directory. This is
why you must delete and clean an application when you localize a file –
otherwise, the previous unlocalized file will still be in the root level of the
application bundle. Even though there are lproj folders in the application
bundle, the bundle finds the top-level file first and returns its URL
6. For the More Curious: Importing and Exporting as XLIFF
a. Export:
i. To export the localizable strings in XLIFF, select the project (WorldTrotter)
in the project navigator. Then select the Product menu, and then Export
For Localization.... On the next screen, you can choose whether to export
existing translations (which is probably a good idea so the translator does
not do redundant work) and which languages you would like exported
(Figure 7.22)
b. Import:
i. select the project (WorldTrotter) in the project navigator. Then select
Product → Import Localizations.... After choosing a file, you will be able to
confirm the updates before you import

7. NSLocalizedString and strings tables


a. When creating strings dynamically or display string literals, you need to create
string table to display this content in translated manner
b. Strings Table: resource file with key-value pairs file containing translations that
will be needed in app
c. Example to internationalize a string:
let greeting = "Hello!"
let greeting = NSLocalizedString("Hello!", comment: "The greeting for the user")
d. The key is the lookup value in a strings table. At runtime,
NSLocalizedString(_:comment:) will look through the strings tables bundled
with your application for a table that matches the user’s language settings. Then,
in that table, the function gets the translated string that matches the key
e. Create string table:
i. In terminal: go to directory where VC located
ii. Create with command: genstrings MapViewController.swift
iii. Results in file named: Localizable.strings and placed in project folder
in finder but not attached to project
iv. Add file to project with: File → Add Files to "WorldTrotter"... menu item
v. Now it looks like this. Notice that the comment above each string is the
second argument you supplied to the NSLocalizedString function. Even
though the function does not require the comment argument, including it
will make your localizing life easier

vi. Now that you have created Localizable.strings, you need to localize it in
Xcode. Open its file inspector and click the Localize... button in inspector

vii. Make sure Spanish is selected from the pop-up and click Localize
viii. Select Localizable.strings in the project navigator again and add the
English localization by checking the checkbox next to that language in the
file inspector
ix. In the project navigator, click the disclosure triangle that now appears next
to Localizable.strings. Open the Spanish version. The string on the left
hand side is the key that is passed to the
NSLocalizedString(_:comment:) function, and the string on the right
hand side is what is returned. Change the text on the righthand sides of
the pairs to the Spanish translations
x. Now Build & Run!
Debugging

1. Basics
a. Interpreting Console Messages
i. Assume you had a connected UIButton which you then decided to change
to a UISwitch but failed to make the connection change as well in
storyboard and made it only in code
@IBAction func buttonTapped(_ sender: UIButton) {
@IBAction func switchToggled(_ sender: UISwitch) {
print("Called buttonTapped(_:)")
}
ii. Debug logger will show the following - here is first part
2024-02-10 05:44:48.063666-0800 Buggy[81895:2057990] -[Buggy.ViewController
buttonTapped:]: unrecognized selector sent to instance 0x12dd06340
2024-02-10 05:44:48.086963-0800 Buggy[81895:2057990] *** Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '-[Buggy.ViewController
buttonTapped:]: unrecognized selector sent to instance 0x12dd06340'
iii. remember that an iOS application may be written in Swift, but it is still built
on top of Cocoa Touch, which is a collection of frameworks written in
Objective-C. Objective-C is a dynamic language, and when a message is
sent to an instance, the Objective-C runtime finds the actual method to be
called at that precise time based on its selector, a kind of ID
iv. What's unrecognized selector sent to instance 0x12dd06340
1. app tried to call a method on an instance that did not have it
v. Why Buggy.ViewController and NOT ViewController?
1. Swift namespaces include the name of the module, which in this
case is the app name. Second it is located at memory address
vi. What's -[Buggy.ViewController buttonTapped:]
1. Objective-C message enclosed in square brackets in the form
[receiver selector]
2. The receiver is the class or instance to which the message is sent
3. The dash (-) before the opening square bracket indicates that the
receiver is an instance of ViewController
4. If it had a plus sign (+) it would indicate that the receiver was the
class itself
5. The console tells you that the selector buttonTapped: was sent
to an instance of Buggy.ViewController but it was not
recognized
6. Next line of the message adds the information that the app was
terminated due to an “uncaught exception” and specifies the
type of the exception as NSInvalidArgumentException.
vii. Then Debugger includes the stack trace
*** First throw call stack:
(
0 CoreFoundation [0x000...] __exceptionPreprocess + 172
1 libobjc.A.dylib [0x000...] objc_exception_throw + 56
2 CoreFoundation [0x000...] +[NSObject(NSObject)
instanceMethodSignatureForSelector:] + 0
3 UIKitCore [0x000...] -[UIResponder doesNotRecognizeSelector:] + 232
4 CoreFoundation [0x000...] ___forwarding___ + 1308
5 CoreFoundation [0x000...] _CF_forwarding_prep_0 + 92
6 UIKitCore [0x000...] -[UIApplication sendAction:to:from:forEvent:] + 96
7 UIKitCore [0x000...] -[UIControl sendAction:to:forEvent:] + 108
8 UIKitCore [0x000...] -[UIControl _sendActionsForEvents:withEvent:] + 268
9 UIKitCore [0x000...] -[UIButton _sendActionsForEvents:withEvent:] + 120
10 UIKitCore [0x000...] -[UIControl touchesEnded:withEvent:] + 392
11 UIKitCore [0x000...] -[UIWindow _sendTouchesForEvent:] + 900
12 UIKitCore [0x000...] -[UIWindow sendEvent:] + 3180
13 UIKitCore [0x000...] -[UIApplication sendEvent:] + 692
14 UIKitCore [0x000...] __dispatchPreprocessedEventFromEventQueue + 1880
15 UIKitCore [0x000...] __processEventQueue + 5556
16 UIKitCore [0x000...] __eventFetcherSourceCallback + 156
17 CoreFoundation [0x000...]
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
18 CoreFoundation [0x000...] __CFRunLoopDoSource0 + 172
19 CoreFoundation [0x000...] __CFRunLoopDoSources0 + 232
20 CoreFoundation [0x000...] __CFRunLoopRun + 748
21 CoreFoundation [0x000...] CFRunLoopRunSpecific + 584
22 GraphicsServices [0x000...] GSEventRunModal + 160
23 UIKitCore [0x000...] -[UIApplication _run] + 868
24 UIKitCore [0x000...] UIApplicationMain + 124
25 libswiftUIKit.dylib [0x000...]
$s5UIKit17UIApplicationMainys5Int32VAD_SpySpys4Int8VGGSgSSSgAJtF + 100
26 Buggy [0x000...] $sSo21UIApplicationDelegateP5UIKitE4mainyyFZ + 120
27 Buggy [0x000...] $s5Buggy11AppDelegateC5$mainyyFZ + 44
28 Buggy [0x000...] main + 28
29 dyld [0x000...] start_sim + 20
30 ??? [0x000...] 0x0 + 4380434216
31 ??? [0x000...] 0x0 + 2476979795053772800
)
libc++abi: terminating due to uncaught exception of type NSException
1. a list of all the functions or methods that were called up to the
point of the application crash. Knowing which logical path the
application took before crashing can help you reproduce and fix a
bug. None of the calls in the stack trace had a chance to return,
and they are listed with the most recent call on top
2. Each row includes a call number, the module name, a memory
address, and a symbol representing the function or method
viii. You can explicitly see the connection to the missing action in Interface
Builder. Open Main.storyboard and Control-click the View Controller on
the document outline. The black panel will show a warning icon beside
the buttonTapped: action in the Received Actions section, as shown in
Figure 8.3

ix. To fix you can revert change to UIButton or update to switch in Interface
Builder in Main.Storyboard
b. Caveman Debugging
i. strategically placing print() calls in your code to verify that functions
and methods are being called (and called in the proper sequence) and to
log variable values to the console to keep an eye on important data
ii. Literal Expressions
1. Caveman debugging gets a little more sophisticated when you use
literal expressions to make the console messages more explicit.
Swift has four literal expressions that can assist you in logging
information to the console (Table 8.1):

2. View in action
@IBAction func buttonTapped(_ sender: UIButton){
// print("Called buttonTapped(_:)")
print("Method: \(#function) in file: \(#file) line: \(#line) called.")
}
3. Displays
Method: buttonTapped(_:) in file:
/Users/yanichik/dev/iosBigNerdRanch/Buggy/Buggy/ViewController.swift line: 14 called.
4. While caveman debugging is useful, be aware that print
statements are not stripped from your code as you build your
project for release. These print statements can be viewed if a
device is connected to a Mac, so be careful about printing
sensitive information
c. LLDB - Xcode Debugger
i. Information when stopped at a breakpoint

ii. Observe these as you are paused inside buttonTapped(_:) after click
1. Click the disclosure triangle for sender, and you will see that it
contains a baseUIControl@0 property. Within it there is a
_targetActions array that contains the button’s attached
target-action pairs.
2. select the _target property within first item ([0])
3. Press the spacebar while _target is selected, and a Quick Look
window will open, showing a preview of the variable (which is an
instance of ViewController)

4. find the _action. Next to it, you will see (SEL)


"buttonTapped:". The (SEL) indicates that this is a selector,
and "buttonTapped:" is the name of the selector
iii. Debug Bar

1. • Continue program execution ( ) – resumes normal execution of


the program
2. • Step over ( ) – executes a single line of code without entering
any function or method call
3. • Step into ( ) – executes the next line of code, including entering a
function or method call
4. • Step out ( ) – continues execution until the current function
or method is exited
iv. Breakpoint Actions
1. Occasionally, you want to be notified when a line of code is
triggered, but you do not need any additional information or for the
application to pause when it hits that line. To accomplish this, you
can add a sound to a breakpoint and have it automatically
continue execution after being triggered
a. Add Sound & continue without stopping for breakpoint
b. Sound and Logging Message

2. Exception Breakpoint
a. In real- world development you will often have no idea
where in your application a bug is hiding. With exception
breakpoints you can tell exactly which line of code causing
the uncaught exception. It would be nice if you could tell
which line of code is causing an uncaught exception
resulting in a crash.
b. Can create bp on throw or on catch. Here's on throw:

c. This strategy is the one to begin with as you tackle a new


bug. In fact, many programmers always keep an exception
breakpoint active while developing
3. Symbolic Breakpoint
a. Breakpoints specified not by line number, but by the name
of a function or method (referred to as a symbol).
Symbolic breakpoints are triggered when the symbol is
called – whether the symbol is in your code or in a
framework for which you have no code
b. In a real-world app, it is rare that you would use a
symbolic breakpoint on a method that you created; you
would likely add a normal breakpoint like the ones you saw
earlier in this chapter. Symbolic breakpoints are most
useful to stop on a method that you did not write, such
as a method in one of Apple’s frameworks. For
example, you might want to know whenever the method
loadView() is triggered for any view controller within the
application
d. LLDB Console
i. Commands
1. print-object, abbrev. po
2. step, abbrev. s: advance 1 line of code
3. enter: repeats previous command
4. print, abbrev. p: more verbose version of po
a. Ex: print on empty array
i. po array ->
(lldb) po array
0 elements
ii. p array ->
(lldb) p array
(NSMutableArray) $R2 = 0x0000600002c48c30 0 elements {}
5. expression, abbrev. expr, or abbrev. e: allows you to enter Swift
code to modify variables
6. continue, abbrev. c: continue until next breakpoint
ii. NOTE: you can also change the UI with LLDB expressions

UITableView & UITableViewController


1. MVC Design Pattern: Model-View-Controller
a. model: holds data & knows nothing about UI
b. view: visible to user and knows nothing about the model objects
c. controller: keeps the model and view objects in sync and controls flow of
application
d. UITableView is View Object
e. UITableViewController is Controller Object
2. What does a UITableView need to function?
a. View Controller: to handle its appearance on screen
b. Data Source: to relate number of rows, data to show in those rows, and other info
i. without data source, it's just an empty container
ii. dataSource for UITableView can be ANY OBJECT as long as it
conforms to UITableViewDataSource protocol
c. Delegate: to inform other objects of events relevant to UITableView
i. delegate for UITableView can be ANY OBJECT as long as it conforms
to UITableViewDelegate
d. An instance of UITableViewController CAN FILL all 3 roles. In MVC pattern,
all these roles are filled by controller objects
3. UITableViewController is actually subclass of UIViewController
a. it has a view property, which is an instance of UITableView
4. When a UITableViewController creates its view object, the dataSource and
delegate properties are automatically set to point to UITableViewController

5. Custom Initializers
a. Without default values XCode will report an error that Item has no initializers.
Therefore, need to give values to these properties in a designated initializer

b. Compared to structs (who DO NOT support inheritance), classes support 2 types


of initializers
i. Designated Initializers: primary initializer for the class. Every class has
at least one designated initializer. A designated initializer ensures that all
properties in the class have a value. Once it ensures that, a designated
initializer calls a designated initializer on its superclass (if it has a
superclass from which it inherits)
1. Once you've created a custom initializer, you CANNOT use the
"free" init() which is useful when ALL properties have default
values.

ii. Convenience Initializers: Every class MUST HAVE at least 1 designated


initializer BUT convenience initializers ARE OPTIONAL. It's a helper
initializer and it will always call another initializer on the same class
1. use convenience keyword prior to initializer
2. convenience inits MUST CALL another initializer defined on the
same type, which eventually MUST CALL a designated init
defined in the same class. One convenience init CAN CALL
another convenience init
3. But a designated initializer must call a designated initializer on the
superclass (if there is a superclass)
6. UITableView's Data Source
a. The process of providing rows to a UITableView in Cocoa Touch (the
collection of frameworks used to build iOS apps) is different from the typical
procedural programming task. In a procedural design, you tell the table view what
it should display. In Cocoa Touch, the table view asks another object – its
dataSource – what it should display. In this case, ItemsViewController is
the data source, so it needs a way to store item data
b. We are going to use an array to store the Item instances, but with a twist. The
array that holds the Item instances will be abstracted into another object – an
ItemStore

c. If an object wants to see all the items, it will ask the ItemStore for the array that
contains them
d. Example of Data Source
import UIKit
class ItemStore {
var allItems = [Item]()
@discardableResult func createItem() -> Item{
var randomItem = Item(random: true)
allItems.append(randomItem)
return randomItem
}
}
i. @discardableResult attribute: used to suppress the compiler warning
when a function's result is not used. When applied to a function, it
indicates that the return value of the function can be intentionally ignored,
and the compiler should not generate a warning if the return value is not
assigned or used.
1. // This is OK
let newItem = itemStore.createItem()
// This is also OK; the result is not assigned to a variable
itemStore.createItem()
e. Implementing Data Source Methods
i. When a UITableView wants to know what to display, it calls methods
from the set of methods declared in the UITableViewDataSource
protocol
ii. Whenever a UITableView needs to display itself, it calls a series of
methods (the required methods plus any optional ones that have been
implemented) on its dataSource
iii. Required Methods
1. tableView(_:numberOfRowsInSection:) - # rows to display
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return itemStore.allItems.count
}
2. tableView(_:cellForRowAt:) - what to display in each row
iv. TableView Sections
1. Table views can be broken up into sections, with each section
having its own set of rows. For example, in the address book, all
names beginning with “C” are grouped together in a section. By
default, a table view has one section
v.
7. UITableViewCell
a. Each row of a table view is a view. These views are instances of
UITableViewCell
b. A cell itself has one subview – its contentView. The contentView is the
superview for the content of the cell. The cell may also have an accessory view

c. accessory view shows an action-oriented icon, such as a checkmark, a


disclosure icon, or an information button. These icons are accessed through
predefined constants for the appearance of the accessory view. The default is
UITableViewCellAccessoryType.none
d. contentView, which has three subviews of its own. Two of those subviews are
UILabel instances that are properties of UITableViewCell named textLabel
and detailTextLabel. The third subview is a UIImageView called imageView
e. UITableViewCellStyle : determines which subviews are used and their
position within the contentView

f. Required Method tableView(_:cellForRowAt:) returns the configured cell


i. Parameters
1. indexPath: has section and row properties to indicate exactly
which cell we are setting
g. Reusing UITableViewCells: when scrolling to view more cells than the screen can
hold better to conserve memory by reusing memory of previously created cells
that are off screen rather than take up more memory. When using
tableView(_:cellForRowAt:) to display cells you are actually creating a new
cell each time and taking up more and more memory. When the user scrolls the
table, some cells move offscreen and are put into a pool of cells available for
reuse. Then, instead of creating a brand new cell for every request, the data
source first checks the pool. If there is an unused cell, the data source configures
it with new data and returns it to the table view

i. NOTE: If you subclass UITableViewCell to create a special look or


behavior, then your UITableView will have different types of cells. Different
subclasses floating around in the pool of reusable cells create the
possibility of getting back a cell of the wrong type. You must be sure of
the type of the cell returned so that you can be sure of what properties
and methods it has.
1. You need a cell of a specific type. Every cell has a
reuseIdentifier property of type String. When a data source
asks the table view for a reusable cell, it passes a string and says,
“I need a cell with this reuse identifier.” By convention, the reuse
identifier is typically the name of the cell class
2. To reuse cells, you need to register either a prototype cell or a
class with the table view for a specific reuse identifier
3. Here using String "UITableViewCell" as the identifier, placed in
the attribute "Identifier" fo the prototype cell
8. UITableView Editing Mode
a. has an editing property, and when this property is set to true, the UITableView
enters editing mode: change order, add, remove rows
i. does not allow the user to edit the content of a row
b. You could toggle the editing property of UITableView directly. However,
UIViewController also has an editing property. A UITableViewController
instance automatically sets the editing property of its table view to match its own
editing property. By setting the editing property on the view controller itself, you
can ensure that other aspects of the interface also enter and leave editing mode.
c. Adding Rows: Two Interfaces
i. A button above the cells of the table view: usually for adding a record
for which there is a detail view. For example, in the Contacts app, you tap
a button when you meet a new person and want to take down their
information.
ii. A cell with a green : usually for adding a new field to a record, such as
when you want to add a birthday to a person’s record in the Contacts app.
In editing mode, you tap the green next to add birthday.
d. Deleting Rows
i. Before the table view will delete a row, it calls a method on its data source
about the proposed deletion and waits for confirmation
ii. Do 2 Things to Delete:
1. Remove the row from the UITableView and remove the Item
associated with it from the ItemStore. To pull this off, the
ItemStore must know how to remove objects from itself
2. Implement removeItem function in ItemStore
func removeItem(_ item: Item){
if let index = allItems.firstIndex(of: item){
allItems.remove(at: index)
}
}
3. Implement tableView(_:commit:forRowAt:) from the
UITableViewDataSource protocol
override func tableView(_ tableView: UITableView, commit editingStyle:
UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let item = itemStore.allItems[indexPath.row]
itemStore.removeItem(item)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
e. Moving Rows
i. Unlike deletion, moving a row does not require confirmation: The table
view moves the row on its own authority and reports the move to its data
source by calling the method tableView(_:moveRowAt:to:)
ii. Do 2 things:
1. Create function to move Item inside the ItemStore and
implement tableView(_:moveRowAt:to:) on the UITableView
func moveItem(from fromIndex: Int, to toIndex: Int){
if fromIndex == toIndex {
return
}
let movedItem = allItems[fromIndex]
allItems.remove(at: fromIndex)
allItems.insert(movedItem, at: toIndex)
}
2. Implement tableView(_:moveRowAt:to:)
a. Note that simply implementing
tableView(_:moveRowAt:to:) caused the reordering
controls to appear. The UITableView asks its data
source at runtime whether it implements
tableView(_:moveRowAt:to:). If it does, then the table
view adds the reordering controls whenever the table view
enters editing mode
9. Dependency Inversion Principle
a. The essential goal of this principle is to decouple objects in an application by
inverting certain dependencies between them. This results in more robust and
maintainable code.
b. Principle Concepts:
i. 1. High-level objects should not depend on low-level objects. Both should
depend on abstractions
ii. 2. Abstractions should not depend on details. Details should depend on
abstractions
c. The abstraction required by the dependency inversion principle in LootLogger is
the concept of a “store.” A store is a lower-level object that retrieves and saves
Item instances through details that are only known to that class.
d. Dependency Injection
i. A common pattern used when implementing the dependency inversion
principle is dependency injection. In its simplest form, dependency
injection means that higher-level objects do not assume which
lower-level objects they need to use. Instead, those are passed to
them through an initializer or property. In your implementation of
ItemsViewController, you used injection through a property to give it a
store
10. Design Patterns
a. Delegation: One object delegates certain responsibilities to another object. You
used delegation with the UITextField to be informed when the contents of the text
field change
b. Data source: A data source is similar to a delegate, but instead of reacting to
another object, a data source is responsible for providing data to another object
when requested. You used the data source pattern with table views: Each table
view has a data source that is responsible for, at a minimum, telling the table
view how many rows to display and which cell it should display at each index
path
c. Model-View-Controller: Each object in your applications fulfills one of three
roles. Model objects are the data. Views display the UI. Controllers provide the
glue that ties the models and views together
d. Target-action pairs: One object calls a method on another object when a
specific event occurs. The target is the object that has a method called on it, and
the action is the method being called. For example, you used target-action pairs
with buttons: When a touch event occurs, a method will be called on another
object (often a view controller)

Stack Views
1. A stack view is an instance of UIStackView that allows you to create a vertical or
horizontal layout that is easy to lay out and manages most of the constraints that you
would typically have to manage yourself. Perhaps best of all, you are able to nest stack
views within other stack views, which allows you to create truly amazing interfaces in a
fraction of the time.
2. Stack view DOES NOT have a separate view controller. Instead, MUST insert stack view
into existing view controller and setup constraints as needed.
3. Vertical stack view layout
4. Implicit Constraints
a. Every view has intrinsic content size
b. If you do not define constraints that explicitly determine width and height, then
view will derive width and height from its intrinsic content size
i. HOW: using implicit constraints derived from the developer defined
content hugging and compression resistance priorities. Each view has
one of each for its vertical and horizontal components
5. Content Hugging/Compression Priorities
a. Content hugging: priority is like a rubber band that is placed around a view. The
rubber band makes the view not want to be bigger than its intrinsic content size in
that dimension. The view with the higher content hugging priority is the one that
does not stretch. You can think about the priority value as the “strength” of the
rubber band. The higher the priority value, the stronger the rubber band and the
more it wants to hug to its intrinsic content size.
b. Priority range: 1 - 1000
c. 1000 priority: req'd & view can't get bigger than intrinsic size on that dimension
d. < 1000 priority: not required but will occur based on how its priority compares to
that of a conflicting constraint's priority
e. Content Compression Resistance: priority determines how much a view resists
getting smaller than its intrinsic content size. The view with the greater content
compression resistance priority is the one that will resist compression and,
therefore, not truncate its text
6. Nested Stack Views
a. Add labels inside existing vertical stack view, then embed some labels into
another stack view and turn those embedded stack views into horizontal
b. Embed by selecting label then clicking on Embed In of the Auto Layout
constraints menu

7. Segues
a. A segue moves another view controller’s view onto the screen and is represented
by an instance of UIStoryboardSegue. Each segue has a style, an action
item, and an identifier.
b. style: how the new view controller will be presented
c. action: view object in the storyboard file that triggers the segue, like a button, a
table view cell, or some other UIControl
d. identifier: used to programmatically access the segue. Useful when you want
to trigger a segue that does not come from an action item, like a shake or some
other interface element that cannot be set up in the storyboard file
e. Show Segue: displays a view controller in whatever manner works best for the
circumstance (usually by presenting it modally)
i.
ii.

Navigation Controllers
1. Drill-down interface: the Settings application is example which has multiple related
screens of information: a list of settings (including some for apps, like Safari), a detail
page for each setting, and a selection page for each detail

2. UINavigationController
a. maintains an array of view controllers presenting related information in a stack.
When a UIViewController is on top of the stack, its view is visible
b. When you initialize an instance of UINavigationController, you give it a
UIViewController. This UIViewController is added to the navigation
controller’s viewControllers array and becomes the navigation controller’s root
view controller. The root view controller is always on the bottom of the stack
i. NOTE: that while this view controller is referred to as the navigation
controller’s “root view controller,” UINavigationController does not
have a rootViewController property
ii.
c. More view controllers can be pushed on top of the UINavigationController's
stack while the application is running. These view controllers are added to the
end of the viewControllers array that corresponds to the top of the stack
d. topViewController property keeps a reference to the view controller at the top
of the stac​​k
e. When a view controller is pushed onto the stack, its view slides onscreen from
the right. When the stack is popped (i.e., the last item is removed), the top view
controller is removed from the stack and its view slides off to the right, exposing
the view of the next view controller on the stack, which becomes the top view
controller
f. UINavigationController stack

g. UINavigationController subclass of UIViewController. Structured such:


i. view - the actual topViewController
1. subViews:
a. UINavigationBar - navigation bar view itself
b. topViewController - reference to top of stack
2.
h. Requirements to Implement
i. Give it a root view controller
ii. Add its view to the window
i. Steps to Implement
i. Embed ViewController in NavigationController - requirement #1
1. Automatically updates storyboard to set the Navigation Controller
as the initial view controller
ii. Set NavigationController as the rootViewController inside the
SceneDelegate - requirement #2

j. Navigating w/ Navigation Controller


i. When a show segue is triggered from a view controller embedded within a
navigation controller, the destination view controller is pushed onto
the navigation controller’s view controller stack
ii. Because the UINavigationController’s stack is an array, it will take
ownership of any view controller added to it
1. Thus, the DetailViewController is owned only by the
UINavigationController after the segue finishes. When the
stack is popped, the DetailViewController is destroyed. The
next time a row is tapped, a new instance of
DetailViewController is created
iii. Typical Behavior: Having a view controller push the next view controller
is a common pattern. The root view controller typically creates the next
view controller, and the next view controller creates the one after that, and
so on
k. Appearing & Disappearing Views
i. Calls two methods when ready to swap views
1. viewWillDisappear(_:) - called on current VC prior to
disappearing, then pops that VC
a. save any changes prior to disappear in order to hold on to
changes
2. viewWillAppear(_:) - called on destination VC prior to
appearing, then appends that VC to top of stack
a. Call tableView.reloadData() when need to get updated
tableView to load
3. It is important to call the superclass’s implementation – it might
have some work to do and needs to be given the chance to do it
l. Event Handling
i. First Responder
1. Many views and controls can be a first responder within your view
hierarchy – but only one at a time. Think of it as a flag that can be
passed among views. Whichever view holds the flag will receive
the shake or keyboard event
2. Instances of UITextField and UITextView have an uncommon
response to touch events. When touched, a text field or a text
view becomes the first responder, which in turn triggers the
system to put the keyboard on screen and send the keyboard
events to that text field or view. The keyboard and the text field or
view have no direct connection, but they work together through the
first responder status
3. Read Apple’s Using Responders and the Responder Chain to Handle
Events and Responder Chain You Should Understand in iOS
ii. Dismiss Keyboard with Return Key
1. resignFirstResponder(): to resign the first responder status
and give up control
2. UITextFieldDelegate: delegate protocol for UITextField
a. textFieldShouldReturn(_:): method called whenever
Return key pressed
iii. Dismiss Keyboard by Tapping Elsewhere
1. Gesture Recognizer:

2. Connect UITapGestureRecognized action to


DetailViewController

3. Dismiss keyboard by implementing endEditing on the view

4. @IBAction func backgroundTapped(_ sender:


UITapGestureRecognizer) {
view.endEditing(true)
}
a. Calling endEditing(_:) is a convenient way to dismiss
the keyboard without having to know (or care) which text
field is the first responder. When the view gets this call, it
checks whether any text field in its hierarchy is the first
responder. If so, then resignFirstResponder() is called
on that particular view.
3. UINavigationBar
a. navigationItem : every UIViewController has property of type
UINavigationItem which is responsible for supplying the navigation bar with the
content that it needs to draw
i. UINavigationItem IS NOT Subclass of UIView
ii. When a UIViewController comes to the top of a
UINavigationController’s stack, the UINavigationBar uses the
UIViewController’s navigationItem to configure itself

iii. By default, a UINavigationItem is empty. At the most basic level, a


UINavigationItem has a simple title string. When a UIViewController
is moved to the top of the navigation stack and its navigationItem has a
valid string for its title property, the navigation bar will display that string

iv. 3 Customizable Properties of navigationItem


1. leftBarButtonItem, rightBarButtonItem, and titleView
a. left and right buttons are references to instance of
UIBarButtonItem, which contain info for button that CAN
ONLY be displayed on UINavigationBar or a UIToolbar

b. titleView : You can either use a basic string as the title


or have a subclass of UIView sit in the center of the
navigation item
2. A bar button item has a target-action pair that works like
UIControl’s target-action mechanism: When tapped, it sends the
action message to the target.
3. View controllers expose a bar button item that will automatically
toggle their editing mode. There is no way to access this through
Interface Builder, so you will need to add this bar button item
programmatically
a. required init?(coder aCoder: NSCoder) {
super.init(coder: aCoder)
navigationItem.leftBarButtonItem = editButtonItem
}
b. To modify the navigation item's navigationItem.backBarButtonItem, need to
modify on the previous VC b/c the back button belongs to the previous VC

Saving, Loading, & Scene States

1. Codable
a. Codable types conform to the and Encodable
Decodable protocols and implement their required methods, which are
encode(to:) and init(from:)
b. protocol Encodable {
func encode(to encoder: Encoder) throws
}
protocol Decodable {
init(from decoder: Decoder) throws
}
c. Apple has a protocol composition type for types that conform to both protocols
called Codable
typealias Codable = Decodable & Encodable
d. NOTE: Any Codable type whose properties are all Codable automatically
conforms to the protocol itself
e. Once a type confirmed that IT CAN BE ENCODED, then you need an encoder.
Two Built-In Encoders:
i. PropertyListEncoder: saves data out in a property list format
ii. JSONEncoder: saves data out in a JSON format
2. Property List (plist)
a. Representation of data that can be saved to disk and read back in at a later point.
Property lists can represent hierarchies of data and so are a great tool for
saving and loading lightweight object graphs
b. Under the hood most frequently represented using an XML or binary format
i. XML:

c. plist can hold these data types: Array, Bool, Data, Date, Dictionary,
Float, Int, and String
i. as long as type is composed of these types or hierarchies of these types
than it can be represented in a plist
3. Error Handling
a. Graceful way of handling/catching errors such that you can continue application
flow based on failure type
i. Optionals are another ways of handling errors BUT with optionals we DO
NOT CARE for the reason a failure occurred - the optional simply sets
variable to nil at failure
b. If a method can generate an error, its method signature needs to indicate this
using the throws keyword, which indicates that this function can throw an error.
With throws keyword, compiler ensures that anyone who uses this method
knows that it can throw an error – and, more importantly, that the caller handles
any potential errors
func encode(_ value: Encodable) throws -> Data
c. do-catch: statement that wraps any portion of the code that can potentially
throw an error
i. within the do block, you annotate any methods that might throw an error
using the try keyword to reinforce the idea that the call might fail
ii. func saveChanges() -> Bool{
do {
let encoder = PropertyListEncoder()
let allItemsData = try encoder.encode(allItems)
} catch {
print("Error encoding allItems: \(error)")
}
return true
}
iii. Within the catch block, there is an implicit error constant that contains
information describing the error. You can optionally give this constant an
explicit name s// encodingError
4. Application Sandbox
a. Directory on the filesystem that is barricaded from the rest of the filesystem. Your
application must stay in its sandbox, and no other application can access its
sandbox

b. Documents: can write data here that the application generates during runtime
and that you want to persist between runs of the app. It is backed up when the
device is synchronized with iCloud or Finder. If something goes wrong with the
device, files in this directory can be restored from iCloud or Finder
c. Library/Caches: also can write data that's generated at runtime and persists, but
it does not get backed up when the device is synchronized with iCloud or Finder.
Primary reason for not backing up cached data is that the data can be very
large and extend the time it takes to synchronize your device.
i. Data already stored somewhere else – like a web server – can be placed
in this directory. Used to quickly access large set of data at runtime
d. Library/Preferences: where any preferences are stored and where the Settings
application looks for application preferences. This directory is handled
automatically by the class UserDefaults and is backed up when the device is
synchronized with iCloud or Finder
e. tmp: here write data that you will use temporarily during an application’s
runtime. OS may purge files in this directory when your application is not running.
However, to be tidy you should explicitly remove files from this directory when
you no longer need them. Does Not get backed up
5. Construct File URL
i. Use closure to construct directory file url to save persisting application data
ii. Notice that the closure here has a signature of () -> URL, meaning it does not
take in any arguments and it returns an instance of URL.
let itemArchiveURL: URL = {
let documentsDirectories = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = documentsDirectories.first!
return documentDirectory.appendingPathComponent("items.plist")
}()
1. In iOS, the last argument in urls(for:in:) is always the same
because this method is borrowed from macOS, where there are
significantly more options
2. The first argument is a SearchPathDirectory enumeration that
specifies the directory in the sandbox you want the URL for
3. return value of is an array of URLs because in macOS there may
be multiple URLs that meet the search criteria
a. in iOS there will only be one
4.
iii. When the ItemStore class is instantiated, this closure will be run and
the return value will be assigned to the itemArchiveURL property.
Using a closure like this allows you to set the value for a variable or
constant that requires multiple lines of code, which can be very useful
when configuring objects
6. Scene States and Transitions
a. Scene is a single instance of your application’s UI
b. Scenes can be created and destroyed as a user opens and closes windows, so
you should think about the lifecycle of a scene in addition to the lifecycle of the
application as a whole
c. States of Scene

i. State transitions of typical scene

d. Unattached State: does not execute any code or have any memory reserved in
RAM
e. Foreground Inactive State: after scene launch it briefly enters before entering
Foreground Active
f. Foreground Active State: a scene’s interface is on the screen, it is accepting
events, and its code is handling those events
i. a scene can be temporarily interrupted by a system event, like a phone
call, or interrupted by a user event, like triggering Siri or opening the task
switcher
ii. At this point, the scene reenters the foreground inactive state. In the
inactive state, a scene is usually visible and is executing code, but it is not
receiving events. Scenes typically spend very little time in the inactive
state
g. Background State: the scene enters when the user returns to the Home screen
or switches to another application. (Actually, it spends a brief moment in the
foreground inactive state before transitioning to the background state.) In the
background state, a scene’s interface is not visible or receiving events, but it can
still execute code
i. By default, a scene that enters the background state has about 10
seconds before it enters the suspended state. But your scenes should
not rely on having this much time; instead, they should save user data
and release any shared resources as quickly as possible
h. Suspended State: cannot execute code. You cannot see its interface, and any
resources it does not need while suspended are destroyed. A suspended scene
is essentially flash-frozen and can be quickly thawed when the user relaunches it
i. will remain in suspended state as long as there is adequate system
memory. When the OS decides memory is getting low, it will terminate
suspended scenes as needed, moving them to the unattached state
ii. NOTE: suspended scene gets no indication that it is about to be
terminated. It is simply removed from memory
i. Saving Changes: Transitioning to the background state is a good place to
save any outstanding changes, because it is the last time your scene can
execute code before it enters the suspended state. Once in the suspended state,
a scene can be terminated at the whim of the OS
7. Persisting Items
g. someDataInstance.write(to:options:) - write to file system
h. func saveChanges() -> Bool{
print("Saving allItems to: \(itemArchiveURL)")
do {
let encoder = PropertyListEncoder()
let allItemsData = try encoder.encode(allItems)
try allItemsData.write(to: itemArchiveURL, options: .atomic)
print("Saved allItems to data file")
return true
} catch let encodingError{
print("Error encoding allItems: \(encodingError)")
return false
}
}
i. .atomic writing option: ensures no data corruption by first saving to temp
file. If writing successful it renames the file
1. there will usually be an existing items.plist file that will be
replaced during a save. If there is a problem during the write
operation, the original file will not be affected
8. Notification Center
a. An object can post notifications about what it is doing to a centralized notification
center. Interested objects register to receive a callback when a particular
notification is posted or when a particular object posts
b. Every application has an instance of NotificationCenter, which works like a
smart bulletin board
c.
d. NotificationCenter posts notification to all observers of specific notifications
and/or objects. Array of observers
e. When you register as an observer, you can specify a notification name, a
posting object, and the method that should be called when a qualifying
notification is posted

f.
i. NotificationCenter() vs NotificationCenter.default
1. NotificationCenter(): new instance of NotificationCenter
using its default initializer. This means you are creating your own
instance of NotificationCenter, and it is not associated with the
default notification center for the app. You would typically use this
approach if you need a separate, isolated notification center for a
specific purpose within your code
2. NotificationCenter.default: retrieves the default notification
center instance for the app. The default property is a shared
instance that represents the default notification center for the app.
Using this instance allows you to post and observe notifications
that are broadcasted throughout the entire app
g. nil is wildcard in the notification center world. You can pass nil as the name
argument, which will give you every notification regardless of its name. If you
pass nil for the notification name and the posting object, you will get every
notification. While passing nil for the name is uncommon, it is fairly common to
pass in nil for the object
h. Callback method that is triggered must be in objective-c

i. notification may have a userInfo object attached to it for additional info

j. Important to understand that Notifications and the NotificationCenter


are not associated with visual “notifications,” like push and local notifications that
the user sees when an alarm goes off or a text message is received.
Notifications and the NotificationCenter comprise a design pattern, like
target-action pairs or delegation
9. Saving Items
a. Scene states have associated notifications that are sent as a scene transitions
either in or out of them
b. Some examples:
i. UIScene.willConnectNotification
UIScene.didDisconnectNotification
UIScene.willEnterForegroundNotification
UIScene.didActivateNotification
UIScene.willDeactivateNotification
UIScene.didEnterBackgroundNotification
ii. There are also corresponding delegate callbacks for most of those
notifications in the SceneDelegate class

Presenting View Controllers

1. UIToolBar
a. Works a lot like a UINavigationBar – you can add instances of
UIBarButtonItem to it. However, where a navigation bar has two slots for bar
button items, a toolbar has an array of bar button items. You can place as many
bar button items in a toolbar as can fit on the screen
2. Alert Controllers
a. often used to display information the user must act on. When you want to
display alert, you create an instance of UIAlertController with a preferred
style. The two available styles are UIAlertControllerStyle.actionSheet and
UIAlertControllerStyle.alert

b. .actionSheet: style is used to present the user with a list of actions to choose
from.
i. if the user can back out of a decision or if the action is not critical, then an
.actionSheet is probably the best choice
c. .alert: type is used to display critical information and requires the user to
decide how to proceed
d. UIAlertAction
i. add to Alert Controllers to display actions for the user
@IBAction func choosePhotoSource(_ sender: UIBarButtonItem) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default, handler: {_ in
print("Present Camera")
})
let photoLibraryAction = UIAlertAction(title: "Photo Library", style: .default, handler: {_ in
print("Present Photo Library")
})
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {_ in
print("Cancel Camera")
})
alertController.addAction(cameraAction)
alertController.addAction(photoLibraryAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
ii. Action styles influence the action, presentation and location
1. .default
2. .destructive use red font colors to emphasize the destructive
nature of the action
3. .cancel shows up at the bottom of the list
e. Present alert with actions
i. present(_:animated:completion) method takes in a view controller to
present, a Bool indicating whether that presentation should be animated,
and an optional closure to call once the presentation is completed.
Generally, you will want the presentation to be animated, as this provides
context to the user about what is happening
3. Presentation Styles for VCs
a. When you present a standard view controller, it slides up to cover the window -
this is usually default behavior
b. .automatic: Presents the view controller using a style chosen by the system.
Typically this results in a .formSheet presentation. This is the default presentation
style.
c. .formSheet: Presents the view controller centered on top of the existing content.
d. .fullScreen: Presents the view controller over the entire application.
e. .overFullScreen: Similar to .fullScreen except the view underneath the
presented view controller stays visible. Use this style if the presented view
controller has transparency, so that the user can see the view controller
underneath.
f. .popover: Presents the view controller in a popover view on iPad. (On iPhone,
using this style falls back to a form sheet presentation style due to space
constraints)
i. NOTE: Apple’s documentation for UIImagePickerController mentions
that the camera should be presented full screen. And photo library and
saved photos album must be presented in a popover

ii. NOTE: When you set preferredStyle: .actionSheet, iOS


automatically uses a popover presentation style on iPad, and the
UIAlertController is presented as a popover, and therefore
REQUIRED to choose location to place the popover with setting as
example the location to be the sender
alertController.popoverPresentationController?.barButtonIte
m = sender
iii. Present vs Push VC

1.

2.
3. In summary, presenting a view controller is often used for showing
temporary or transient views modally, while pushing a view
controller is used for hierarchical navigation within a navigation
controller. The choice between the two depends on the app's
navigation flow and the type of content being displayed.
iv.
Camera
1. UIImageView : presents image according to the image's contentMode, property which
determines where to position and how to resize the content within the image view’s
frame. For image views, you will usually want either aspect fit (if you want to see the
whole image) or aspect fill (if you want the image to fill the image view)

2. \
3. Taking Pictures and UIImagePickerController
a. In the choosePhotoSource(_:) method, you will instantiate a
UIImagePickerController and present it on the screen. When creating an
instance of UIImagePickerController, you must set its sourceType property
and assign it a delegate. Because there is set-up work needed for the image
picker controller, you need to create and present it programmatically instead
of through the storyboard
4. Creating a UIImagePickerController
a. Create Image Picker
func imagePicker (for sourceType: UIImagePickerController.SourceType) ->
UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = sourceType
return imagePicker
}
b. sourceType is a UIImagePickerController.SourceType enumeration value
and tells the image picker where to get images. It has three possible values:
i. .camera: Allows the user to take a new photo
ii. .photoLibrary: Prompts the user to select an album and then a photo
from that album
iii. .savedPhotosAlbum: Prompts the user to choose from the most recently
taken photos
5. Setting Image Picker’s Delegate
a. UIImagePickerController instance needs a delegate. When the user selects an image
from the UIImagePickerController's interface, the delegate is sent the message
imagePickerController(_:didFinishPickingMediaWithInfo:). (If the user taps
the cancel button, then the delegate receives the message
imagePickerControllerDidCancel(_:).). The image picker's delegate will be the
instance of DetailViewController. At the top of DetailViewController.swift,
declare that DetailViewController conforms to the
UINavigationControllerDelegate and the UIImagePickerControllerDelegate
protocols

b.
c. Why UINavigationControllerDelegate? UIImagePickerController's delegate
property is actually inherited from its superclass, UINavigationController, and while
UIImagePickerController has its own delegate protocol, its inherited delegate property
is declared to reference an object that conforms to UINavigationControllerDelegate.
Next, set the instance of DetailViewController to be the image picker's delegate in
imagePicker(for:)
d. UINavigationControllerDelegate: This protocol is being adopted because the
UIImagePickerController class inherits from UINavigationController. Although
UIImagePickerController has its own delegate protocol
(UIImagePickerControllerDelegate), its delegate property is declared to reference
an object that conforms to UINavigationControllerDelegate. Therefore, when you
use an instance of UIImagePickerController, it expects its delegate to also conform to
the UINavigationControllerDelegate protocol. This allows the
UIImagePickerController to work seamlessly with navigation controllers.
6. Permissions
a. There are a number of capabilities on iOS that require user approval before use.
The camera is one. Some of the others are:
i. Photos, microphone, calendar, location, HealthKit data, reminders
ii. ​For these, your application must supply a usage description that specifies
the reason that your application wants to access the capability or
information. This description will be presented to the user when the
application attempts the access.
b. If you have an actual iOS device to run on, you will notice a problem if you try to
use the camera. When you select an Item, tap the camera button, and choose
Camera, the application crashes.
i. Take a look at the description of the crash in the console:
1. LootLogger[3575:64615] [access] This app has crashed because
it attempted to access privacy-sensitive data without a usage
description. The app's Info.plist must contain an
NSCameraUsageDescription key with a string value explaining to
the user how the app uses this data.
2. When attempting to access potentially private information, such as
the camera, iOS prompts the user to consent to that access.
Contained within this prompt is a description of why the application
wants to access the information. LootLogger is missing this
description, and therefore the application is crashing.
7. Each time an image is captured, it will be added to the store. Notice that the images are
saved immediately after being taken, while the instances of Item are saved only when
the application enters the background. You save the images right away because they are
too big to keep in memory for long. Both the ImageStore and the Item will know the key
for the image, so both will be able to access it

8.

Swift
10. Swift Types: structures, classes, and enumerations
a. more powerful: can also conform to protocols and can be extended
b. Typically “primitive” types such as numbers and Boolean values may surprise
you: They are all structures
i. Numbers: Int, Float, Double
ii. Boolean: Bool
iii. Text: String, Character
iv. Collections: Array<Element>, Dictionary<Key:Hashable,Value>,
Set<Element:Hashable>
v. THEREFORE: This means that standard types have properties,
initializers, and methods of their own. They can also conform to protocols
and be extended.
c.
11. Properties
a. values associated with a type
i. Ex: String has the property isEmpty, which is a Bool that tells you
whether the string is empty
1. Array<T> has the property count, which is the number of
elements in the array as an Int.
b. To create a property, you need to declare it outside of methods
i.
12. initializers: code that initializes an instance of a type
13. instance methods: functions specific to a type that can be called on an instance of that
type
14. class or static methods: functions specific to a type that can be called on the type itself
15. Optionals
a. allows you to store either a value of a particular type or no value at all
b. lets you express the possibility that a variable may not store a value at all. The
value of an optional will either be an instance of the specified type or nil
c. Unwrapping
i. However, you cannot use these optional floats like non-optional
floats – even if they have been assigned Float values. Before you can
read the value of an optional variable, you must address the possibility
of its value being nil.
ii. Forcibly Unwrapping
1. says that you guarantee that the value is not nil
var varOne: Float?
var varTwo: Float?
varOne = 23.4
varTwo = 99
var final = varOne! / varTwo!
2. Error when mistaken: Fatal error: Unexpectedly found
nil while unwrapping an Optional value
3. If you forcibly unwrap an optional and that optional turns out to be
nil, it will cause a trap, stopping your application.
iii. Optional Binding
1. works within a conditional if-let statement: You assign the optional
to a temporary constant of the corresponding non-optional type. If
your optional has a value, then the assignment is valid and you
proceed using the non-optional constant. If the optional is nil, then
you can handle that case with an else clause.
if let r1 = reading1,
let r2 = reading2,
let r3 = reading3 {
let avgReading = (r1 + r2 + r3) / 3
print(avgReading)
} else {
let errorString = "Instrument reported a reading that was nil."
print(errorString)
}
16. Loops & Interpolation
a. Swift does require braces on clauses.
b. Additionally, the expressions for if- and while-like statements must evaluate
to a Bool
i. NOTE: in some languages the expression just cannot be nil, but not
have to be a Bool
c. For-Loop
i. NOT traditional
let range = 0..<countingUp.count
for i in range {
let string = countingUp[i]
// Use 'string'
}
ii. Most direct route
for string in countingUp {
// Use string
}
iii. If want the index of each item in the array
1. enumerated() function returns a sequence of integers and values
from its argument:
for (i, string) in countingUp.enumerated() {
// (0, "one"), (1, "two")
}
2. enumerated() function returns a sequence of tuples. A tuple is an
ordered grouping of values similar to an array, except each
member may have a distinct type. In this example the tuple is of
type (Int, String)
3. NOTE: tuples not used in iOS APIs because Objective-C does
not support them
a. Another application of tuples is in enumerating the
contents of a dictionary
b.
17.
18. Dictionary<String, AnyObject> vs [String:AnyObject] -> identical functionally.
Latter modern syntax
19. AnyObject vs Any
a. AnyObject: protocol that represents instance of any class type
b. Any: type that represents values of any type, including non-class types like
structs and enums. The more general Any is often preferred unless you
specifically need class instances.
20. Type Inference: xcode will infer type of const or var without you declaring IF you provide
value at declaration
21. Specify Type: can specify at declaration to ensure
22. Number & Boolean Types
a. Int: most common integer type
i. others based on word size and signedness
b. Floating Point
i. Float for 32-bit, Double for 64-bit, and Float80 for 80-bit
c. Boolean: true or false
23. Collection Types
a. Arrays
i. ordered collection of elements
ii. Array<T> where T is the type of element that the array will contain
iii. Can contain: standard type, a structure, or a class
iv. Strongly Typed Arrays
1. Once you declare an array as containing elements of, say, Int, you
cannot add a String to it.
2. var arrayOfInts: Array<Int> = []
3. Shorthand syntax for declaring arrays: square brackets around
type
a. var array: [Int] = [4, 53, 66]
v.
b. Dictionaries
i. unordered collection of key-value pairs
ii. vals: can be of any type
iii. keys: can be of any type, but they must be unique. Specifically,
the keys must be hashable, which allows the dictionary to guarantee
that the keys are unique and to access the value for a given key more
efficiently.
1. Swift types such as Int, Float, Character, and String are all
hashable.
iv. Strongly Typed Dictionary
1. can only contain keys and values of the declared type
2. var dictOfCapitals: Dictionary<String,String> = [:]
3. Shorthand:
a. var dictOfCapitals: [String,String] = [:]
c. Sets
i. similar to an array in that it contains a number of elements of a certain
type. However, sets are unordered, and the members must be unique as
well as hashable. The unorderedness of sets makes them faster when
you simply need to determine whether something is a member of a set.
ii. var winningLotteryNumbers: Set<Int> = []
iii. NO Shorthand
iv. NSCountedSet : set that also keeps track of the amount of times each
element was added
func challenge5c(input: String, count: String) -> Int {
let array = input.map { String($0) }
let counted = NSCountedSet(array: array)
return counted.count(for: count)
}
challenge5c(input: str1, count: "a") -> 2

d. Collection Methods
i. removeAll(keepingCapacity: true) : used to remove all elements
from the collection while optionally keeping the allocated capacity for
performance reasons.
24. Literals and subscripting
a. literal vs assigned value
i. literal: var myStr = "my string"
ii. assigned: var newStr = myStr
b. Subscripting: shorthand for accessing arrays
i. Must use a valid index. Attempting to access an out-of-bounds index
results in a trap. A trap is a runtime error that stops the program
before it gets into an unknown state.
25. Initializers
a. Initializers are responsible for preparing the contents of a new instance of a type.
When an initializer is finished, the instance is ready for action.
b. Initializers that return empty literals with NO arguments
i. let emptyString = String() -> ""
let emptyArrayOfInts = [Int]() -> []
let emptySetOfFloats = Set<Float>() -> Set([])
c. Initializers that return default values with NO arguments
i. let defaultNumber = Int() 0
let defaultBool = Bool() false
d. Types can have multiple initializers
i. String can be init with an int
let meaningOfLife = String(number) -> "42"
ii. Set initializer that accepts an array literal:
let availableRooms = Set([205, 411, 412]) -> {412, 205, 411}
iii. Float has several initializers. The parameter-less initializer returns an
instance of Float with the default value. There is also an initializer that
accepts a floating-point literal.
let defaultFloat = Float() -> 0
let floatFromLiteral = Float(3.14) -> 3.14
1. NOTE: if use type inference for a floating-point literal, the type
defaults to Double
26. String
a. Common Methods
i. item.hasPrefix("nssl") -> returns Bool
27. Enumerations
a. a type with a discrete set of values
enum PieType {
case apple
case cherry
case pecan
}
let favoritePie = PieType.apple
b. can have raw values
enum PieType: Int {
case apple = 0
case cherry
case pecan
}
c.
d.
28. Switch Case
a. great for matching on enum cases
var name: String
switch favoritePie {
case .apple:
name = "apple"
case .cherry:
name = "cherry"
case .pecan:
name = "pecan"
}
b. The cases for a switch statement must be exhaustive: Each possible value of
the switch expression must be accounted for, whether explicitly or via a default
case. Unlike in C, Swift switch cases do not fall through – only the code for the
case that is matched is executed. (If you need the fall-through behavior of C, you
can explicitly request it using the fallthrough keyword.)
c. Switch statements can match on many types, including ranges
var someNum = 45
switch someNum {
case 1 ... 10:
print("ok")
case 11 ... 20:
print("doing better")
case 21 ... 30:
print("picking up now")
case 31 ... 40:
print("oh baby, c'mon!")
case _ where someNum > 40:
print("the best!")
default:
print("anything else I just can't tell")
}
d. fallthrough: keyword is used in a switch statement to fall through to the next
case. When a fallthrough statement is encountered in a case block, it causes the
control flow to move to the next case block, even if the conditions of that case are
not met
29. Closures
a. Similar to functions but with shortened syntax and can easily be passed around
and remember context
b. @escaping Closure
i. closure that will outlive the current function and will be called after. this
indicates to compiler that the closure will be stored or otherwise outlive
the current function's scope
func getJSON(with urlString: String, completion: @escaping ([GHFollower]?) -> Void){
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
return
}
guard let data = data else {
completion(nil)
return
}
let decoder = JSONDecoder()
// using optional try b/c
guard let json = try? decoder.decode([GHFollower].self, from: data) else {
print("Failed decoding")
completion(nil)
return
}
completion(json)
}.resume()
}
getJSON(with: "https://www.https://api.github.com/users/twostraws/followers") {followers in
guard let followers = followers else { return }
for follower in followers {
print(follower.login)
}
}
1. completion of getJSON is marked as an escaping closure b/c the
data task is an async function call and it needs to complete before
the completion handler is called.
2. This means that it's allowed to capture and store references to
variables outside its own scope, and it might be called after the
getJSON function has already returned.
30. Common Scripts
a. Split string into words
let fullName = "First Last"
let components = fullName.split{ !$0.isLetter }
31. Transformations
a. map vs compactMap: both are higher-order functions used with sequences, such
as arrays; however, compactMap additionally removes nil values from the result
b. It's particularly useful when the transformation can fail, such as converting a
string to an integer
let numbers = ["1", "2", "3", "abc"]
let compactMapped = numbers.compactMap { Int($0) }
// Result: [1, 2, 3]
c. It's preferred when dealing with transformations that might fail or produce
optional values. It helps in filtering out the nil results, leaving you with a cleaner
result
32.

XCode

1. Application Icons
a. Application Icon Sizes by Device

b.
c. x2 and x3 are point sizes in Retina displays
d. Asset Catalog
i.
ii. Add asset to Assets.xcassets
iii. To Change from Single 1024 size to All Sizes:

e. Image Rendering
i. Original Image:
1. In "Original Image" mode, the image is displayed exactly as it
appears in the asset catalog without any modifications.
2. The color and transparency of each pixel in the image are
retained.
3. This mode is suitable for images that are designed to look exactly
as intended, without any color or transparency adjustments.
ii. Template Image:
1. In "Template Image" mode, the image is treated as a template,
and its color is adjusted based on the current tint color.
2. When the image is used as part of a UI element, such as a tab bar
item or navigation bar button, it takes on the color of the element's
tint color.
3. This mode is useful for creating dynamic, customizable user
interfaces where images can adapt to the app's color scheme.
f.
2. Playground
a. View value history

3. Options for adding new files


a. Groups vs Folder Refs
i. In Xcode, when adding files to your project, you have the option to either
"Create groups" or "Create folder references." The choice between these
options determines how Xcode organizes and manages the files in your
project.
​ Create Groups:
● When you choose "Create groups," Xcode organizes the files in logical
groups within the Xcode project, but it doesn't create physical folders on
the disk.
● The files are still stored in a flat directory structure on the file system, and
Xcode creates references to these files in the project.
● Changes made within Xcode, such as renaming a group or moving files
between groups, only affect the organization within the Xcode project, not
the actual file structure on disk.
​ Create Folder References:
● When you choose "Create folder references," Xcode not only organizes
the files within the project but also creates physical folder references on
the disk.
● Changes made within Xcode, such as renaming a folder reference or
moving files between folder references, are reflected in the actual file
structure on disk.
● This option is useful when you want to maintain a specific folder hierarchy
on disk and have those changes reflected in Xcode.
Choosing Between Them:
● If your project structure is straightforward, and you don't need to maintain
a specific folder hierarchy on disk, "Create groups" is often sufficient.
● If your project structure mirrors a folder hierarchy on disk, or you want
changes made in Xcode to also affect the file structure on disk, "Create
folder references" may be more appropriate.
In general, "Create groups" is more commonly used for simplicity, especially in
smaller projects, while "Create folder references" is used when it's important to
maintain a specific folder structure and have Xcode reflect changes on disk.

4. Refactoring in XCode
a. If need to change name of VC for example across entire code base - go to VC
and cntr+click on the file name, variable, function name, or reference name
that you want to update
b. this will bring up an editing mode where you can see all locations where item is
being used and upon updating to new name, click on Rename to update

c. Rename Project Steps


i. Duplicate entire folder
ii. Open project file: .xcodeproj or .xcworkspace
iii. Update bundle ID: project file -> select target -> "General" tab ->
change "Bundle Identifier" to unique identifier
iv. Update App Display Name (Optional):
1. If you want to change the app's display name, update the "Display
Name" under the "Info" tab in the target settings.
v. Update Provisioning Profiles (if applicable):
1. If you are using provisioning profiles, make sure to update them in
the target settings.
vi. Clean & Build
vii. Change location of info.plist file

viii.
5. Info.Plist
a. the app's Info.plist file contains settings that won't change
b. This file is there to describe things like the name going under our icon, any
special URLs we want to handle, or system messages we want to show to users
c.
UIKit w/ Hacking WIth Swift

1. FileManager
a. data type that allows working with files
b. let path = Bundle.main.resourcePath! declares a constant called path that
is set to the resource path of our app's bundle.
i. Bundle is a directory containing our compiled program and all our assets.
So, this line says, "tell me where I can find all those images I added to
my app."
c. let items = try! fm.contentsOfDirectory(atPath: path) declares
constant items that is set to the contents of the directory at a path. Which path?
Well, the one that was returned by the line before. As you can see, Apple's long
method names really does make their code quite self-descriptive! The items
constant will be an array of strings containing filenames.
d. Meaning of try! & try?
i. try used function calls that are capable of throwing an error. when
function contains throws it means that it can propagate errors to the
caller. So try used to invoke such functions in a way that allows the
calling code to handle any potential errors.
ii. try?: "call this code, and if it throws an error just send me back nil
instead."
iii. try!:
e. Loading Contents of File
i.
2. Object Library
a. panel that provides a graphical interface for adding and configuring UI elements
and other objects in your app. It's a part of the Interface Builder, which is a visual
design tool integrated into Xcode for building user interfaces
b. all elements inside the object lib are contained inside UIKit
c.
3. UIKit
a. the iOS user interface framework
b. all Apple elements that begin with UI are part of UIKit. ie:
UITableViewController, UITableView, UIViewController
4. UIViewController
a. Properties
i. title: title = "Storm Viewer"

b.
5. UITableViewController
a. main UI component: based on UIViewController
i. UIViewController: MOST basic type of screen
b. to connect a VC inside storyboard to specific VC in code, need to select the VC
inside identity inspector's custom class

c. “Is Initial View Controller” property: indicates that is the first VC to be shown in
this storyboard
d. Override Methods
i. numberOfRowsInSection
1. returns num of rows in specific section
2. part of UITableViewDataSource
3. called on by table view itself when needs to display a specific
section of the table view
ii. tableView.reloadData
1. forces all table view methods to be called again, including calls to
numberOfRowsInSection and cellForRowAt
e. Dequeuing Cells
i. To save CPU time and RAM, iOS only creates as many rows as it needs
to work. When one rows moves off the top of the screen, iOS will take it
away and put it into a reuse queue ready to be recycled into a new row
that comes in from the bottom. This means you can scroll through
hundreds of rows a second, and iOS can behave lazily and avoid creating
any new table view cells – it just recycles the existing ones
ii. let cell = tableView.dequeueReusableCell(withIdentifier:
"Picture", for: indexPath)
1. Retrieves a reusable cell from the table view's reuse queue. Cells
are reused to improve performance and reduce memory usage.
a. withIdentifier: the identifier you assigned to the cell
prototype in IB. It's a string that uniquely identifies the type
of cell that you want to reuse
b. for: indexPath: index path for the cell you are
configuring
2. when unused cell dequeued and placed into cell it is set with
default settings such as "Title" for text
iii. IndexPath
1. specifies the section and row of the cell in the table view
6. UIImageView
a. UIImage vs UIImageView: UIImage stores images, but does not display them
b. Attributes
i. Aspect Fit: sizes the image so that it's all visible

ii. Aspect Fill: scales the content proportionally to completely fill the bounds
of the view while maintaining its aspect ratio. This means that the
entire view is covered by the content, and some portions of the content
might be clipped if the aspect ratios of the view and content differ
iii. Scale to Fill: sizes the image so that there's no space left blank by
stretching it on both axes

c. Methods
i. setImage(): sets image for a button
btn.setImage(UIImage(named: countries[0]), for: .normal)
1. for: describes state of button
2. .normal the standard state of the button
a. .normal parameter looks like an enum, but it’s actually a
static property of a struct called UIControlState. In
Objective-C – the language UIKit was written in – it’s an
enum, but in Swift it gets mapped to a struct that just
happens to be used like an enum, so if you want to be
technically correct it’s not a true enum in Swift
3.
7. UIButton
a. Events
i. TouchUpInside: "the user touched this button, then released their finger
while they were still over it" – i.e., the button was tapped
b. Attributes
i. Tags: behind the scenes all views have a special identifying number that
we can set, called its Tag
ii.
8. CALayer
a. Core Animation data type responsible for managing the way your view looks
b. CALayer sits beneath all your UIViews (that's the parent of UIButton,
UITableView, and so on), so it's like an exposed underbelly giving you lots of
options for modifying the appearance of views, as long as you don't mind dealing
with a little more complexity
i. example of option for modifying appearance: borderWidth
c. NOTE: CALayer sits at a lower technical level than UIButton, which means it
doesn't understand what a UIColor is. UIButton knows what a UIColor is
because they are both at the same technical level, but CALayer is below
UIButton, so UIColor is a mystery
d. has its own way of setting colors called CGColor
i. comes from Apple's Core Graphics framework
e. UIColor (which sits above CGColor) is able to convert to and from CGColor
easily
i. .cgColor: converts UIColor to CGColor to the end of the UIColor to
have it automagically converted to a CGColor
9. UITableViewCell
a. responsible for displaying one row of data in a table, and we’re going to display
one picture name in each cell
b. need to label specific cell inside IB such that can be referenced inside code. give
cell an identity inside the attributes inspector. Here given id "Picture"
c. Attributes
i. Disclosure Indicator: is "Accessory" attribute

d. Subclassing UITableViewCell
i. You customize the appearance of UITableViewCell subclasses by
adding subviews to its contentView. Adding subviews to the
contentView instead of directly to the cell itself is important because
the cell will resize its contentView at certain times. For example,
when a table view enters editing mode, the contentView resizes itself to
make room for the editing controls
ii. Table view cell layout in standard and editing mode

iii.
iv. If you added subviews directly to the UITableViewCell, the editing
controls would obscure the subviews. The cell cannot adjust its size
when entering edit mode (it must remain the width of the table view), but
the contentView can and does.
v. Dynamic Cell Heights
vi. Responding to User Changes and Dynamic Type
1. when user changes such as preferred system text size as shown
below, need to ensure adjustments are made in code as well.

a. Setting Dynamic Type to "Automatically Adjusts Font" will


set adjustsFontForContentSizeCategory to true
2. Text Styles

10. UINavigationController
a. Embed VC inside NC
i. IB will move your existing VC to the right and add a navigation controller
around it – you should see a simulated gray bar above your table view
now. It will also move the “Is Initial View Controller” property to the
navigation controller
b. Properties
i. hidesBarsOnTap:
1. When this is set to true, the user can tap anywhere on the current
view controller to hide the navigation bar, then tap again to show
it.
2. Be warned: you need to set it carefully when working with
iPhones. If we had it set on all the time then it would affect taps in
the table view, which would cause havoc when the user tried to
select things. So, we need to enable it when showing the detail
view controller, then disable it when hiding.
3. override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.hidesBarsOnTap = true
}
ii. prefersLargeTitles: makes titles really large when set to true
1. navigationController?.navigationBar.prefersLargeTitle
s = true

vs
2. Apple recommends you use large titles only when it makes
sense, and that usually means only on the first screen of your app.
Apple recommends using large titles only for root view
controllers
3.
iii. largeTitleDisplayMode: allows us to control large titles mode for specific
navigation items
1. navigationItem.largeTitleDisplayMode = .never
iv.
c. navigationItem vs navigationController?.navigationItem
i. navigationItem: controls the nav for the specific VC you're on now
ii. navigationController?.navigationItem: controls nav for all VCs
under this nav controller
d. navigationController?.navigationBar vs
navigationController?.navigationItem
i. navigationController?.navigationItem: represents the navigation
item associated with the current view controller
1. has properties s/: title, leftBarButtonItems,
rightBarButtonItems, etc., allowing you to customize the
appearance and behavior of the navigation item for the specific
view controller
ii. navigationController?.navigationBar: represents the navigation
bar of the navigation controller. The navigationBar is the entire bar at
the top of the screen that contains the title and buttons. It allows you to
customize the appearance of the navigation bar itself, including its
background color, bar style, and other global settings
iii. navigationItem is specific to a particular view controller, while
navigationBar is about the appearance and behavior of the entire
navigation bar within the navigation controller
11. UIActivityViewController
a. VC in UIKit framework that provides a simple and consistent way to allow users
to share content and perform actions with that content. It presents a share
sheet that displays a list of available activities or services that users can use to
share or interact with the content
b. Activity view controllers must be presented from something on iPad
i. On iPad, iOS wants to connect activity view controllers to whatever
triggered them, so that users have some extra context
12. Selectors
a. #selector allows Swift to check that a method exists while building our code
b. Selectors are a simple way of saying "here's the function I want you to run". They
pre-date Swift
c. The #selector directive CANNOT be skipped with a force unwrap.
i. #selector is required; force unwraps can't help here
d.
13. Auto Layout
a. Resolve Auto Layout Issues > Reset To Suggested Constraints
i. Shortcut for Selected View: Shift+Alt+Cmd+=
b. Update Frames: use when making constraint/auto layout updates
that haven't yet appeared in IB
c. sizeToFit(): places at default position (at the top-left of the screen)
14. Retina vs Non-Retina
a. iOS assets come in the sizes 2x and 3x, which are two times and three times the
size of the layout you created in IB
b. With iPhone 4, Apple introduced retina screens that had double the number of
pixels as previous screens
c. From Pixels to Points
i. Points: virtual pixels
ii. On non-retina devices, a width of 10 points became 10 pixels, but on
retina devices it became 20 pixels
d. Naming Convention
i. [email protected]
e. Retina HD Screens: added a x3 option
f. App Thinning:
i. You might think this sounds awfully heavy – why should a retina device
have to download apps that include Retina HD content that it can’t show?
ii. App thinning automatically delivers only the content each device is
capable of showing – it strips out the other assets when the app is being
downloaded, so there’s no space wasted
15. WKWebView
16. UIToolBar
a. UI component in iOS that provides a container for a set of buttons or controls
typically used in conjunction with a UINavigationController or at the
bottom of a view. It's commonly used to provide easy access to frequently
used actions or navigation controls
b. holds and shows a collection of UIBarButtonItem objects that the user can tap
on. We already saw how each view controller has a rightBarButton item, so a
UIToolbar is like having a whole bar of these items
c. all view controllers automatically come with a toolbarItems array that
automatically gets read in when the view controller is active inside a
UINavigationController
d. Characteristics
i. UIToolBarItem: class in UIKit that represents an item that can be
placed on a UIToolbar or UINavigationBar. It encapsulates various
types of UI elements that can be used as items, such as buttons, text
fields, or custom views
1. Example:
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target:
self, action: #selector(doneButtonTapped))
2. barButtonSystemItem types
a. .flexibleSpace: which creates a flexible space. It doesn't
need a target or action because it can't be tapped
ii. UIProgressView: shows how far the page is through loading
1. NOTE: You can't just add random UIView subclasses to a
UIToolbar, or to the rightBarButtonItem property. Instead,
you need to wrap them in a special UIBarButtonItem, and use
that instead
2. Example:
// Set property
var progressView: UIProgressView!
...
progressView = UIProgressView(progressViewStyle: .default)
progressView.sizeToFit()

// Wrapping progressView inside UIBarButtonItem


let progressButton = UIBarButtonItem(customView: progressView)
17. UIAlertController
a. Used to show an alert with options to the user
b. Requires use of closure
c. UIAlertAction
i. creates action that can be added to alert and acted upon by user with
handler function to act upon the action
d. Example
let ac = UIAlertController(title: "Enter answer", message: nil, preferredStyle: .alert)
ac.addTextField()
let submitAction = UIAlertAction(title: "Submit", style: .default) { [weak self, weak ac] action in
guard let answer = ac?.textFields?[0].text else { return }
self?.submit(answer)
}
i.Capture List: [weak self, weak ac] -> defines how surrounding variables are
captured in order to prevent strong reference cycles (retain cycles)
1. Default: captured strongly
ii. IMPORTANT NOTE: if the closure executes after self or ac has been
deallocated (become nil), accessing them inside the closure will not
result in a crash because of the weak capture. The guard let statements
are used to safely unwrap these weak references. If the objects are
deallocated, the guard conditions fail, and the closure exits early.
iii. Weak vs Unowned References: Weak references must be unwrapped
before use; unowned references can be used directly
e. UITextChecker: iOS class that is designed to spot spelling errors
func isReal(word: String) -> Bool {
let checker = UITextChecker()
let range = NSRange(location: 0, length: word.utf16.count)
let misspelledRange = checker.rangeOfMisspelledWord(in: word, range:
range, startingAt: 0, wrap: false, language: "en")
return misspelledRange.location == NSNotFound
}
i. Why word.utf16.count instead of word.count?
1. Swift’s strings natively store international characters as individual
characters, e.g. the letter “é” is stored as precisely that. However,
UIKit was written in Objective-C before Swift’s strings came along,
and it uses a different character system called UTF-16 – short for
16-bit Unicode Transformation Format – where the accent and
the letter are stored separately.
2. It’s a subtle difference, and often it isn’t a difference at all, but it’s
becoming increasingly problematic because of the rise of emoji –
those little images that are frequently used in messages. Emoji are
actually just special character combinations behind the scenes,
and they are measured differently with Swift strings and UTF-16
strings: Swift strings count them as 1-letter strings, but UTF-16
considers them to be 2-letter strings. This means if you use count
with UIKit methods, you run the risk of miscounting the string
length.
3. Use this simple rule: when you’re working with UIKit, SpriteKit, or
any other Apple framework, use utf16.count for the character
count. If it’s just your own code - i.e. looping over characters
and processing each one individually – then use count instead.
18. Visual Layout Format
a. technique kind of like a way of drawing the layout you want with a series of
keyboard symbols
b. Returns array of constraints
i. When you use VFL to create constraints, it returns an array of
constraints under certain circumstances
ii. In Swift, you typically use the
NSLayoutConstraint.constraints(withVisualFormat:options:met
rics:views:) method to create constraints using VFL. This method
returns an array of constraints.
iii. let views = ["button": button, "label": label]
let horizontalConstraints =
NSLayoutConstraint.constraints(
withVisualFormat: "H:|-[button]-[label]-|",
options: [],
metrics: nil,
views: views
)
NSLayoutConstraint.activate(horizontalConstraints)
c. Step1: create a dictionary of the views we want to lay out
let viewsDictionary = ["label1": label1, "label2": label2,
"label3": label3, "label4": label4, "label5": label5]
d. Step2: add horizontal constraints
for label in viewsDictionary {
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[\(label)]|",
options: [], metrics: nil, views: viewsDictionary))
}
i. view.addConstraints():this adds an array of constraints to our view
controller's view. This array is used rather than a single constraint
because VFL can generate multiple constraints at a time.
ii. NSLayoutConstraint.constraints(withVisualFormat:) is the Auto
Layout method that converts VFL into an array of constraints. It accepts
lots of parameters, but the important ones are the first and last.
iii. We pass [] (an empty array) for the options parameter and nil for the
metrics parameter. You can use these options to customize the meaning
of the VFL, but for now we don't care.
iv. "H:|[label1]|"
1. The H: parts means that we're defining a horizontal layout; we'll
do a vertical layout soon. The pipe symbol, |, means "the edge of
the view." We're adding these constraints to the main view inside
our view controller, so this effectively means "the edge of the view
controller." Finally, we have [label1], which is a visual way of
saying "put label1 here". Imagine the brackets, [ and ], are the
edges of the view. So, "H:|[label1]|" means "horizontally, I
want my label1 to go edge to edge in my view."
e. Step3: add vertical constraints
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:
"V:|-[label1]-[label2]-[label3]-[label4]-[label5]", options: [], metrics: nil,
views: viewsDictionary))
i. - symbol: means space
1. 10 points by default
ii. NOTE: that our vertical VFL doesn't have a pipe at the end, so we're not
forcing the last label to stretch all the way to the edge of our view.
This will leave whitespace after the last label, which is what we want right
now
f. metrics: allow us to use variables inside the format
let metrics = ["labelHeight": 80]
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:
"V:|-[label1(==labelHeight)]-[label2(==labelHeight)]-[label3(==labelHeig
ht)]-[label4(==labelHeight)]-[label5(==labelHeight)]", options: [],
metrics: metrics, views: viewsDictionary))
g. priority: enable us to rank importance of certain constraints from 1 to 1000,
where 1000 is required and anything below is considered optional but the higher
the number the higher the priority. Here the labelHeight priority for label1 is set at
999 by adding @999 to the constraint
let metrics = ["labelHeight": 80]
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:
"V:|-[label1(==labelHeight@999)]-[label2(==labelHeight)]-[label3(==label
Height)]-[label4(==labelHeight)]-[label5(==labelHeight)]", options: [],
metrics: metrics, views: viewsDictionary))
h. Setting labels with equal height by setting height of label1 and the rest equal to
height of label1
let metrics = ["labelHeight": 80]
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:
"V:|-[label1(==labelHeight@999)]-[label2(label1)]-[label3(label1)]-[labe
l4(label1)]-[label5(label1)]", options: [], metrics: metrics, views:
viewsDictionary))
i. Using Multiplier and Constant
i. equalTo option lets you specify another height anchor as its first
parameter, along with a multiplier and a constant. When you use both a
multiplier and a constant, the multiplier gets factored in first then the
constant. So, if you wanted to make one view half the width of the main
view plus 50, you might write something like this:
ii. yourView.widthAnchor.constraint(equalTo:
view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.5,
constant: 50).isActive = true
iOS Topics

1. JSON & Codable


a. JSON Formatter Online
b. Tutorial by Van Der Lee
c. Video Tutorial by Stuart Lynch
d. JSON Decoder REQUIRES a data object to decode
e. When creating the Swift Structure, make sure to use same key names as used in
the data
i. string object to decode into JSON
{
"palette_name":"Flat Colours",
"palette_info":"For Design",
"palette_colors":[
{
"sort_order":0,
"description":"Flat Powder Blue (Light)",
"red":170,
"green":186,
"blue":241,
"alpha":1.0
}, ...
ii. Therefore when creating struct DO NOT change keys to something like
this
paletteName: String
paletteInfo: String
f. keyDecodingStrategy: allows you to convert JSON key formats from
snake_case and others to camelCase
g. To change key names entirely:
h. Decoding Dates
iii. by Default Decodable/Encodable uses a Double representation of the
timeIntervalSince1970 date which is the representing the number of
seconds since the reference date
1. VERY uncommon
iv. Need a Date Strategy for decoding
v.

150 Interview Qs

1. Accessibility
a. How much experience do you have testing with VoiceOver?
i. tested ADA accessibility items working on accounts overview portion of
BofA app
ii. made sure voice over matching text when needed
iii. ensured that when there are html elements on the view like <small> that
those do not get read out. instead only the actual text reads out. Did this
by passing in parsed version of text to ADA
iv. Screen curtain: When Screen Curtain is enabled, it turns off the display of
the device, effectively making the screen black. This can be useful for
users who are blind or have low vision and rely on VoiceOver, a screen
reader on iOS, to navigate and interact with their device. By enabling
Screen Curtain, users can conserve battery life and maintain privacy in
situations where they may not want the content on their screen to be
visible. It allows users to interact with their device using VoiceOver
without the need to see the visual content.
v. ???Using Screen Curtain
b. How would you explain Dynamic Type to a new iOS developer?
i. it's a way of enabling user to adjust font size on supported apps. it's an
ios accessibility feature and purpose is to enhance readability and
usability of apps for users with visual impairment.
ii. to implement utilize dynamic text styles by UIKit. ie:
UIFontTextStyleBody or UIFontTextStyleHeadline
iii. Users can configure their preferred text size in the iOS Settings app under
Display & Text Size
iv. using these aligns with Apples Best Practices
c. What are the main problems we need to solve when making accessible apps?
i. visual impairment, color blindness, touch problems, and audio problems
ii. ???Using Dynamic Type
d. What accommodations have you added to apps to make them more accessible?
i. Added voice over capability, correct pronunciation
1. ie: ios doesn't pronounce the letter 'a' correctly. it pronounces it as
'a' in 'apple' instead of as 'a' in 'ankle', so had to iterate through to
figure out exactly best way. i think i used 'ay' to simulate proper
pronunciation
2. Data
a. Differences between classes and structs?
i. structs are value types -
1. structs have memoized inits so don't require an init
2. when struct passed around it is copied
class Car {
var year: Int
var make: String
var color: String
}
var myCar = Car(year: 2022, make: "Porsche", color: "Grey")
var stolenCar = myCar
stolenCar.color = "Yellow"
print (myCar.color) -> "Grey"
ii. classes are reference types - any pointer to reference can change the
object
class Car {
var year: Int
var make: String
var color: String
init(year: Int, make: String, color: String) {
self.year = year
self.make = make
self.color = color
}
}
var myCar = Car(year: 2022, make: "Porsche", color: "Grey")
var stolenCar = myCar
stolenCar.color = "Yellow"
print (myCar.color) -> "Yellow"
iii.
both create objects
iv.
When to use each?
1. Struct - more lightweight and performant, and value type
2. Class - have inheritance so can be based on other existing
classes and can inherit using subclassing. So can build on top of
inherited class
b. Why tuples useful?
i. Why use tuples
ii. Understanding tuples and using them
iii. Similar to anonymous structs
iv. Not extendible

Swift Knowledge Base

1. Strings
a. Raw Strings
i. Ignores quotes instead of ending the string
var raw = #"this is a raw string that ignore "quotes""#
print(raw) -> "this is a raw string that ignore "quotes"
ii. Ignore escapes
var ignoreEscapes = #"this one ignores \n escapes"#
print(ignoreEscapes) -> "this one ignores \n escapes"
iii. Use interpolation
var myName = "Yan"
var useStrInterpolation = #"printing name here with
interpolation: \#(myName)"#
print(useStrInterpolation) -> "printing name here with interpolation: Yan"
b. Capitalize All Words in String - aka Title Case
i. let str1 = "sunday, monday, happy days"
print(str1.capitalized) -> "Sunday, Monday, Happy Days"
2.
Warnings

Errors
1. Cannot convert value of type 'String' to expected element type 'Int':
var array: Array<Int> = [4, 53, "string"]
a. array types declared as Int so cannot include string
2. Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type
annotation if this is intentional:
var array = [4, 53, "string"]
a. without explicitly declaring type Any cannot have mixed types inside array
b. Must do: var array: Array<Any> = [4, 53, "string"]
3. Type 'Int' cannot be used as a boolean; test for '!= 0' instead:
for (i, any) in array.enumerated() {
if i%2{
print(any)
}
}
a. if & while statements MUST eval to a Bool
b. Must change if statement to: if i%2 != 0{
4. No exact matches in call to subscript:
The error you're encountering is due to the fact that the brackets dictionary keys
and values are both strings, but sArr[i] is of type Character. In Swift, you cannot
directly use a Character as a subscript for a dictionary with String keys.
class Solution {
func isValid(_ s: String) -> Bool {
let brackets = [
"(": ")",
"{": "}",
"[": "]"
]
let sArr = Array(s)
let len = s.count
if len%2 != 0 {
return false
}
for i in 0..<(len/2){
if brackets[sArr[i]] != sArr[len-i-1]{
return false
}
}
return true
}
}
5. Cannot assign through subscript: 'arr' is a 'let' constant:
func modifyArray(arr: [Int]) {
// Error: Cannot assign through subscript: 'arr' is a 'let'
constant
arr[0] = 42
}
let myArray = [1, 2, 3]
modifyArray(arr: myArray)
a. To solve this issue, you can make the parameter an inout parameter, which
allows the function to modify the value of the parameter:
func modifyArray(arr: inout [Int]) {
b. NOTE: when passing in inout parameter, MUST INCLUDE '&':
modifyArray(&arr)
6. error: cannot convert value of type 'String.SubSequence' (aka 'Substring') to
expected argument type 'String':
a. Starting from Swift 4, substrings are represented by the Substring type, which is
a view into the original string. When you encounter this error, it means that a
function or method is expecting a String as an argument, but you're providing a
Substring.
b. To resolve this issue, you can convert the Substring to a String.
7. Line 22: Char 48: error: missing argument label 'into:' in call in solution.swift
let temp = nums.enumerated().reduce(1) {(result, tuple) in:
a. tuple is not accessed like an array. To access tuple:
Access tuple.offset and tuple.element directly instead of using
tuple[0] and tuple[1].
8. Referencing instance method 'firstIndex(of:)' on 'Collection' requires that 'Item'
conform to 'Equatable'
@IBAction func addNewItem(_ sender: UIButton){
let newItem = itemStore.createItem()
if let index = itemStore.allItems.firstIndex(of: newItem){
let indexPath = IndexPath(row: index, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic
}
}
a. The firstIndex(of:) method works by comparing the item that you are looking
for to each of the items in the collection. It does this using the == operator. Types
that conform to the Equatable protocol must implement this operator, and Item
does not yet conform to Equatable.
b. Update Item class to conform to Equatable protocol and define the == operator
static func == (lhs: Item, rhs: Item) -> Bool {
return lhs.name == rhs.name &&
lhs.dateCreated == rhs.dateCreated &&
lhs.serialNumber == rhs.serialNumber &&
lhs.valueInDollars == rhs.valueInDollars
}
c.
9. [WindowScene] Failed to instantiate the default view controller for
UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?
a. set initial view controller in storyboard
b. set programmatically
c. Value of type 'AppDelegate' has no member 'window'
i. no window in app delegate, instead need to set in scene delegate
ii. set programmatically

d.
10.

Code Questions
1. In these two func defs, what's the significance of using ? versus ! in the UIAlertAction
a. Option 1:
func askQuestion(action: UIAlertAction?) {
// Your code
}
b. Option 2:
func askQuestion(action: UIAlertAction!) {
// Your code
}
c. Answer: with !, developer is saying that action will be a UIAlertAction optional
that will definitely be unwrapped
2. Can you return from a function while inside a foreach?
a. You can't directly return from a function while inside a forEach closure. The
return statement inside a closure WILL ONLY exit the closure itself, not the
entire function
3.

Misc

1. Git
a. Rename local and remote branch
b. Remove untracked files
i. git clean -n: dry run to show which files intend to remove
ii. git clean -f: actually remove files
iii. git clean -i: interactive mode
iv. git clean -d: remove directories
c. Add worktree to push to same repo from different local locations
i. git worktree add /path/to/second/location <branch-name>
1. branch-name cannot be currently checked out
ii. git worktree list : lists existing worktrees
d. Remove files from tracking. If already being tracked and updating .gitignore file
still does not affect the tracking, need to remove tracking from cache
i. git rm --cached *trackedFile
ii. commit and push
e. Add untracked files WITHOUT staging them
i. git add -N file1 file2
1. -N or --intent-to-add both work
f. Amend previous commit
i. Amend commit comment: git commit --amend
ii. Amend by adding more files to previous commit:
1. add the additional files with: git add files
2. amend with previous comment: git commit --amend -C HEAD
3. amend with new comment: git commit --amend
g. Diff of staged
i. git diff --staged
ii. git difftool --staged: more visual representation
iii. git gui: use GUI
h. Stage by hunk
i. git add -p
1. Git will interactively prompt you with each hunk, asking if you want
to stage it. You'll see options like:
2. y - stage this hunk
3. n - do not stage this hunk
4. q - quit; do not stage this hunk or any of the remaining ones
5. a - stage this hunk and all later hunks in the file
6. d - do not stage this hunk or any of the later hunks in the file
i.
2. General Qs &As
a. Q: in iOS dev on Xcode and swift, when do you need to import a module and
when can the file you're working on already see it without import
i. A: If you are working within the same module (e.g., your app target), you
generally don't need to import the module. Swift files within the same
module can access each other without an explicit import statement.
b. Q: what is a bitmap image
i. A: also known as a raster image, is a digital image composed of pixels
arranged in a grid. Each pixel (picture element) represents a single point
in the image and contains information about its color and brightness. The
term "bitmap" refers to the mapping of bits (binary digits) to each pixel,
where each bit represents a specific attribute, such as color.
c. Convert a string to a safe format for URL slugs and filenames
d. What is Command Line Tools
i. Command Line Tools for Xcode is a package of command line utilities,
compilers, and other development tools provided by Apple. These tools
are separate from the full Xcode IDE (Integrated Development
Environment) but are essential for software development on macOS,
especially when working with the command line or building software
without the need for the complete Xcode graphical interface.
ii. Command Line Tools include various tools such as compilers (like GCC
and Clang), debugging tools, performance analysis tools, and utilities for
managing developer resources. They provide a lightweight way for
developers to access essential development tools without installing the
entire Xcode IDE.
iii. Developers often use Command Line Tools when working in a Terminal
environment or when building and compiling software using build systems
like Make or CMake. The tools are available for download and installation
separately from Xcode, making them convenient for developers who
prefer to work with a minimal set of tools.
iv. To install Command Line Tools, you can use the xcode-select command
or download them directly from the Apple Developer website. The
installation can be done independently of, or in addition to, a full Xcode
installation.
e. What is Developer Directory
i. The developer directory, also known as the "active developer directory," is
the path to the directory that contains developer tools, utilities, and
resources needed for software development on macOS. It includes
compilers, libraries, headers, and other essential components.
ii. In the context of Xcode and the Command Line Tools, the developer
directory is the location where Xcode installs its developer tools. For
Xcode installations, the developer directory is typically within the Xcode
application bundle. For Command Line Tools, it's a separate directory.
iii. You can set or switch the active developer directory using the
xcode-select command in the Terminal. This command allows you to
choose which set of developer tools is used by the system.
iv. For example, to set the active developer directory to a specific Xcode
installation, you can use the following command:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
1. This command ensures that Xcode's developer tools are used for
compilation, building, and other development tasks.
f. Build Script Sandboxing
g. Frame vs Bounds
h.

You might also like