본문 바로가기

프로그래밍/iOS,macOS

swift , iOS 기본 사항만 빠르게 살펴보기

배열

var arrays : [타입] = []
var arrays = [타입]()
var arrays : Array<타입> = []
var arrays = [ 값, 값, 값 ]
var arrays = [타입]( repeating:반복할요소, count: 반복횟수 )

값추가
arrays.append( 값 )
arrays += [ 값 ]

 

배열에서 다양한 타입의 값이 저장되어야 하는 경우 타입을 Any 로 지정

 

Enum

c언어처럼 int 가 할당되는 것이아닌 항목자체가 값이다

enum TypeName {
   case one
   case two
}
let value:TypeName = .one
let value = TypeName.one

 

 

특정 값으로 할당

enum TypeName : String {

   case value1  // 값을 할당하지 않으면 타입에 따라 "value1"로 할당됨

   case value2="Value2"

}

 

 

프로퍼티

getter, setter

getter, setter를 위한 프로퍼티는 Computed Property 이며, 실제값이 저장되는 Stored Property 에서 값을 설정하거나 읽기위한 프로퍼티이다.

class Test {
   var _myProperty:Int = 0
   var myProperty: Int {
      get { return _myProperty }
      set { _myProperty = newValue }
   }
}

 

 

읽기만 가능할 경우 get 생략 가능.   

var myProperty: Int { return _myProperty }

 

 

 

프로퍼티 옵저버 

실제값의 변경여부를 판단하므로 Stored property만 적용 가능.

생성자에서 초기화될때는 willSet, didSet 함수가 호출되지 않음

class Test {
   var myProperty: Int = 0 {
      willSet( newValue ) { }
      didSet( oldValue ) { }
   }
}

 

 

lazy Stored Property

해당 프로퍼티에 처음 접근할때 초기화되는 프로퍼티

사용하는 시점에 할당되므로, 내부 멤버에대해 self 접근이 가능하고, 다른 멤버가 할당된 이후에 값을 지정이 가능하므로 불필요한 할당 코드가 줄어들게 된다.

 

 

 

Optional Binding

if let, if var 을 통한 nil 체크와 함께 변수에 값 할당

해당 변수는 if 블록내로 스코프가 제한됨

{
   if let 변수 = Optional변수 {
      // 할당됨
   } else {
      // 할당되지 않음
   }
}

 

guard else

조건이 false 이면 else 구문이 실행되며, guard let 변수는 상위 스코프까지 사용 가능하다.

{
   guard let 변수 = Optional변수 else {
      // 할당되지 않음
      // return, throw 등이 없을 경우 컴파일 에러 발생(assert)
   }
   // 할당됨
}

 

 

 

초기화 제한

init함수에 ?(옵셔널)을 붙이게 되면 Failable Initialize(실패가능한 초기화)로 지정되며, 조건에 의해 실패할 시 nil 을 반환하도록 구성이 가능하다.

init?( value : Int ) {
   guard value > 0 else {
      return nil
   }
   self.value = value;
}

 

 

 

클로저

var closure = { header in body }

 

타입 : ( 인자 목록 ) -> 리턴타입

구현 :

{ (  인자 목록 ) -> 리턴타입 in

   실행 코드 블럭

}

 

클로저 선언으로 타입을 이미 알고 있으므로, 구현시 인자타입과 리턴타입은 생략 가능하다.

var closure : ( String )->Int 

closure = { name in return 0 }

 

인자와 리턴값이 없는 클로저 ()->Void 의 경우 코드 블록만 작성 가능

var closure : ()->Void

closure = { print("Hello World") }

 

 

마지막 인자로 클로저를 사용하는 경우 괄호밖에서 작성 가능

인자가 클로저 하나인 경우 괄호 생략 가능

func test( label value: ()->Void ) {}

test( { 클로저 블럭 })

test() { 클로저 블럭 }

test{ 클로저 블럭 }

 

프로토콜

다른 언어의 인터페이스, 추상 클래스와 비슷

