[swift] Generic(제네릭)


Generic

  • ‘포괄적인’이라는 뜻을 가지고 있다.
  • 타입에 유연하게 대처할 수 있다.
  • 제네릭으로 구현한 기능과 타입은 재사용에 용이하다.
  • 코드의 중복을 줄일 수 있어 가독성이 좋다.




Generic을 사용하지 않았을 때

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let tempA = a
    a = b
    b = tempA
}


위 경우에서는 파라미터 모두 Int형일 경우 문제가 없지만 타입이 다른 경우 사용할 수 없다

그렇기에 다른 타입(예시로 Double, String)일 경우에도 사용하려면 다음과 같이 코드를 작성해야 한다.


func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let tempA = a
    a = b
    b = tempA
}

func swapTwoInts(_ a: inout Double, _ b: inout Double) {
    let tempA = a
    a = b
    b = tempA
}

func swapTwoInts(_ a: inout String, _ b: inout String) {
    let tempA = a
    a = b
    b = tempA
}


이렇게 코드가 굉장히 길어질 수 있기 때문에 Generic을 사용하는 것이다.




Generic Function

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let tempA = a
    a = b
    b = tempA
}


이런식으로 코드를 작성할 경우 ‘<’, ‘>’를 사용하여 안에 타입처럼 사용할 이름(T)을 선언해 주면 그 뒤로 해당 이름을 타입처럼 사용할 수 있다.

여기서 이 T를 Type Parameter라 하는데, T라는 새로운 형식이 생성되는 것이 아닌, 실제 함수가 호출될 때 해당 매개변수의 타입으로 대체되는 Placeholder이다.




Generic Type

제네릭은 함수에서만 사용 가능한 것이 아니라 구조체, 클래스, 열거형 타입에서도 선언할 수 있다.


struct Stack<T> {
    let items: [T] = []

    mutating func push(_ item: T) { ... }
    mutating func pop() -> T { ... }
}


이렇게 제네릭 타입으로 Stack을 선언할 수 있다.




Type Constraints

제네릭 함수와 타입을 사용할 때 특정 클래스의 하위 클래스나, 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약을 둘 수 있다.



Protocol Constraints

func isSameValues<T>(_ a: T, _ b: T) -> Bool {
    return a == b
}


이렇게 선언하면 에러가 난다. == 이란 a와 b의 타입이 Equatable이란 프로토콜을 준수할 때만 사용할 수 있기 때문이다.
따라서 이 때는 T: Equatable 이라는 제약을 줄 수 있다.


func isSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
    return a == b
}



Class Constraints

프로토콜 제약과 똑같지만, 해당 자리에 프로토콜이 아닌 클래스 이름이 오는 것이다.


class Bird { }
class Human { }
class Teacher: Human { }

func printName<T: Human>(_ a: T) { }


이렇게 T: Human 클래스 이름을 작성하면


let bird = Bird.init()
let human = Human.init()
let teacher = Teacher.init()

printName(bird) // Global function 'printName' requires that 'Bird' inherit from 'Human'
printName(human)
printName(teacher)


Human 클래스 인스터인스인 Human, Human 클래스를 상속 받은(Subclass) teacher은 printName이란 제네릭 함수를 싱핼시킬 수 있지만, Human 클래스의 Subclass가 아닌 bird 인스턴스는 실행할 수 없다.

[swift] SwiftUI


SwiftUI

SwiftUI는 IOS, tvOS, macOS, watchOS 등 애플이 제공하는 모든 플랫폼에서 앱의 사용자 인터페이스와 동작을 선언하게 된다.

SwiftUI는 앱의 사용자 인터페이스를 선언하기 위한 뷰, 컨트롤러, 레이아웃을 구조체 형태로 제공하게 된다.

이 프레임워크는 탭이나 제스처, 기타 어떠한 유형의 입력을 앱에 전달하기 위한 이벤트 핸들러와 앱 모델에서 사용자가 보고 상호작용 할 뷰, 컨트롤에 대한 데이터 흐름을 관리하는 도구를 제공한다.




SwiftUI의 View

Views are a function of state, not of a sequence of evnets.


SwiftUI는 선언형 구조이다.
SwiftUI에서의 View는 상태의 함수라고 표현을 한다.

즉, 표현하고자 하는 View, UI의 속성은 ‘상태’로 표현되고 이러한 상태를 함수 형태의 인자로 전달을 하면 세세한 것은 SwiftUI가 알아서 해석을 하여 View로 표현하게 된다.




Property Wrapper

  • @State
  • ObservableObject

dataFlow




SwiftUI의 Data Flow

dataFlow

[swift] Intermediate_02. Firebase


Firebase

Firebase

Firebase란 IOS앱과 같은 모바일 기기 개발 플랫폼이다.



대표적인 Firebase Platforms

  • Firebase Cloud Firestore
  • Firebase Auth
  • Firebase Realtime Database
  • Firebase A/B Testing
  • Firebase Cloud messaging
  • Firebase Remote Config




OAuth

OAuth란?

  • 사용자 인증 방식에 대한 업계 표준
  • ID/PW를 노출하지 않고 OAuth를 사용하는 업체의 API 접근 권한을 위임 받음
  • 기본 개념
    • User Service Provider에 계정을 가지고 있는 사용자
    • Consumer Service Provider의 API(제공 기능)를 사용하려는 서비스 (App, Web 등)
    • Service Provider Oauth를 사용하여 API를 제공하는 서비스
    • Access Token 인증 완료 후 Service Provider의 제공 기능을 이용할 수 있는 권한을 위임받은 인증 키




