Tumgik
#goodorbetterprogrammer
Text
HackingWithSwift Day 42/43/44 - Project 10
Now get to be introduce to UICollectionViewController.
Overall more or less the same as objective-c. And the author introduce new thing, fatalError() - it will crash the app and print out the error message the developer code but it’s something needed as well if one just want to terminate the app if certain things didn’t go right and maybe no point for the user to continue it.
From the tutorial , maybe need to add 1 more thing is estimate size on at the storyboard there need to change to “None” instead of automatic or else the cell size setting won’t work
Then follow by UIImagePickerController, which needs UIImagePickerControllerDelegate and UINavigationControllerDelegate as well.
If we need to access the image later on, or want to store it in app private storage, what we can do is get a file path, and write it to the path. For example:
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let imageName = UUID().uuidString let imagePath = paths[0].appendingPathComponent(imageName) if let jpegData = image.jpegData(compressionQuality: 0.8) { try? jpegData.write(to: imagePath) }
Overall that’s all for this project.
1 note · View note
Text
HackingWithSwift Day 39/40 - Project 9
Golden rules - access remote resources must do it in background thread instead of main thread since that will freeze the UI which has to be run on the main thread. And GCD can fix that
And async() is the keyword, to switch from main thread to background thread, and vice versa. Although it says it’s First In First Out (FIFO) but then since one can open many thread run at the same time, so it’s kinda hard to say it’s FIFO all the time, unless one define one single operation queue and every task share the same thing.
Each thread priority is control by quality of service (QoS) , and there’s 4 QoS - .userInteractive - highest priority, almost dedicate all available resource to this thread - .userInitiated - it’s important still because you’ll need that task to complete to show something - .utility - something that need maybe a long time to complete but no immediate attention require - .background - run task that’s not important or something user doesn’t care much
User Interactive and User Initiated thread will eat the battery to boost performance, Utility try boost performance as possible but w/o killing the battery much, like 50:50 , while Background mode only cares about the battery part , 0 concern on performance and let it run forever.
There’s default mode as well, which is in between User Interactive and User Initiated
And no weak self needed in GCD closure as it get discarded after finish run
Example DispatchQueue.global(qos: .userInitiated).async
But after go background thread, we need to come back to main thread as well to update the ui after finished the background task.
Example DispatchQueue.main.async
So .global vs .main , between background and main thread.
But how do I know at certain point I’m on background thread or main thread?
Another way to use GCD
performSelector(inBackground:) and performSelector(onMainThread:)
1 note · View note
Text
HackingWithSwift Day 36/37/38 - Project 8
This time is build UI with just code
So far this subject is review how to write constraint by code level.
Nothing much new besides Collection.enumerated, String.replacingOccurrences and didSet for the Property observe
So far not much surprise yet
0 notes
Text
HackingWithSwift Day 33/34/35 - Project 7
Here introduce to UITabBarController
“Swift has built-in support for working with JSON using a protocol called Codable. When you say “my data conforms to Codable”, Swift will allow you to convert freely between that data and JSON using only a little code.”
Definition by the author, let’s see how powerful is this.
So it’s quite convenient to parse the json data into swift native support data type, instead of self declare and unwrap it layer by layer.
Only 1 thing is the window code to add a new tab, I think it’s time to update it using SceneDelegate since window is omitted from AppDelegate with the latest Xcode.
Seems like Codable usage is very wide, might need more research on this
0 notes
Text
HackingWithSwift Day 30/31
https://www.hackingwithswift.com/read/6/2/advanced-auto-layout For this part, kinda hope the author can add in some visual aids instead of full text description on what to change as everyone may be using different screen size or different constraint to begin with
let label2 = UILabel() label2.translatesAutoresizingMaskIntoConstraints = false
translatesAutoresizingMaskIntoConstraints set to false to prevent iOS generate default auto layout constraints if we intend to do it manually
===========================
So time for Auto Layout Visual Format Language (VFL). I have try this before in ObjC, hope the syntax is not so complicated in swift
let viewsDictionary = ["label1": label1, "label2": label2, "label3": label3, "label4": label4, "label5": label5] view.addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "H:|[label1]|", options: [], metrics: nil, views: viewsDictionary)) H = horizontal constraint | | = single pipe on both end means to go edge to edge in parent view (equivalent to leading space = 0,  trailing space = 0, or width = parent width)
view.addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "V:|[label1]-[label2]-[label3]-[label4]-[label5]", options: [], metrics: nil, views: viewsDictionary))
V = vertical constraint | = single pipe at the start point means align from top - = dash in between, it’s vertical spacing , default = 10 points (equivalent to top spacing or bottom spacing, depends on the relationship between 2 views)
Now try update to this view.addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "V:|[label1(==88)]-[label2(==88)]-[label3(==88)]-[label4(==88)]-[label5(==88)]-(>=10)-|", options: [], metrics: nil, views: viewsDictionary))
==88, 88 points for height (since it’s V) >=10, minimum bottom spacing 10 points (since it’s V)
Imagine if one need to change the height very frequent due to certain circumstances, human error may occur when wrong value input into it. So one can make use of metric, meaning aside the value to a variable and use that variable whenever needed, then can reduce human error
let metrics = ["labelHeight": 88]
view.addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "V:|[label1(labelHeight)]-[label2(labelHeight)]-[label3(labelHeight)]-[label4(labelHeight)]-[label5(labelHeight)]->=10-|", options: [], metrics: metrics, views: viewsDictionary))
===========================
If turn to landscape, there will be constraint issue since the total height probably can’t fulfil 5 labels with 88 points height with the addition spacing in between. So we need to tell which constraint get higher priority
Constraint priority is a value between 1 and 1000. The higher the number, the higher the priority. 1000 = must follow. Since we know 88 points for height is impossible to be fulfilled in landscape mode, so can make that optional
view.addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "V:|[label1(labelHeight@999)]-[label2(label1)]-[label3(label1)]-[label4(label1)]-[label5(label1)]->=10-|", options: [], metrics: metrics, views: viewsDictionary))
labelHeight@999 = set labelHeight with priority value 999 , so if it can’t fulfil the height set, auto layout can shrink it since it’s not 1000 label2(label1) = label2 copy label1 height (equivalent to label 2 equal height to label 1)
===========================
And then it’s Anchor time
Each view comes with widthAnchor, heightAnchor, topAnchor, bottomAnchor, leftAnchor, rightAnchor, leadingAnchor, trailingAnchor, centerXAnchor, and centerYAnchor.
If device language is those type from right to left, like Arabic, using leadingAnchor and trailingAnchor will flip the view the other way around. If want remain the same as leadingAnchor and trailingAnchor with language like English as device language, one need to use leftAnchor and rightAnchor instead.
var previous: UILabel?
for label in [label1, label2, label3, label4, label5] {    label.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true    label.heightAnchor.constraint(equalToConstant: 88).isActive = true
   if let previous = previous {        // we have a previous label – create a height constraint        label.topAnchor.constraint(equalTo: previous.bottomAnchor, constant: 10).isActive = true    }
   // set the previous label to be the current one, for the next loop iteration    previous = label }
By default the view will stick to the top if we don’t assign anything, so for the first label, we don’t need the topAnchor, only the following one onwards. BUT there’s one problem. To make sure the view look nice at iPhone X series or iPhone with notch, we need to ensure the top or bottom view is within the safe area zone
if let previous = previous {    // we have a previous label – create a height constraint    label.topAnchor.constraint(equalTo: previous.bottomAnchor, constant: 10).isActive = true } else {    // this is the first label    label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true }
So we will need this to ensure it look nice.
0 notes
Text
HackingWithSwift Day 27/28/29
Resuming….the closure nightmare
Weak vs strong vs unowned
class Singer {    func playSong() {        print("Shake it off!")    } }
func sing() -> () -> Void {    let taylor = Singer()
   let singing = {        taylor.playSong()        return    }
   return singing }
“taylor” inside the sing closure will never get destroy even after the function has returned, under “strong” reference.
===================== func sing() -> () -> Void {    let taylor = Singer()
   let singing = { [weak taylor] in        taylor?.playSong()        return    }
   return singing }
If change to “weak” then it’s possible to get destroy when it gets “nil”
“taylor” now become optional now, since it’s possible to be destroy at any time. But that also means “taylor” only exist inside sing()
======================
Alternative to weak will be “unowned”
func sing() -> () -> Void {    let taylor = Singer()
   let singing = { [unowned taylor] in        taylor.playSong()        return    }
   return singing }
Unowned is like implicit unwrap, meaning from the previous example, the “weak” taylor need to use “?” or “!” before calling playSong(), unowned is like weak + “!” together .
===================
Project Part
Array.randomElement = randomly return 1 object from the array
And learn about UITextChecker
0 notes
Text
HackingWithSwift Day 24/25/26
Introduction to WKWebview
“The delegation solution is brilliant: we can tell WKWebView that we want to be informed when something interesting happens. In our code, we're setting the web view's navigationDelegate property to self, which means "when any web page navigation happens, please tell me – the current view controller.” Definition of delegation by the author, this will comes in handle when we create our own custom delegate after this.
class ViewController: UIViewController, WKNavigationDelegate
Parent class comes first, then only protocol next
@objc func openTapped() { let ac = UIAlertController(title: "Open page…", message: nil, preferredStyle: .actionSheet)        ac.addAction(UIAlertAction(title: "apple.com", style: .default, handler: openPage))        ac.addAction(UIAlertAction(title: "hackingwithswift.com", style: .default, handler: openPage))        ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))        ac.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem        present(ac, animated: true) }
func openPage(action: UIAlertAction) { let url = URL(string: "https://" + action.title!)!        webView.load(URLRequest(url: url)) }
action.title is refer to the title set at the new instance of UIAlertAction
================== And the very first delegate we use under WKNavigationDelegate
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {        title = webView.title }
Whenever web view finished load a page, it will change the navigation title to be the same as web view title
==================
Now introduce UIToolbar and UIProgressView
var progressView: UIProgressView = UIProgressView(progressViewStyle: .default) progressView.sizeToFit() let progressButton = UIBarButtonItem(customView: progressView)
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload))
toolbarItems = [progressButton, spacer, refresh]
Wrap the progress button in UIBarButtonItem and add it to toolbarItems =============
Key-value observing (KVO)
To update the progressView we need to know the web view loading progress
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
Usually when do addObserver(), one need to take care of removeObserver() to ensure everything run fine
Then add the following method to get the value
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {    if keyPath == "estimatedProgress" {        progressView.progress = Float(webView.estimatedProgress)    } }
Since we want to observe the key path of WKWebView.estimatedProgress, then inside the method, we check if the key path of the value is actually same as WKWebView.estimatedProgress. If it’s the same, we get the estimate progress and update the progressView progress.
The extra float casting is needed since webView.estimatedProgress is a Double, and progressView.progress only accept Float.
================= func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {    let url = navigationAction.request.url
   if let host = url?.host {        for website in websites {            if host.contains(website) {                decisionHandler(.allow)                return            }        }    }
   decisionHandler(.cancel) }