메쏘드 , 프로퍼티 등 상속받은 측에서 구현해야하는 항목들의 구현 요구사항을 기술

protocol MyProtocol {
   // 초기화 명세
   init( 인자 )

   // 프로퍼티 명세
   var value1 : Int { get }
   var value2 : Int { get set }

   // 함수 명세, class를 붙이면 클래스 메쏘드
   func myFunction() -> Void
   class func myStaticFunction()

   // optional 은 필수로 구현해야 하지 않은 항목
   optional public func testFunction()
} 

 

Extension

기존 클래스나 구조체, 프로토콜의 기능을 확장할 때 사용한다.

새로운 기능의 추가만 가능하고, 기존 기능을 오버라이드는 불가능하다.

extention NewExtType : BaseProtocol1, BaseProtocol2 {
   func protocolFunction() {
   }
}

 

 

 

Codable을 사용한 JSON 처리

codable은 swift4에 추가된 데이터 변환을 위한 타입 프로토콜

Codable을 상속받으면 변수명을 키값으로 자동 치환해 준다.

값으로 별도의 enum 타입을 사용하는 경우에도 Codable을 상속받으면

해당 이름으로 치환

enum MessageType : String, Codable {
    case msg
    case noti
}

struct Message: Codable {
   let id: String
   let msg : String
   let type : MessageType
}

=> "id":"value", "msg":"value", "type":"msg"

 

 

디코딩

// 문자열을 객체로 디코딩
let jsonString = "{ \"id\": \"kim\", \"msg\": \"test msg\", \"type\": \"msg\" }"
let jsonData = jsonString.data( using: .utf8 )
let decoder = JSONDecoder()
let message = try? decoder.decode( Message.self, for: jsonData)



// 배열의 경우
let arrays = try decoder.decode( [Type].self , from: jsonData )

 

 

인코딩

let objData = Message( id: "kim", msg:"test msg", type: .msg )
let encoder = JSONEncoder()
encoder.ouputFormatting = [ .prettyPrinted, .sortedKeys ]

if let jsonData = try? encoder.encode( objData ) {
   let jsonString = String( data: jsonData, encoding: .utf8)
}

 

키 이름 변경

구조체의 변수명이 그대로 키 이름으로 사용되었으나 해당 키 이름도 변경가능하다

enum MessageType : String, Codable {
    case msg="message"
    case noti="notification"
}

struct Message: Codable {
   let id: String
   let msg : String
   let type : MessageType

   enum CodingKeys: String, CodingKey {
      case id="id"
      case msg="msg"
      case type="type"
   }
     
}

 

 

디코딩 커스터마이징

디코더는 객체 생성 후 init(from: Decoder) 을 호출하는데, 해당 함수를 구현해 커스터마이징이 가능하다

struct Message: Codable {
    let id: String
    let msg : String
    let type : MessageType
    
    // 디코더, 인코더에서 인식할 키 정의
    enum MessageKeys : String, CodingKey {
        case id="id"
        case msg="msg"
        case type="type"
    }
    
    // 디코더 : json->오브젝트
    init( from decoder: Decoder ) throws {
        let values = try decoder.container( keyedBy: MessageKeys.self )
        
        // 디코딩은 해당키에서 값을 찾아 변수에 입력하게 된다
        id = try values.decode( String.self, forKey: .id )
        msg = try values.decode( String.self, forKey: .msg )
        
        // null 요소의 경우 아래처럼 예외처리 가능
        // type = (try values.decodeIfPresent( MessageType.self, forKey: .type ) )?? .ms
    }
    
    // 인코더 : 오브젝트->json
    func encode( to encoder: Encoder) throws {
        var values = encoder.container( keyedBy: MessageKeys.self )
        
        // 해당 값과 키를 사용해 인코딩 
        values.encode( msg, forKey: .msg )
    }
}

 

 

 

 

 

GCD

DispatchQueue

 

제공되는 큐

메인큐 (Serial) : 메인 쓰레드에서 처리되는 큐

전역큐 (Concurrent) : 전체 시스템에 공유되는 큐로 우선순위에 따라 처리되는 큐

 

