swift初心者の15パターン


SWIFT初心者向け15パターンの記事を読み、整理します.
Optional Unwrapping
var optional1: Int?
var optional2: Int?

// 1 - Ugly Code
func unwrap() {
  if let o1 = optional1 {
    if let o2 = optional2 {
      // reached
    }
  }
}

// 2 - Clean Code
func unwrap() {
  guard let o1 = optional1, let o2 = optional2 else { return }
  // reached
}
2番目の符号化では、1行のコードは、2つの変数の値を一度に分岐して処理することができ、そのうちの1つの変数の値がnilである場合、返される.
さんこうえんざんし
let myBool = true
let testBool = myBool ? false : true
let testInt = myBool ? 16 : 8
3つの演算子を使用して、true、falseを1行で処理する際の条件論理を使用します.
この場合、3つの演算子は重ね合わせとして使用できますが、可読性が低下する可能性があります.
// 2번의 중첩사용
let ternaryDouble = conditional ? true : (conditional2 ? true : false)
//3번의 중첩사용
let ternaryTriple = conditional ? true : (conditional2 ? true : (conditional3 ? true : false))
Generic
JENICプログラミングでは,最小の仮定に基づいて関数とコードを記述するため,抽象を許容することで誤りを低減し,良好なコードを生成する.
var strings = ["Hello", "This", "Is", "A", "Test"]
var integers = [1, 2, 3, 4, 5, 6, 7]

// 1 - Ugly Code
func printStrings(_ array: [String]) {
  for s in array {
    print(s)  
  }
}

func printIntegers(_ array: [Int]) {
  for i in array {
    print(i)  
  }
}

// 2 - Clean Code
func printArray<T>(_ array: [T]) {
  for item in array {
     print(element)  
  }
}
2つの方法で符号化を行う場合、printArray()に基づいて2つのアレイの出力を一度に処理することができる.すなわち、異なるタイプのデータを1つの関数で制御することができる.
Hex CodeからUI色を取得
import UIKit

extension UIColor {
    