To ensure user can’t navigate to other page, need this delegate method to control the site, but need to read more on @escaping closure
===================
0 notes
Text
HackingWithSwift Day 23
Review of project 1, project 2 and project 3
So far learn how to use UITableView, UIViewController, UIButton, UIImageView, UIBarButtonItem. And how to edit some of the view property using CALayer properties, such as  borderWidth, borderColor , and etc And learn about assistant editor view, to add IBOutlet and IBAction as well. And access resources using Bundle, and FileManager, like using the image assets, with the @2x , @3x concept
let items = try! fm.contentsOfDirectory(atPath: path)
Assume fm is FileManager object, swift will enforce you to wrap it in try catch when call contentsOfDirectory, that’s because the path might not exist or you type wrong name, but since our sample code won’t face that issue, we can just direct use try! instead of proper try catch.
When comes to UITableView, we also applied some of the protocol under UITableViewDelegate and UITableViewDataSource
0 notes
Text
HackingWithSwift Day 22
Introduction to UIActivityViewController
let vc = UIActivityViewController(activityItems: [image], applicationActivities: []) vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem present(vc, animated: true)
You can add any item you want to the activityItemsArray, and popoverPresentationController code is needed for iPad , if the app only target iPhone then you can omit this part but best to provide it
There isn’t much to write about Project 3 , aside from the @objc tag for selector, which may need more research to know about it.
0 notes
Text
HackingWithSwift Day 19/20/21
Project 2
var countries = [String]()
countries.append("estonia") countries.append("france") countries.append("germany")
countries += ["estonia", "france", "germany",]
Two ways to append array =======================
func askQuestion(action: UIAlertAction! = nil) { button1.setImage(UIImage(named: countries[0]), for: .normal)        button2.setImage(UIImage(named: countries[1]), for: .normal)        button3.setImage(UIImage(named: countries[2]), for: .normal) }
If assign a default value to a passing param for a function, when call the function, can just write askQuestion() without pass in anything. Looks confusing but better try to get use to it
============================
Just realized I can’t do ++ , but only += 1 when I try to do the challenge
For this project, not much note as per project 1 as well, since most of them I learned before when I use objective-c
0 notes
Text
HackingWithSwift Day 16/17/18
Project 1
class ViewController: UIViewController {
   override func viewDidLoad() {        super.viewDidLoad()        // Do any additional setup after loading the view.
       let fm = FileManager.default        let path = Bundle.main.resourcePath!        let items = try! fm.contentsOfDirectory(atPath: path)
       for item in items {            if item.hasPrefix("nssl") {                // this is a picture to load!            }        }    } }
Data type start with “UI” = from uikit
Can go to Preference -> Text Editing -> Show Line Number
============== Way to add auto layout > Go to the Editor menu and choose > Resolve Auto Layout Issues > Reset To Suggested Constraints. > Press Shift+Alt+Cmd+= to accomplish the same thing. > Manually
Large title “One of Apple’s design guidelines is the use of large titles – the text that appears in the gray bar at the top of apps. The default style is small text, which is what we’ve had so far, but with a couple of lines of code we can adopt the new design.” - by the author
navigationController?.navigationBar.prefersLargeTitles = true
0 notes
Text
HackingWithSwift Day 15
Properties - Which are the variables and constants Properties observer - didset , willset Computed Properties - variable with only get, no need assign value , get value on the fly Static Properties - make the properties belongs to the type instead of the instance Access Control - Public: this means everyone can read and write the property. - Internal: this means only your Swift code can read and write the property. If you ship your code as a framework for others to use, they won’t be able to read the property. - File Private: this means that only Swift code in the same file as the type can read and write the property. - Private: this is the most restrictive option, and means the property is available only inside methods that belong to the type, or its extensions.
// Declaring "A" class that has the two types of "private" and "fileprivate": class A {    private var aPrivate: String?    fileprivate var aFileprivate: String?
   func accessMySelf() {        // this works fine        self.aPrivate = ""        self.aFileprivate = ""    } }
// Declaring "B" for checking the abiltiy of accessing "A" class: class B {    func accessA() {        // create an instance of "A" class        let aObject = A()
       // Error! this is NOT accessable...        aObject.aPrivate = "I CANNOT set a value for it!"
       // this works fine        aObject.aFileprivate = "I CAN set a value for it!"    } }
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html Have to read more…….
============= TypeCasting
Typecasting “as?” allow you to check the data type is the one you want or not, else it will return nil and won’t execute the code inside the if statement
============= Closure …..might as well just read back the previous note for review.
0 notes
Text
HackingWithSwift Day 14
Review basic Part 2
Functions
Create re-usable code, and only need to call it by the function itself. Allow to pass in parameters and return values as well. Then there’s the external and internal parameters names as well, depends if the user want to use it or not.
================
Optionals
Basically, a value that can contain nil value, or no value, which will make sense for certain case. And to use the value , one need to unwrap it. And there’s a few ways to unwrap it.
Unwrap #1 - if let style
if let unwrappedStatus = status {    // unwrappedStatus contains a non-optional value! } else {    // in case you want an else block, here you go… }
Unwrap #2 - guard let style
   guard let unwrapped = name else {        print("You didn't provide a name!")        return    }
Unwrap #3 - forced unwrap with “!” (Not recommended unless you know what you are doing) - usually happen for IBOutlet variable or constant (which will be introduce later)
===================
Optional Chaining
let names = ["Vincent": "van Gogh", "Pablo": "Picasso", "Claude": "Monet"] let surnameLetter = names["Vincent"]?.first?.uppercased() ?? “?” //optional chaining combo with Nil coalescing (mean provide a default value)
===================
Enumeration
enum WeatherType {    case sun    case cloud    case rain    case wind(speed: Int)    case snow }
Enumeration with pass in parameter sample. With enum , default case is not necessary for switch as well.
====================
Struct
struct Person {    var clothes: String    var shoes: String
   func describe() {        print("I like wearing \(clothes) with \(shoes)")    } }
A normal struct with function inside, and never forget the “mutating” as well
=====================
Classes
class Singer {    var name: String    var age: Int
   init(name: String, age: Int) {        self.name = name        self.age = age    }
   func sing() {        print("La la la la")    } }
Self declare init is mandatory. Then one (child) can inherit it. Although classes got no “mutating” but there’s “override” for child to use if needed to override parent functions
0 notes
Text
HackingWithSwift Day 13
Review basic Part 1
Variables and constant
If you know that you are not going to or not suppose to change the value of a variable , it’s best to declare it as constant so when you try to change it, Xcode will throw error. In a way, it’s a safe guard. =======================
Types of Data
User can choose to explicitly define the type annotation or let swift decide it, also known as type inference.
For example
let score = 20.0 is the same as
let score : Double score = 20.0
Float vs Double As for which one to use, it depends on how big the number you want to store
var longitude: Float longitude = -86.783333 //78333 longitude = -186.783333 //7833 longitude = -1286.783333 //783 longitude = -12386.783333 //78 longitude = -123486.783333 //8 longitude = -1234586.783333 //no more faction
Since Float has limited space, it prioritise round number first, then only do the fraction number. If you change it to Double, then no issue at all
Boolean - probably gonna skip this one since it’s pretty straight forward
====================
Operators
+ , - , / , * , = , %
Those are the basic operators, then there’s the -=, +=.
Then follow by “Comparison Operators”
> , < , >= , <= , == , !=
=====================
String interpolation
“This is a fancy name for what is actually a very simple thing: combining variables and constants inside a string.” I like this definition by the author, haha. “Fancy Name”
var name = "Tim McGraw" var age = 25 var latitude = 36.166667
"Your name is \(name), your age is \(age), and your latitude is \(latitude)"
The “\()” is the key. For sure we can do it with “+” as well, but that’s only applicable when you try combine with string only. If string + Int or other data type, it will not work.
========================
Arrays
var evenNumbers = [2, 4, 6, 8] var songs = ["Shake it Off", "You Belong with Me", "Back to December"]
With type inference, swift know that evenNumbers is an array of Int, and songs is an array of String
To know the actual type of the array, you can print type(of: songs) , then you will see the data type.
var songs = ["Shake it Off", "You Belong with Me", "Back to December", 3]
Swift will throw error since type inference can’t handle mixed data type, and to explicitly define it, you can use [Any] , means it accept any kind of data. But this is not a good practice as it will involve a lot of unwrapping to do to confirm the data type before assign to a variable or constant
And how to create an empty array? There are 2 choices
var songs: [String] = [] var songs = [String]()
And there’s operator for array as well
var songs = ["Shake it Off", "You Belong with Me", "Love Story"] var songs2 = ["Today was a Fairytale", "Welcome to New York", "Fifteen"] var both = songs + songs2 both += ["Everything has Changed"]
+= will work, but -= won’t work in this case so must take note.
=========================
Dictionaries
var person = ["Taylor", "Alison", "Swift", "December", "taylorswift.com"]
If we store a person info in an array, we won’t know how to get the data easily as all the data go by index.
var person = ["first": "Taylor", "middle": "Alison", "last": "Swift", "month": "December", "website": "taylorswift.com"]
If we change to dictionary, value can be retrieve by key, such as person[“first”]. Both array and dictionaries also required an identifier to obtain the value BUT, one of them is index, the other one is key, for the key, we can label it with something more meaningful and easier to remember, compare to index, which is 0,1,2,3,4,5
=========================
Conditional statements
Basically is “if”, “else” , “if else”.
And a few operators, “==” , “||” , “&&” , “!” (NOT)
==========================
Loops
Standard For loops, with … , ..< and _ (underscore) when value not needed
for i in 1...10 {    print("\(i) x 10 is \(i * 10)") }
for i in 1..<10 {    print("\(i) x 10 is \(i * 10)") }
for _ in 1 ... 5 {    str += " fake" }
Sample for looping an array
var songs = ["Shake it Off", "You Belong with Me", "Look What You Made Me Do"]
for song in songs {    print("My favorite song is \(song)") }
For loop in a for loop var people = ["players", "haters", "heart-breakers", "fakers"] var actions = ["play", "hate", "break", "fake"]
for i in 0 ..< people.count {    var str = "\(people[i]) gonna"
   for _ in 1 ... 5 {        str += " \(actions[i])"    }
   print(str) }
Then there’s the “while” , “do while” , and “break” , along with for loop labelling to break a few nested loop at the same time.
======================
Switch case
let liveAlbums = 2
switch liveAlbums { case 0:    print("You're just starting out")
case 1:    print("You just released iTunes Live From SoHo")
case 2:    print("You just released Speak Now World Tour")
default:    print("Have you done something new?") }
Nothing much to explain for this. Straight forward
0 notes
Text
HackingWithSwift Day 12
Optional value
var age: Int? = nil
The scenario given by the author is we can’t assign 0 on age for those user who we don’t know their age, because baby is also age 0. So if I assign 0 , it can be baby as well instead of unknown user, which can be very misleading.
==================
Unwrapping optionals
var name: String? = nil
When try to read an optional value, it might be nil or doesn’t contain any value. Now if you call name.count to calculate the length. In normal ObjC environment, the code will crashed. But in swift , it won’t allow to be execute because it’s unsafe. So we need to check if there’s was a value assign to it, a.k.a “unwrap”. “If let” is one of the easiest way.
if let unwrapped = name {    print("\(unwrapped.count) letters") } else {    print("Missing name.") }
==================
Unwrapping with guard
“guard let” is another way to unwrap it.
func greet(_ name: String?) {    guard let unwrapped = name else {        print("You didn't provide a name!")        return    }
   print("Hello, \(unwrapped)!") }
If the name is nil, guard will stop the code and print “You didn’t provide a name!” Instead of proceed to the “hello” message underneath.
“So, use if let if you just want to unwrap some optionals, but prefer guard let if you’re specifically checking that conditions are correct before continuing.” If let vs guard let - by the author to decide on which one
=================
Force unwrapping
If under certain circumstances that you know the value WON’T be nil, you can forced unwrap it by using “!”. But not the best idea ever as the code may crash.
let url = URL(string: "https://www.apple.com”)!
Usually one write as below let url = URL(string: "https://www.apple.com”)
But this “url” will only return as optional value, so if you want to use it immediately, you still need to unwrap it, while the one above doesn’t have to
=======================
Implicitly unwrapped optionals
let age: Int! = nil
It’s like forced unwrap when you initialise it, provided you know before you use the properties you know you will assign a valid value to it although it has the same risk as “force unwrapping”. So use at own cautious.
Why need this high risk thing ? Usually use for connecting iboutlet with ui but author also mentioned it’s not applicable anymore with SwiftUI or at least less occasion
===================
Nil coalescing
func username(for id: Int) -> String? {    if id == 1 {        return "Taylor Swift"    } else {        return nil    } }
let user = username(for: 15) ?? "Anonymous"
Short version -> provide a default value if the unwrap return nil value
Alternative for Dictionary
let scores = ["Picard": 800, "Data": 7000, "Troi": 900] let crusherScore = scores["Crusher"] ?? 0 //Version 1 coalescing let crusherScore = scores["Crusher", default: 0] //Version 2 coalescing
=====================
Optional chaining
We can chain a few optional together, and swift will check each condition sequentially before execute the next one.
let names = ["Vincent": "van Gogh", "Pablo": "Picasso", "Claude": "Monet"] let surnameLetter = names["Vincent"]?.first?.uppercased() ?? “?”
Check and see if there’s key “Vincent” in “names” , then get first character of the string, then only uppercased it. We can combine it with nil coalescing so can always get a valid value
===================
Optional try
enum PasswordError: Error {    case obvious }
func checkPassword(_ password: String) throws -> Bool {    if password == "password" {        throw PasswordError.obvious    }
   return true }
if let result = try? checkPassword("password") {    print("Result was \(result)") } else {   ��print("D'oh.") }
Typical approach will be do-try-catch, while alternative, can go with “try?” Which does the same thing as well
=====================
Failable initializers
struct Person {    var id: String
   init?(id: String) {        if id.count == 9 {            self.id = id        } else {            return nil        }    } }
If id string length is not equal to 9, it will return a nil struct instead of a valid Person struct
“Failable initializers give us the opportunity to back out of an object’s creation if validation checks fail. “ One line by the author, which explains everything in compact. Haha
======================
Typecasting
class Animal { } class Fish: Animal { }
class Dog: Animal {    func makeNoise() {        print("Woof!")    } }
let pets = [Fish(), Dog(), Fish(), Dog()] //since all of them is a subclass of Animal
for pet in pets {    if let dog = pet as? Dog {        dog.makeNoise()    } }
Typecasting “as?” allow you to check the data type is the one you want or not, else it will return nil and won’t execute the code inside the if statement
0 notes
Text
HackingWithSwift Day 11
Protocols
“Protocols are a way of describing what properties and methods something must have. You then tell Swift which types use that protocol – a process known as adopting or conforming to a protocol.”
Definition by the author.
The one I familiar with should be those delegate , like UITableViewDelegate, UITableViewDataSource , UITextfieldDelegate and etc.
Create an Identifiable protocol, which will require all conforming types to have an id string that can be read (“get”) or written (“set”):
protocol Purchaseable {    var name: String { get set } }
If there’s only “get” , means you can set the value once (when initialise it?), then only can read it, if there’s “get” and “set”, you can overwrite the value as many time as you want
Create a struct that conforms to it:
struct Book: Purchaseable {    var name: String    var author: String }
struct Movie: Purchaseable {    var name: String    var actors: [String] }
struct Car: Purchaseable {    var name: String    var manufacturer: String }
struct Coffee: Purchaseable {    var name: String    var strength: Int }
func buy(_ item: Purchaseable) {    print("I'm buying \(item.name)") }
Meaning this buy , it accept any object that conform to “Purchaseable”
More flexible, or else I will need to create many “buy” function to accept different type of struct ================
Protocol Inheritance
protocol Payable {    func calculateWages() -> Int }
protocol NeedsTraining {    func study() }
protocol HasVacation {    func takeVacation(days: Int) }
protocol Employee: Payable, NeedsTraining, HasVacation { }
So Employee protocol inherits other protocols, which are Payable, NeedsTraining, HasVacation. While classes can’t do so.
So how does it shine ?
protocol Computer {    var price: Double { get set }    var weight: Int { get set }    var cpu: String { get set }    var memory: Int { get set }    var storage: Int { get set } }
protocol Laptop {    var price: Double { get set }    var weight: Int { get set }    var cpu: String { get set }    var memory: Int { get set }    var storage: Int { get set }    var screenSize: Int { get set } }
Both protocols have the same variable, which is the base variables, like both computer and laptop have price, weight, cpu, memory, storage. To prevent repetitive code and more clean structure, it’s better to further break it down.
protocol Product {    var price: Double { get set }    var weight: Int { get set } }
Aside from computer and laptop, any product also comes with price and weight (excluded soft-copy / software)
protocol Computer: Product {    var cpu: String { get set }    var memory: Int { get set }    var storage: Int { get set } }
And for a computer (I assume author is referring it to the cpu box), which has the price and weight, but also got cpu processor, memory, and also hard disk storage.
protocol Laptop: Computer {    var screenSize: Int { get set } }
For a laptop, it has everything that computer has, just computer (cpu box) doesn’t come with monitor, but laptop does, so there goes the screenSize.
By break it down like this, there will be no overlap variable and also improve the reusability. Example, Product can be reuse for other hardware like mouse, keyboard, speaker and etc. For Computer, it can be reuse by things like tablet as well, not just laptop
Actually I don’t really understand the second reason yet, so I need to read more https://www.hackingwithswift.com/quick-start/understanding-swift/when-should-we-use-protocol-inheritance
=======================
Extensions
Extensions allow you to add methods to existing types, to make them do things they weren’t originally designed to do.
extension Int {    func squared() -> Int {        return self * self    } }
let number = 8 number.squared() //and you will get 64
Swift doesn’t let you add stored properties in extensions, so you must use computed properties instead.
extension Int {    var isEven: Bool {        return self % 2 == 0    } }
The purpose of extensions, definitely need to read more, my head now still spinning https://www.hackingwithswift.com/quick-start/understanding-swift/when-should-you-use-extensions-in-swift
=====================
Protocols Extensions
let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"] let beatles = Set(["John", "Paul", "George", "Ringo"])
Swift’s arrays and sets both conform to a protocol called Collection, so we can write an extension to that protocol to add a summarize() method to print the collection neatly
extension Collection {    func summarize() {        print("There are \(count) of us:")
       for name in self {            print(name)        }    } }
pythons.summarize() beatles.summarize()
Bottomline...understand protocol-oriented programming language will take some time
===================
Protocol-oriented programming
First, here’s a protocol called Identifiable that requires any conforming type to have an id property and an identify() method:
protocol Identifiable {    var id: String { get set }    func identify() }
extension Identifiable {    func identify() {        print("My ID is \(id).")    } } struct User: Identifiable {    var id: String }
let twostraws = User(id: "twostraws") twostraws.identify()
So protocol is the empty shell, with extension, we added the default code for identify()
Object Oriented vs Protocol Oriented https://www.hackingwithswift.com/quick-start/understanding-swift/how-is-protocol-oriented-programming-different-from-object-oriented-programming
Still too deep for me until I put it to practice, which the author to suggest us to go through wwdc videos for this as well ==================
The summary by the author , which I need to re-read it over and over again
1. Protocols describe what methods and properties a conforming type must have, but don’t provide the implementations of those methods. 2. You can build protocols on top of other protocols, similar to classes. 3. Extensions let you add methods and computed properties to specific types such as Int. 4. Protocol extensions let you add methods and computed properties to protocols. 5. Protocol-oriented programming is the practice of designing your app architecture as a series of protocols, then using protocol extensions to provide default method implementations.
0 notes