iOS App Dev - iOS W - Big Nerd Branch
iOS App Dev - iOS W - Big Nerd Branch
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
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
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.
Simulator
1. Active-scheme: selects the iPhone type for simulator to run
2.
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
Programmatic Views
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:
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
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.
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:
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)
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:
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
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
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 stack
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
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
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
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
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
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
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.
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()
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
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.