    convenience init(hex:Int, alpha: CGFloat = 1.0) {
        self.init(
            red:   CGFloat((hex & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((hex & 0x00FF00) >> 8)  / 255.0,
            blue:  CGFloat((hex & 0x0000FF) >> 0)  / 255.0,
            alpha: alpha
        )
    }
    
}
前述したように、extensionを使用してhex値をUIcolor値とすることができます.
(カスタムカラーを使用する場合、より使いやすいです.)
Extension +
符号化を行う方法の1つは、一般的なモードを関数、クラスに設定することである.
import UIKit

extension UIButton {
    
    func press(completion: @escaping() -> ()) {
        UIView.animate(withDuration: 0.125, delay: 0, options: [.curveEaseIn], animations: {
            self.transform = CGAffineTransform(scaleX: 0.94, y: 0.94)
        }, completion: { _ in
            UIView.animate(withDuration: 0.125, delay: 0, options: [.curveEaseIn], animations: {
                self.transform = .identity
            }, completion: { _ in
                completion()
            })
        })
    }
    
}
たとえば、アニメーション効果のあるボタンを複数回使用する場合は、UI Buttonを拡張してアニメーション関数を作成できます.
let myButton = UIButton()
myButton.press() {
 //handle completion
}
上記のコードに示すように、必要に応じて呼び出せばよい.
複数の場所で呼び出されたバックエンド関数のセットをクラスにする
コードを記述するときに同じ関数を複数の場所で呼び出す場合は、クラスを1つの場所に集約する関数を記述することが望ましい.(集約関数:Funnels関数)
// 1 - Bad Code
class MyClass1 {
  init() {
    FAB.updateData() 
  } 
}

class MyClass2 {
  init() {
    FAB.updateData() 
  } 
}

class MyClass3 {
  init() {
    FAB.updateData() 
  } 
}

// 2 - Good Code
// Network 클래스를 통해 한 곳에 모아주도록 
class Network {
  func updateData() {
    FAB.updateData() 
  }
}

class MyClass1 {
  init() {
    Network.updateData() 
  } 
}

class MyClass2 {
  init() {
    Network.updateData() 
  } 
}

class MyClass3 {
  init() {
    Network.updateData() 
  } 
}
このようにコードを記述する場合、firebaseを変更しようとすると、firebaseを呼び出す3つの場所でコードを変更する必要がありますが、2番目の場所ではNetworkクラス内の1つの場所を変更するだけでいいです.
Guard let
Guard文を使用すると、パッケージ値を取り消すだけでなく、条件論理をより簡単にすることができます.
func checkTheText() {

  guard let text = textField.text else {
    return 
  }
  
  //we made it this far... now we can use text for something!
  updateLabel(text)

}

func conditionalCheck() {
    let c1 = true
    let c2 = false
    let c3 = true
    let c4 = true
    
    guard c1, c2, c3, c4 else {
        return
    }
}

func validatePassword(_ password: String) -> Bool {

    guard password.count >= 8 else { return false }
    guard password.count <= 15 else { return false }
    guard checkPasswordCharacters(password) else { return false } //must contain capital letter, special character, etc...
    
    //password is valid
    return true
    
}
複文
重複文の場合、重複コードを減らす最も効果的で強力なコードです.
// for loop (less than equal than)
for d in 0..<5 {
    print(d) //output: 0 1 2 3 4
}

// for loop (stride, reversed)
for k in stride(from: 3, to: 0, by: -1) {
    print(k) //output: 3 2 1
}

// for loop (stride)
for g in stride(from: 1, to: 10, by: 3) {
    print(g) //output: 1 4 7
}
Enum
Enumを用いて分岐処理を安定的に行うことができる.
// 1 - Ugly code
var marketShare: Int!
let operatingSystem = "iOS"

switch operatingSystem {
    case "iOS": marketShare = 30
    case "android": marketShare = 45
    case "windows": marketShare = 15
    case "sailfish": marketShare = 8
    case "ubuntu": marketShare = 2
    default: marketShare = 0
}

// 2 - Clean code
enum OS { case iOS, android, windows, sailfish, ubuntu }

var marketShare_: Int!
let operatingSystem_ = OS.iOS

switch operatingSystem_ {
    case .iOS: marketShare_ = 30
    case .android: marketShare_ = 45
    case .windows: marketShare_ = 15
    case .sailfish: marketShare_ = 8
    case .ubuntu: marketShare_ = 2
}
1番では文字列を直接使用して分岐するため、実行時に問題が発生する可能性があります.(開発者のエラー…など)しかし,2つ目の場合,Enumを用いることで,各タイプに安全な分岐処理が得られるため,エラーの値コンパイルを防止できる.
Call Back
コールバックを使用すると、警告が表示された後に完了時の動作を呼び出すことができます.
class Modal {
    func getData(completion: ((_ data: String) -> Void)) {
        let data = "Network data!"
        completion(data)
    }
}

class ViewController: UIViewController {
    let model = Model()
    override func viewDidLoad() {
        super.viewDidLoad()
        model.getData { [weak self] (data: String) in
            self?.updateView(data)
        }
    }
    private func updateView(_ data: String) {
        print(data)
    }
}
Default値
デフォルト値を記入するよりも、きれいに記入したほうがいいです.
// 1 - Ugly Code
var textInput: String?
var text = ""

if let t = textInput { text = t } else {
    text = "Hello World!"
}

// 2 - Clean code
let text_ = textInput ?? "Hello World!"
Constants
関数にグローバルに使用される値がある場合は、この値を定量化して、より読み取り可能なコードを記述できます.
import Foundation

struct Constants {

  struct Colors {
    static let blue = UIColor(hex: 0x111111)
    static let green = UIColor(hex: 0x222222)
    static let red = UIColor(hex: 0x333333)
  }
  
  struct Names {
    static let myName = "Gavin"
    static let myMomsName = "Marie"
    static let myDadsName = "Jake"
    static let mySistersName = "Jennifer"
  }
  
}
上記のコードのように定量化されていれば、コードには以下のように使用することができる.
let myColorPick = Constants.Colors.green
let sistersName = Constants.Names.mySistersName
ARC (Automatic Reference Counting)
この部分はswift正式ドキュメントを確認することをお勧めします.
ARCはswiftのメモリ管理に使用されます.
ARCがどのように初期化され無効化されているかを簡単に見てみましょう.
まずpersonというクラスがあると仮定します.
class Person {
init() { print(“initialized!) }
deinit { print(“deinitialized!) }
}
次に、3つの外部形式の変数を作成し、nil値を指定します.
var ref1: Person? // nil
var ref2: Person? // nil
var ref3: Person? // nil
ref 1にPersonインスタンスを作成し、ref 2とre 3に同じオブジェクトを割り当てると、3つの値は同じ参照値になります.
ref1 = Person() // console output: "initialized!"

ref2 = ref1 // Person
ref3 = ref1 // Person
この場合、次のように2つの値にnil値を割り当てるとどうなりますか?
ref1 = nil
ref2 = nil
最後のref 3にはpersonオブジェクトもあるので、personオブジェクトはメモリに保持されます.
したがって、初期化解除を呼び出すには、最後に3番目のpersonオブジェクトにnil値を指定する必要があります.
ref3 = nil // console output: “deinitialized!”
関数パラメータのデフォルト値の設定
関数パラメータにデフォルト値を設定すると、開発者のミスを減らすことができます.
User Default値をメモリのコード/復号とする
import Foundation

// - represents a single Task
struct TaskItem: Codable {
    var isToggledOn: Bool
    var title: String
    var notes: String
}

// - handles on-device memory retrieval and storage
class MemoryManager {
    
    static var tasks: [TaskItem]! // - static array of TaskItems that currently exists on the device
    private let defaults = UserDefaults.standard // - reference to application's UserDefaults dictionary
    private let DEFAULTS_KEY = "TASK_LIST" // - the key we use to retrieve/save our array of TaskItems
    
    init() {
        MemoryManager.tasks = [TaskItem]()
        retrieveData()
        saveData()
    }
    
    // - decode our array from memory
    private func retrieveData() {
        // - check if an array of TaskItems already exists in memory
        var didFail = false
        if let data = UserDefaults.standard.value(forKey: DEFAULTS_KEY) as? Data {
            if let tasks = try? PropertyListDecoder().decode(Array<TaskItem>.self, from: data) {
                MemoryManager.tasks = tasks
            } else { didFail = true }
        } else { didFail = true }
        
        // - guard statement: if we had a failure then continue
        guard didFail else { return }
        
        // - we had a failure in finding a pre-existing array, create a new array of TaskItems!
        MemoryManager.tasks = [
            TaskItem(isToggledOn: false, title: "task 1", notes: "this is task 1"),
            TaskItem(isToggledOn: false, title: "task 2", notes: "this is task 2"),
            TaskItem(isToggledOn: true, title: "task 3", notes: "this is task 3"),
            TaskItem(isToggledOn: false, title: "task 4", notes: "this is task 4"),
            TaskItem(isToggledOn: false, title: "task 5", notes: "this is task 5"),
            TaskItem(isToggledOn: true, title: "task 6", notes: "this is task 6")
        ]
    }
    
    // - encode our array into memory
    private func saveData() {
        UserDefaults.standard.set(try? PropertyListEncoder().encode(MemoryManager.tasks), forKey: DEFAULTS_KEY)
    }
    
}
コードの上から見ると、Structと宣言されたTaskItemはCodableを継承している.ここでColableを継承することにより、Struct値をJSONのようなシーケンス化形式で符号化/復号することができる.
もう少し下を見るとretrieveData()関数が見えます.ここでguard文とifletを使用して、TaskItems配列のUserDefaultのデフォルト値が存在するかどうかを確認します.
配列が存在しない場合は、新しい配列を作成して割り当てます.
この形式は主にアプリケーションの初期化段階で使用されます.
予め存在するデータがあるかどうかを判断した後、存在しない場合はメモリにデータを入れて保存できます.