Firebase 인증

Firebase 인증 절차

firebaseAuth



Firebase 인증 제공 업체

  • 이메일/전화번호
  • 전화
  • Google
  • Play 게임
  • 게임 센터
  • FaceBook
  • Twitter
  • GitHub
  • Yahoo
  • Microsoft
  • Apple
  • 익명

[swift] Intermediate_01. Backend-Frontend


Frontend and Backend

Client-Server Model

Client = 서비스를 사용하는 사용자 혹는 사용자가 사용하는 기기, 인터넷 등을 일컫는 말이다.
Server = 서버, 데이터베이스 등을 말한다.



Frontend and Backend

Frontend = 프로세스의 처음 단계
Backend = 프로세스의 마지막 단계

즉, Frontend는 사용자의 행위(입력, 삭제, 저장 등)를 처리하는 것을 의미한다.

[swift] BASIC_11. Alamofire


Alamofire

Alamofire란

Swift 기반의 HTTP 네트워킹 라이브러리이다. URLSession에 기반한 라이브러리로, 네트워킹 작업을 단순화하고, 네트워킹을 위한 다양한 메서드와 JSON 파싱을 제공한다.



Alamofire 특성

  • 연결가능한 Request, Response 메서드를 제공한다.
  • URL JSON형태의 파라미터 인코딩을 지원한다.
  • 파일 데이터 스트링 멀티파트폼 데이터 등 업로드 기능을 제공한다.
  • HTTP Response 검증을 제공한다.
  • 광범위한 단위 테스트 및 통합 테스트를 보장한다.



URLSession 대신 Alamofire를 사용하는 이유

  • 코드의 간소화, 가독성 측면에서 도움을 준다.
  • 여러 기능을 직접 구축하지 않아도 쉽게 사용할 수 있다.


다음은 URLSession 코드이다.

// 호출 URL 만들기
var components = URLComponents(string: "https://api~~~~~~~~")!
components.queryItems = ["title": "Junhyeok"].map { (key, value) in
  URLQueryItem(name: key, value: value)
}

// Request 생성 및 실행
let request = try! URLRequest(url: components.url!, method: .get)
URLSession.shared.dataTask(with: request) { (data, response, error) in 
  do {
    guard let data = data,
      let response = response as? HTTPURLResponse, (200 ..< 300) ~= response.statusCode, error == nil
      else {
        throw error ?? Error.requestFailed
    }
    let response = try JSONDecoder().decode(Response.self, from: data)
    print("Success \(response)")
  } catch {
    print("Failure: \(error.localizedDescription)")
  }
}


이를 Alamofire를 이용하면 다음과 같이 간소화하여 가독성을 높일 수 있다.

AF.request("https://api~~~~~~~~~~", method: .get, parameters: ["title": "Junhyeok"])
  .validate()
  .responseJSON { response in
    switch response.result {
    case .success(let result):
      debugPrint("success \(result)")
    case .failure(let error):
      debugPrint("Failure \(error)")
    }
  }




Alamofire Request

// 파라미터로 URL과 HTTP 메서드 등을 쉽게 설정할 수 있다.
open func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                         method: HTTPMethod = .get,
                                         parameters: Parameters? = nil,
                                         encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                         headers: HTTPHeaders? = nil,
                                         interceptor: RequestInterceptor? = nil) -> DataRequest




Alamofire HTTP Method

public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
  public static let connect = HTTPMethod(rawValue: "CONNECT")
  public static let delete = HTTPMethod(rawValue: "DELETE")
  public static let get = HTTPMethod(rawValue: "GET")
  public static let head = HTTPMethod(rawValue: "HEAD")
  public static let options = HTTPMethod(rawValue: "OPTIONS")
  public static let patch = HTTPMethod(rawValue: "PATCH")
  public static let post = HTTPMethod(rawValue: "POST")
  public static let put = HTTPMethod(rawValue: "PUT")
  public static let trace = HTTPMethod(rawValue: "TRACE")

  public let rawValue: String

  public init(rawValue: String) {
    self.rawValue = rawValue
  }
}


AF.request("https://httpbin.org/get")
AF.request("https://httpbin.org/post", method: .post)
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)




Alamofire Response

// Response Handler - Unserialized Response
func response(queue: DispatchQueue = .main,
              completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self

// Response Serializer Hander - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
                                                          responseSerializer: Serializer,
                                                          completionHandler: @escapting (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self

//Response Data Handler - Serialized into Data
func responseData(queue: DispatchQueue = .main,
                  dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                  emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                  emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods,
                  completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self

// Response String Handler - Serialized into String
func responseString(queue: DispatchQueue = .main,
                    dataPreprocessor: Datapreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                    encoding: String.Encoding? = nil,
                    emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                    emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods,
                    completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self

// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(queue: DispatchQueue = .main,
                  dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor,
                  emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes,
                  emptyRequestMEthods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods,
                  options: JSONSerialization.ReadingOptions = .allowFragments,
                  completionHandler: @escaping (AFDataResponse<Any>) -> Void) -> Self

// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable>(of type: T.Type = T.self,
                                     queue: DispatchQueue = .main,
                                     dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                     decoder: DataDecoder = JSONDecoder(),
                                     emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                     emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods,
                                     completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self



다음과 같이 사용한다.

AF.request("https://httpbin.org/get").responseJSON { response in
  debugPrint(response)
}