twitter.com/@godrm
facebook.com/godrm
?
?
?
?
?
?
+
+
✱ Main - , , ,
✱ Reusability - DRY, components, generics
✱ Reliability - Exception handling and cleanup
✱ Extensibility
✱ Security - , , ,
✱ Performance - , Lazy Loading, ,
✱ Scalability -
✱ Usability -
- SOLID
1. SRP (Single-Responsibility Principle)
( , , ) .
- .
2. OCP (Open-Close Principle)
, .
.
3. LSP (Liskov Substitution Principle)
( ) .
( ) .
4. DIP (Dependency-Inversion Principle)
. (
, .
5. ISP (Interface-Segregation Principle)
.
SRP
struct InputView {
func readInput() {
print(" .")
let userCoordinate = readLine()
guard let input = userCoordinate else { return }
print(seperateCoordinates(userInput: input))
}
//…
}
struct InputView {
func readInput() -> String {
print(" .")
let userCoordinate = readLine()
guard let input = userCoordinate else { return "" }
return input
}
//…
}
DI
class MessageListViewController: UITableViewController {
private let loader: MessageLoader
init(loader: MessageLoader) {
self.loader = loader
super.init(nibName: nil, bundle: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loader.load { [weak self] messages in
self?.reloadTableView(with: messages)
}
}
}
✱
✱
✱
Interface Protocol Segregation
protocol MyShape {
var resultDescription: String { get }
func calculateOfPosition() -> [MyPoint]
func resultOfMyShape() -> Double
}
struct MyLine: MyShape {
private(set) var pointA = MyPoint()
private(set) var pointB = MyPoint()
func calculateOfPosition() -> [MyPoint] { return [pointA, pointB] }
func resultOfMyShape() -> Double { return calculateOfLength() }
//
var resultDescription: String = {
return " "
}()
}
struct MyPoint: MyShape {
private(set) var x = 0
private(set) var y = 0
func calculateOfPosition() -> [MyPoint] { return [self] }
func resultOfMyShape() -> Double { return 0 }
var resultDescription: String = { return "" }()
}
(before)
Protocol Segregation (after)
protocol MyShape {
func calculateOfPosition() -> [MyPoint]
func resultOfMyShape() -> Double
}
protocol MyDescription {
var resultDescription: String { get }
}
struct MyLine: MyShape, MyDescription {
private(set) var pointA = MyPoint()
private(set) var pointB = MyPoint()
func calculateOfPosition() -> [MyPoint] { return [pointA, pointB] }
func resultOfMyShape() -> Double { return calculateOfLength() }
var resultDescription: String = {
return " "
}()
}
struct MyPoint: MyShape {
private(set) var x = 0
private(set) var y = 0
func calculateOfPosition() -> [MyPoint] { return [self] }
func resultOfMyShape() -> Double { return 0 }
}
Optional
guard let y = x { ... }
//
let image = UIImage(named: "Background") if image == nil {
print("Image not loaded ")
return
}
let size = image!.frame.size.scaled(by: 2.0)
//
let image = UIImage(named: selectedImageName)!
guard let x = x { ... }
//
guard let image = image else {
print("Image not loaded"); return
}
let size = image.size.scaled(by: 2.0)
//
guard let image = UIImage(named: selectedImageName)
else { preconditionFailure("Missing (selectedImageName)
asset") }
IUO
class Company {
let name: String
var ceo: CEO!
init(name: String, ceoName: String) {
self.name = name
self.ceo = CEO(name: ceoName, company: self)
}
}
class CEO {
let name: String
unowned let company: Company
init(name: String, company: Company) {
self.name = name
self.company = company
}
}
let company = Company(name: "HK", ceoName: "Min")
Enum Associated Value
enum JSONData {
case null
case bool(Bool)
case number(Double)
case string(String)
case array([JSONData])
case object([String: JSONData])
var string: String? {
guard case .string(let s) = self else { return nil }
return s
}
var array: [JSONData]? {
guard case .array(let a) = self else { return nil }
return a
}
var object: [String: JSONData]? {
guard case .object(let o) = self else { return nil }
return o
}
}
case bool(boolValue: Bool)
case number(numberValue: Double)X
Enum Error
enum Error: String, Error {
case invalidFormat = " ."
case quit = " ."
case notFound = " ."
}
enum MenuError: Error {
case invalidFormat
case quit
case notFound
var localizedDescription: String {
switch self {
case .invalidInput:
return " ."
case .quit:
return " ."
case .notFound:
return " ."
}
}
}
✱
✱
✱
Type Casting conditionally
var dict: [AnyHashable: Any] = [:]
dict["age"] = 33
if let anyValue = dict[“age"],
anyValue is Int
{
let intValue = anyValue as! Int
//
}
guard let intValue = dict[“age"] as? Int else { return }
let wrappedValue: AnyHashable? = "value" // typed AnyHashable?
let unwrappedValue: AnyHashable = "value" // typed AnyHashable
let optString1 = unwrappedValue as? String // typed String?
let optString2 = wrappedValue as? String // typed String?
let veryOptyString: String???????????? = optString2 // Typed String????????????
let lessOptyString: String??? = veryOptyString // Error!
Lazy Eveluation
internal init(_ seq1: Sequence1, _ seq2: Sequence2)
let sequence = seq1.lazy.flatMap ({
item1 in seq2.lazy.map ({
item2 in (item1, item2)
})
})
_iterator = sequence.makeIterator()
}
✱
✱ (Eager)
✱ lazily, eagerly
guard vs if-else
static func analyzeJSONData(in value: String) throws -> JSONDataCountable {
if value.isEmpty {
throw JSONError.emptyValue
}
guard value.hasPrefix(JSONDataTypePattern.leftBrace) else {
return try makeJSONArray(in: value)
}
return try makeJSONObject(in: value)
}
static func analyzeJSONData(in value: String) throws -> JSONDataCountable {
guard value.count > 0 else {
throw JSONError.emptyValue
}
if value.hasPrefix(JSONDataTypePattern.leftBrace) {
//
return try makeJSONObject(in: value)
}
return try makeJSONArray(in: value)
}
Trailing Closures
let words = sentence.characters.split(separator: " ")
.filter({ $0.count > 4 })
.map({ String($0) })
let nums: [Int] = arguments
.flatMap({ Int($0, radix:10) })
.flatMap({ $0 > 0 ? $0 : nil })
dispatch(after: maxItem + 1) { semaphore.signal() }
DispatchQueue
.global(qos: .default) .asyncAfter(deadline: delay) {
// ...
}
UIView.animate(withDuration: 0.3) {
// ...
}
for value in items.map({ pow($0, 2) }) {
print(value)
}
//
UIView.animate(withDuration: 2.0,
animations: { v.removeFromSuperview() },
completion: { _ in postNotification() } )
?
?
?
?
?
아냐 너 먼저가...아마.. 쉬울꺼야
Best Practices & Styles
✱ Tab vs. Space
✱ Braces Styles
✱ Variable, Function, Class - Naming Rules
✱ Computed Properties vs. Methods
✱ Protocol + Extension + Generic
✱ Value Types vs. Reference Types
1. (intent) .
2. else .
3. .
4. .
5. ( )
6. .
7. 2 .
8. .
9. / / .

Software maintenance is
not 'keep it working like before'.
It is 'keep it being useful in a changing world’
- Jessica Kerr

스위프트를 여행하는 히치하이커를 위한 스타일 안내

  • 1.
  • 2.
  • 3.
  • 4.
    ✱ Main -, , , ✱ Reusability - DRY, components, generics ✱ Reliability - Exception handling and cleanup ✱ Extensibility ✱ Security - , , , ✱ Performance - , Lazy Loading, , ✱ Scalability - ✱ Usability -
  • 5.
    - SOLID 1. SRP(Single-Responsibility Principle) ( , , ) . - . 2. OCP (Open-Close Principle) , . . 3. LSP (Liskov Substitution Principle) ( ) . ( ) . 4. DIP (Dependency-Inversion Principle) . ( , . 5. ISP (Interface-Segregation Principle) .
  • 6.
    SRP struct InputView { funcreadInput() { print(" .") let userCoordinate = readLine() guard let input = userCoordinate else { return } print(seperateCoordinates(userInput: input)) } //… } struct InputView { func readInput() -> String { print(" .") let userCoordinate = readLine() guard let input = userCoordinate else { return "" } return input } //… }
  • 7.
    DI class MessageListViewController: UITableViewController{ private let loader: MessageLoader init(loader: MessageLoader) { self.loader = loader super.init(nibName: nil, bundle: nil) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) loader.load { [weak self] messages in self?.reloadTableView(with: messages) } } } ✱ ✱ ✱
  • 8.
    Interface Protocol Segregation protocolMyShape { var resultDescription: String { get } func calculateOfPosition() -> [MyPoint] func resultOfMyShape() -> Double } struct MyLine: MyShape { private(set) var pointA = MyPoint() private(set) var pointB = MyPoint() func calculateOfPosition() -> [MyPoint] { return [pointA, pointB] } func resultOfMyShape() -> Double { return calculateOfLength() } // var resultDescription: String = { return " " }() } struct MyPoint: MyShape { private(set) var x = 0 private(set) var y = 0 func calculateOfPosition() -> [MyPoint] { return [self] } func resultOfMyShape() -> Double { return 0 } var resultDescription: String = { return "" }() } (before)
  • 9.
    Protocol Segregation (after) protocolMyShape { func calculateOfPosition() -> [MyPoint] func resultOfMyShape() -> Double } protocol MyDescription { var resultDescription: String { get } } struct MyLine: MyShape, MyDescription { private(set) var pointA = MyPoint() private(set) var pointB = MyPoint() func calculateOfPosition() -> [MyPoint] { return [pointA, pointB] } func resultOfMyShape() -> Double { return calculateOfLength() } var resultDescription: String = { return " " }() } struct MyPoint: MyShape { private(set) var x = 0 private(set) var y = 0 func calculateOfPosition() -> [MyPoint] { return [self] } func resultOfMyShape() -> Double { return 0 } }
  • 10.
    Optional guard let y= x { ... } // let image = UIImage(named: "Background") if image == nil { print("Image not loaded ") return } let size = image!.frame.size.scaled(by: 2.0) // let image = UIImage(named: selectedImageName)! guard let x = x { ... } // guard let image = image else { print("Image not loaded"); return } let size = image.size.scaled(by: 2.0) // guard let image = UIImage(named: selectedImageName) else { preconditionFailure("Missing (selectedImageName) asset") }
  • 11.
    IUO class Company { letname: String var ceo: CEO! init(name: String, ceoName: String) { self.name = name self.ceo = CEO(name: ceoName, company: self) } } class CEO { let name: String unowned let company: Company init(name: String, company: Company) { self.name = name self.company = company } } let company = Company(name: "HK", ceoName: "Min")
  • 12.
    Enum Associated Value enumJSONData { case null case bool(Bool) case number(Double) case string(String) case array([JSONData]) case object([String: JSONData]) var string: String? { guard case .string(let s) = self else { return nil } return s } var array: [JSONData]? { guard case .array(let a) = self else { return nil } return a } var object: [String: JSONData]? { guard case .object(let o) = self else { return nil } return o } } case bool(boolValue: Bool) case number(numberValue: Double)X
  • 13.
    Enum Error enum Error:String, Error { case invalidFormat = " ." case quit = " ." case notFound = " ." } enum MenuError: Error { case invalidFormat case quit case notFound var localizedDescription: String { switch self { case .invalidInput: return " ." case .quit: return " ." case .notFound: return " ." } } } ✱ ✱ ✱
  • 14.
    Type Casting conditionally vardict: [AnyHashable: Any] = [:] dict["age"] = 33 if let anyValue = dict[“age"], anyValue is Int { let intValue = anyValue as! Int // } guard let intValue = dict[“age"] as? Int else { return } let wrappedValue: AnyHashable? = "value" // typed AnyHashable? let unwrappedValue: AnyHashable = "value" // typed AnyHashable let optString1 = unwrappedValue as? String // typed String? let optString2 = wrappedValue as? String // typed String? let veryOptyString: String???????????? = optString2 // Typed String???????????? let lessOptyString: String??? = veryOptyString // Error!
  • 15.
    Lazy Eveluation internal init(_seq1: Sequence1, _ seq2: Sequence2) let sequence = seq1.lazy.flatMap ({ item1 in seq2.lazy.map ({ item2 in (item1, item2) }) }) _iterator = sequence.makeIterator() } ✱ ✱ (Eager) ✱ lazily, eagerly
  • 16.
    guard vs if-else staticfunc analyzeJSONData(in value: String) throws -> JSONDataCountable { if value.isEmpty { throw JSONError.emptyValue } guard value.hasPrefix(JSONDataTypePattern.leftBrace) else { return try makeJSONArray(in: value) } return try makeJSONObject(in: value) } static func analyzeJSONData(in value: String) throws -> JSONDataCountable { guard value.count > 0 else { throw JSONError.emptyValue } if value.hasPrefix(JSONDataTypePattern.leftBrace) { // return try makeJSONObject(in: value) } return try makeJSONArray(in: value) }
  • 17.
    Trailing Closures let words= sentence.characters.split(separator: " ") .filter({ $0.count > 4 }) .map({ String($0) }) let nums: [Int] = arguments .flatMap({ Int($0, radix:10) }) .flatMap({ $0 > 0 ? $0 : nil }) dispatch(after: maxItem + 1) { semaphore.signal() } DispatchQueue .global(qos: .default) .asyncAfter(deadline: delay) { // ... } UIView.animate(withDuration: 0.3) { // ... } for value in items.map({ pow($0, 2) }) { print(value) } // UIView.animate(withDuration: 2.0, animations: { v.removeFromSuperview() }, completion: { _ in postNotification() } )
  • 18.
  • 19.
    Best Practices &Styles ✱ Tab vs. Space ✱ Braces Styles ✱ Variable, Function, Class - Naming Rules ✱ Computed Properties vs. Methods ✱ Protocol + Extension + Generic ✱ Value Types vs. Reference Types
  • 20.
    1. (intent) . 2.else . 3. . 4. . 5. ( ) 6. . 7. 2 . 8. . 9. / / .
  • 22.
     Software maintenance is not'keep it working like before'. It is 'keep it being useful in a changing world’ - Jessica Kerr