종류

Serial : 순차적으로 수행

Concurrent : 병렬로 수행

 

실행 방식

동기(sync) : 작업이 종료될때까지 대기

비동기(async) : 추가 여부만 확인 후 작업 완료 여부는 보장하지 않음

 

큐에 작업 넣기

DispatchQueue.main.async( execute: { 메인큐에서 처리할 비동기 작업블럭 } )

 

클로저 표기에 의해 간단하게

DispatchQueue.global().sync{ 전역 큐에서 처리할 동기 작업블럭 }

DispatchQueue.main.asyncAfter(deadline: .now() + 1 ) { 1초 이후에 메인큐에서 처리할 비동기 작업블럭 }

 

 

커스텀 큐

커스텀큐는 기본적으로 Serial 큐

Concurrent큐는 attribues: .concurrent 

let queue = DispatchQueue( label: "이름")

queue.async { }

 


중요 iOS UI 요소

키보드

// 메인뷰 하단에 위치하기 위한 bottom constraint
@IBOutlet weak var textFieldLayoutBottomConstraint: NSLayoutConstraint!

// 키보드 이벤트 등록
NotificationCenter.default.addObserver( 
    self, 
    selector: #selector( MyViewController.keyboardWillShow ),
    name: UIResponder.keyboardWillShowNotification,
    object: nil )
    
NotificationCenter.default.addObserver(
    self,
    selector: #selector( MyViewController.keyboardWillHide ),
    name: UIResponder.keyboardWillHideNotification,
    object: nil )
    

// 키보드 이벤트 해제
NotificationCenter.default.removeObserver( 
    self, 
    name: UIResponder.keyboardWillShowNotification, 
    object: nil)
    
NotificationCenter.default.removeObserver( 
    self, 
    name: UIResponder.keyboardWillHideNotification, 
    object: nil)


// 이벤트 핸들러
@objc func keyboardWillShow( notification: NSNotification ) {
    changeTextFieldBottomConstraint( notification: notification )
}

@objc func keyboardWillHide( notificaiton: NSNotification ) {
    changeTextFieldBottomConstraint( notification: notification )
}

// text filed bottom constraint 변경
func changeTextFieldBottomConstraint( notification:NSNotification ) {
    let userInfo = notification.userInfo!
    
    let keyboardAnimationDuration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
    let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let convertedFrame = self.view.convert( keyboardFrame from: self.view.window )
    let rawAnimationCurve = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber).uintValue << 16
    let animationCurve = UIView.AnimationOptions( rawValue: UInt( rawAnimationCurve ))
    
    textFieldLayoutBottomConstraint.constant = self.view.bounds.maxY - keyboardFrame.minY
	
    UIView.animate( 
        withDuration: animationDuration, 
        delat: 0.0,
        options: UIView.AnimationOptions( rawValue: UIView.AnimationOptions.beginFromCurrentState.rawValue|animationCurve.rawValue),
        animations: { self.view.layoutIfNeeded() },
        completion: nil)

}

 

NSNotification userInfo 의 키보드 관련 내용

public class let keyboardWillShowNotification: NSNotification.Name
public class let keyboardDidShowNotification: NSNotification.Name
public class let keyboardWillHideNotification: NSNotification.Name
public class let keyboardDidHideNotification: NSNotification.Name
public class let keyboardFrameBeginUserInfoKey: String // NSValue of CGRect
public class let keyboardFrameEndUserInfoKey: String // NSValue of CGRect
public class let keyboardAnimationDurationUserInfoKey: String // NSNumber of double
public class let keyboardAnimationCurveUserInfoKey: String // NSNumber of NSUInteger (UIViewAnimationCurve)
public class let keyboardIsLocalUserInfoKey: String // NSNumber of BOOL

// Like the standard keyboard notifications above, these additional notifications include
// a nil object and begin/end frames of the keyboard in screen coordinates in the userInfo dictionary.
public class let keyboardWillChangeFrameNotification: NSNotification.Name
public class let keyboardDidChangeFrameNotification: NSNotification.Name