[swift2.1] Xcode7.1 Playground 변경사항

Swift 2015. 10. 27. 21:19

Xcode7.1 에서 변경된 Playground 기능에 대해 알아보겠습니다.


1. 파일, 이미지, 컬러를 editor로 드래그해서 객체 리터럴을 생성할 수 있습니다.

  • 리터럴은 플랫폼 특정 타입으로 아래와 같이 변환됩니다.
  • Color 리터럴 -> NSColor 또는 UIColor
  • File 리터럴 -> NSURL
  • Image 리터럴 -> NSImage 또는 UIImage


이미지 리터럴

  • Resources 폴더에서 이미지 파일을 에디터로 드래그 하면 그림과 같은 리터럴 형태로 추가됩니다.

UIImage 객체의 imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) 메소드는 템플릿 형태로 렌더링 된 UIImage를 반환합니다. 검정-투명 으로만 이루어진 이미지에서 black 영역을 UIImageView 의 tintColor 속성을 사용해서 원하는 색으로 변경할수 있습니다.




컬러 리터럴

  • Editor > Insert Color Literal 메뉴를 선택하면 컬러 선택창이 나타나 컬러를 변경할 수 있습니다.




파일 리터럴

  • Resources 폴더의 파일을 드래그하면, 아래와 같이 파일 리터럴 형태로 추가됩니다.






2. XCPlayground 가 제공하는 API가 상당수 변경되었습니다.

  • API들이 전역함수에서 XCPlaygroundPage 클래스의 메소드 형태로 변경되었습니다.

API 변경사항으로 XCPCaptureValue, XCPShowView, XCPSetExecutionShouldContinueIndefinitely, XCPExecutionShouldContinueIndefinitely 함수와 XCPSharedDataDirectoryPath 전역상수는 deprecated 되었음. Xcode 이후 버전에서 제거될 예정.


1) 현재 페이지에 대한 참조는 XCPlaygroundPage.currentPage 를 사용합니다.

import XCPlayground

XCPlaygroundPage.currentPage 


2) 타임라인으로 값을 캡처하려면XCPlaygroundPage.captureValue(_:withIdentifier:) 을 사용합니다.

// XCPCaptureValue("식별자", value: i) : deprecated

for i in 1...10 {
    XCPlaygroundPage.currentPage.captureValue(i, withIdentifier: "식별자")
}  




3) 비동기 코드를 포함할 경우 XCPlaygroundPage.needsIndefiniteExecution 을 사용하세요.

  • XCPlaygroundPage.needsIndefiniteExecution 를 true로 설정하면, 실행흐름이 playground 파일 끝에 도달해도 실행을 중단하지 않고, 대기합니다.
// XCPSetExecutionShouldContinueIndefinitely(true) : deprecated

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true




4) XCPlaygroundPage.liveView 를 설정하면 타임라인에 노출됩니다.

  • liveView 속성에 nil이 아닌 값을 설정하면 .needsIndefiniteExecution 가 true 로 설정됩니다.
  • StackView 를 타임라인에 노출하는 예제입니다.

class StackView: UIView {
    var kElementHeight = CGFloat(50)
    var elements:[Int] = []

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.blackColor()
        super.layer.borderColor = UIColor.blueColor().CGColor
        super.layer.borderWidth = 1
        self.layout()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func layout() {
        self.subviews.map { $0.removeFromSuperview() }

        for i in 0..<self.elements.count {
            let value = self.elements[i]

            let elementLabel = UILabel(frame:CGRectZero)
            elementLabel.backgroundColor = UIColor.greenColor()
            elementLabel.text = "\(value)"
            elementLabel.textAlignment = NSTextAlignment.Center
            elementLabel.font = UIFont.systemFontOfSize(15)
            elementLabel.textColor = UIColor.whiteColor()
            self.addSubview(elementLabel)

            elementLabel.frame = CGRectMake(8, self.frame.size.height-(8+kElementHeight)*CGFloat(i+1), self.frame.size.width-8*2, kElementHeight)
        }
    }

    func push(element:Int) {
        self.elements.append(element)
        self.layout()
    }
    func pop() -> Int? {
        if self.elements.count > 0 {
            let topElement = self.elements.removeLast()
            self.layout()
            return topElement
        } else {
            return nil
        }
    }
}

let bounds = CGRectMake(0, 0, 320, 400)
let view = UIView(frame: bounds)
view.backgroundColor = UIColor.whiteColor()

// liveView 속성을 설정하면, Timeline 에 표시됩니다.
// XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 가 자동으로 설정됩니다.

XCPlaygroundPage.currentPage.liveView = view

let stackView = StackView(frame: CGRectInset(view.bounds, 30, 30))
view.addSubview(stackView)

stackView.push(1)
stackView.push(2)
stackView.push(3)  




5) NSURL 객체인 XCPlaygroundSharedDataDirectoryURL 전역상수가 추가되었습니다.

플랫폼 설정이 OS X 인 경우에만 유효한 상수입니다.

  • XCPlaygroundSharedDataDirectoryURL 는 playground 공유 데이터 디렉토리 경로를 나타냅니다.
  • OS X 에서는 ~/Document 경로에 Shared Playground Data 디렉토리를 수동으로 생성해야 합니다.
  • iOS 플랫폼에서는 playground 파일마다 경로가 다르기 때문에 데이터를 공유할 수 없습니다.






3. XCPlayground는 라이브 뷰로 뷰컨트롤러를 지원합니다.

LiveView 는 XCPlaygroundLiveViewable 프로토콜을 구현하는 객체입니다.

  • UIView, UIViewController 는 디폴트로 XCPlaygroundLiveViewable 프로토콜을 구현합니다.
  • XCPlaygroundLiveViewable 프로토콜 명세는 아래와 같습니다.
public protocol XCPlaygroundLiveViewable {
    public func playgroundLiveViewRepresentation() -> XCPlayground.XCPlaygroundLiveViewRepresentation
}

public enum XCPlaygroundLiveViewRepresentation {
    // 루트뷰를 갖지 않는 최상위뷰 
    case View(UIView)

    // 루트 뷰컨트롤러를 갖지 않는 최상위 뷰컨트롤러 
    case ViewController(UIViewController)
}
  • 위에서 구현했던 StackView 를 활용하여 네비게이션 컨트롤러를 라이브 뷰로 설정하는 예제입니다.
let stackView = StackView(frame: CGRectMake(0, 0, 200, 400))
stackView.push(1)
stackView.push(2)
stackView.push(3)

let contentViewController = UIViewController()
contentViewController.title = "StackView"
contentViewController.view.addSubview(stackView)

let liveViewController = UINavigationController(rootViewController: contentViewController)

XCPlaygroundPage.currentPage.liveView = liveViewController






4. XCPlaygroundPage.currentPage.finishExecution()를 호출해서 프로그래밍적으로 playground 실행을 중단 할 수 있습니다.

  • 이 메서드는 Xcode가 현재 playground 페이지 실행을 중단하도록 지시하여, 적절히 클린업을 수행하고 종료할 수 있도록 합니다.
XCPlaygroundPage.currentPage.finishExecution()




[swift2] Functor 와 Monad

Swift 2015. 10. 25. 02:58

이 포스트는 아래 원문을 참고하여, Functor 와 Monad 에 대해 설명합니다.

출처: Functor and Monad in Swift


1. Functor 란?

Functor 란 map 함수를 지원하는 컨테이너 타입입니다.

  • Array 타입은 map 함수를 지원하기 때문에 Functor 입니다.
  • Array 엘리먼트들을 2 배로 곱하는 예제를 살펴보겠습니다.
var doubledImperative:[Int] = [1, 2, 3]
for number in numbers {
    doubledImperative.append(number * 2)
}
print(doubledImperative)  // 2, 4, 6
  • 단순히 루프문을 사용하여 배열 엘리먼트를 2배하여 새 배열에 담습니다.

Map
  • map 함수를 사용하면 다음과 같이 처리할 수 있습니다.
let numbers = [1, 2, 3]    
let doubledNumbers = numbers.map { $0*2 }
print(doubledNumbers)   // 2, 4, 6
  • 예제에서처럼, map 을 사용하면 Array 엘리먼트를 변환하는 작업의도를 더 명확하게 표현할 수 있습니다.
  • 즉, 어떻게가 아닌 무엇을 달성하려는지 를 더 잘 표현합니다.
  • 함수형 프로그래밍의 장점 중 하나라고도 할수 있습니다.


Optional도 map 적용이 가능한 컨테이너 타입입니다.

  • map은 Array 뿐만 아니라 어떤 컨테이너 타입에도 구현할수 있는 고차원 함수입니다.
  • 값이 있거나 또는 없음을 포장하는 Optional 타입도 해당합니다.

고차원 함수: 파라미터 또는 반환값으로 함수를 전달할수 있는 함수.

let number:Int? = 815  // Optional(815)과 동일함 
let transformedNumber = number.map{ $0*2 }.map{ $0%2 == 0 }
print(nilNumber.map{ $0*2 }.map{ $0%2 == 0 }) // Optional(true)


Optional.map 은 우리를 대신에 nil 을 처리해줍니다.

  • Optional 타입에 map을 사용했을 때 장점은 우리를 대신해 nil 값을 처리준다는 것입니다.
  • 원래 값이 nil 이었다면 map을 적용해도 nil을 반환합니다.
  • 이로인해 연산의 중간과정에서 중첩된 if let을 사용해야하는 번거로움을 피할수 있습니다.
// 원래값이 nil인 경우
let nilNumber:Int? = nil
let transformedNilNumber = nilNumber.map{ $0*2 }.map{ $0%2 == 0 }
print(transformedNilNumber) // nil
  • map을 사용하지 않았다면 다음과 같이 처리했을 것입니다.
let nilNumber:Int? =  nil
var result = false

if let number = nilNumber {
    result = ((number*2) % 2) == 0
}
print(result)  


컨테이너 타입에 따라 map은 조금 다르게 동작합니다.

  • map은 컨테이너 타입에 따라, 의미적으로 조금 다르게 동작한다는 것을 알 수 있습니다.
  • T 타입의 값을 포함하는 컨테이너 타입을 구현할 때, map 메서드의 일반적인 시그니처는 다음과 같습니다.
func map<U>(transformFunction: T -> U) -> Container<U>
  • T 는 현재 컨테이너가 포함하는 엘리먼트 타입.
  • U 는 반환될 컨테이너의 엘리먼트 타입.


커스텀 컨테이너 Container 구조체에 map을 구현해보겠습니다.

  • 예제로, Container 라는 커스텀 컨테이너 타입을 구현해보겠습니다.
struct Container<T> {
    var value:T

    func map<U>(transform: T -> U) -> Container<U> {
        let newContainer = Container<U>(value: transform(self.value))
        return newContainer
    }
}  
  • 우리는 T 타입을 파라미터로 받아, U 타입을 반환하는 함수(transform)를 map 에 제공합니다.
  • map은 내부값에 변환함수를 적용하고 교체된 값을 갖는 다른 컨테이너 인스턴스를 생성하여 반환합니다.
  • 아래와 같이 사용할 수 있습니다.
var container = Container<Int>(value: 5)
var resultContainer = container.map { "\($0 * 10)" }
print(resultContainer.value) // "50"  


또다른 커스텀 컨테이너 Result 타입을 구현해봅시다.

  • Result enum 은 요즘 오픈소스 swift 코드에서 많이 볼수 있는 패턴입니다.
enum Result<T> {
    case Value(T)
    case Error(NSError)
}
  • 몇몇 프로그래밍 언어에서 Either 라고 알려진 타입의 구현입니다.
  • case Error 연관값은 제네릭 대신 NSError 타입으로 연산의 결과를 보고하는데 사용합니다.
  • 개념적으로 Result는 값이 있을 수도 있고, 없을 수도 있는 임의의 타입의 값을 포장하는 Optional과 유사합니다.
  • 이 경우에는 추가적으로 왜 그 값이 없는지도(NSError) 알려줍니다.
  • 아래와 같이 파일에서 컨텐츠를 읽고 Result 객체로 반환하는 함수를 구현해보겠습니다.
func dataWithContentsOfFile(file:String, encoding:NSStringEncoding) -> Result<NSData> {

    do {
        let data  = try NSData(contentsOfFile: NSBundle.mainBundle().pathForResource(file, ofType: nil)!, options: NSDataReadingOptions.DataReadingMapped)
        return Result.Value(data)
    } catch {
        return Result.Error(error as NSError)
    }
}
  • 이 함수는 NSData 값을 담은 Result.Value를 반환하거나, (파일을 읽을 수 없는 경우에는) NSError를 갖는 Result.Error를 반환합니다.


텍스트 파일을 읽어 대문자로 변환하는 작업을 구현해봅시다.

var stringContents:String?

switch data {
    case let .Value(value):
        stringContents = NSString(data: value, encoding: NSUTF8StringEncoding) as? String
    case let .Error(error):
        break
}
let uppercasedContents:String? = stringContents?.uppercaseString
  • 매 단계마다 값이 있는지 검색해야 하기 때문에, 중첩된 if let 와 switch 문을 사용하게 됩니다.


map을 사용하면 이렇게 처리할 수 있습니다.

  • NSData 를 받아서, String 으로 변환하고, 그리고 다시 대문자로(String) 변경하는 과정을 수행합니다.
NSData -> String -> String
  • 일련의 map 변환을 적용하여 아래와 같이 처리할 수 있습니다. (map의 구현에 대해서는 아래에서 다룹니다.)
let data:Result<NSData> = dataWithContentsOfFile("test.txt", encoding: NSUTF8StringEncoding)

let uppercaseContents:Result<String> = data.map { NSString(data: $0, encoding: NSUTF8StringEncoding)! }.map { $0.uppercaseString }
  • Array 에 map을 적용했던 이전 예제와 유사하게 이 코드는 훨씬더 의도가 명확합니다.


어떻게 Result.map 을 구현했을까요?

extension Result {
    func map<U>(f: T -> U) -> Result<U> {
        switch self {
        case let .Value(value):
            return Result<U>.Value(f(value))
        case let .Error(error):
            return Result<U>.Error(error)
        }
    }
}
  • 변환함수 f는 타입 T (NSData)의 값을 받고, 타입 U (String)을 반환합니다.
  • 값이 있으면 단순히 value를 파라미터로 f를 호출하고, 값이 없으면 동일한 error를 갖는 다른 Result.Error를 반환합니다.
  • Result라는 껍질(컨테이너)이 포장하고 있는 알맹이에 f함수를 젹용한다고 생각하면 쉽게 이해할수 있을 것입니다.

요약하면

  • Optional, Array 와 같은 컨테이너 타입에 구현된 map 함수가 무엇을 하는지 알아보았습니다.
  • map 은 변환함수에 의해 변경된 값을 갖는 새 컨테이너를 반환합니다. (알맹이만 변경합니다.)
  • Functor는 map 을 구현하는 타입입니다.
  • Dictionary, 클로져와 같은 타입도 Functor 라고 할 수 있고, map 함수가 어떻게 동작하는지 대략 예측해볼 수 있습니다.




2. Optional 은 Monad 입니다.

  • Monad는 값이 있을 수도 있고 없을 수도 있는 상태를 포장하는 타입입니다. (swift 에서는 Optional)
  • Monad 는 Functor 의 한 유형입니다.


map 의 변환함수의 반환타입이 Result 라면 어떤일이 발생할까요?

  • 위 예에서 변환함수를 다른 값으로 변환하는데 사용했습니다.
  • 만일 변환함수가 새 Result 객체를 반환하게 되면 어떻게 될까요?
  • 마찬가지로 변환함수가 새 Error 에러를 반환하면 어떻게 될까요?
  • 먼저 map 함수 시그니처를 통해 알아봅시다.
func map<U>(f: T -> U) -> Result<U>
  • T는 NSData 이고 U는 Result 입니다. 반환타입 Result를 시그니처에 대입해보면
func map(f: NSData -> Result<String>) -> Result<Result<String>>
  • 반환 값에 Result 가 중첩 되었다는 것에 주목하세요. 우리가 원했던 결과는 아닙니다.


중첩된 Result를 받아 단순 Result로 flatten 하는 함수를 구현해봅시다.

extension Result {
    static func flatten<T>(result:Result<Result<T>>) -> Result<T> {
        switch result {
            case let .Value(innerResult):
                return innerResult
            case let .Error(error):
                return Result<T>.Error(error)
        }
    }
}
  • flatten 함수는 중첩된 내부타입 T를 갖는 Result를 받아, 단순히 Value와 Error의 연관값을 추출해서 단일 Result를 반환합니다.
  • flatten 함수는 다른 문맥에서도 찾을수 있는데, 그중 하나로 배열의 배열을 일차원 배열로 flatten 하는 예입니다.

  • map, flatten 을 결합해서 우리의 Result -> Result 변환에 적용해봅시다.

let stringResult = Result<String>.flatten(data.map { (data:NSData) -> (Result<String>) in
    if let string = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
        return Result<String>.Value(string)
    } else {
        return Result<String>.Error(NSError(domain: "error", code: -1, userInfo: nil))
    }
})


일반적으로 map 함수에 flatten 과정을 적용해 flapMap 또는 flattenMap이라 불립니다.

extension Result {
    func flapMap<U>(f:T -> Result<U>) -> Result<U> {
        return Result.flatten(map(f))
    }
}


Monad 타입은 flapMap 함수와 찰떡궁합 입니다.

  • map이 동작하는 타입은 여기서 보았던 것과 유사한 시그니처를 갖는 flatMap 함수를 구현합니다.
  • 다음에 Functor와 Monad라는 단어를 들으면 두려워하지 맙시다.
  • Functor와 Monad는 단순히 컨테이너 타입에 적용할수 있는 공통 연산을 기술하기 위한 디자인 패턴입니다.




'Swift' 카테고리의 다른 글

[swift2] Core Image Filter 예제  (0) 2015.11.10
[swift2.1] Xcode7.1 Playground 변경사항  (0) 2015.10.27
[swift2] Functor 와 Monad  (0) 2015.10.25
[swift2] 타입변환 연산자 (is, as, as?, as!)  (0) 2015.10.18
[swift] Swift 코딩환경 Playground  (0) 2015.10.05
[swift2] Swift Guard  (0) 2015.09.11

[swift2] 타입변환 연산자 (is, as, as?, as!)

Swift 2015. 10. 18. 02:52

이 글은 애플 Language Reference 문서를 참고하여 작성하였습니다.

  • 4개의 타입캐스팅 연산자가 있습니다.
  • is 연산자, as 연산자, as? 연산자, as! 연산자.


is 연산자

  • 런타임에 표현식이 특정 타입으로 다운캐스팅 될수 있는지 검사합니다.
  • 특정 타입으로 다운캐스팅이 가능하면true, 그렇지 않으면 false를 반환합니다.


as 연산자

  • 컴파일 타임에 업캐스팅 또는 브리징과 같이 타입변환이 항상 성공하는 경우에 타입변환을 수행합니다.
  • 업캐스팅(upcasting)은 중간변수 없이 표현식을 타입의 수퍼타입 인스턴스처럼 사용할수 있게합니다.
func f(any: Any) { print("Function for Any") }
func f(int: Int) { print("Function for Int") }
let x = 10
f(x)
// "Function for Int" 출력

let y: Any = x
f(y)
// "Function for Any" 출력

f(x as Any)
// "Function for Any" 출력
  • 브리징(bridging)은 새 인스턴스를 생서하지 않고도, String과 같은 표준라이브러리 타입 표현식을 NSString과 같은 파운데이션 타입으로 사용할 수 있게합니다.


as? 연산자

  • 표현식을 특정타입으로 조건부 타입변환을 수행합니다.
  • as? 연산자는 특정 타입의 옵셔널을 반환합니다.
  • 타입변환이 성공하면 표현식의 값은 옵셔널 타입으로 랩핑되어 반환됩니다. 실패하면 nil을 반환합니다.
  • 특정 타입으로 타입변환이 실패 또는 성공으로 보장되면 컴파일 타임 에러가 발생합니다.


as! 연산자

  • 표현식을 특정 타입으로 강제 타입변환을 수행합니다.
  • as! 연산자는 옵셔널 타입이 아닌 특정 타입의 값을 반환합니다.
  • 타입변환이 실패하면, 런타임 에러가 발생합니다.
  • x as! T 는 (x as? T)! 과 동일하게 동작합니다.




[아이폰] 글 목록

아이폰/글 목록 2015. 10. 17. 23:01

[iOS9] 3D Touch 시작하기

아이폰 2015. 10. 16. 01:26

애플개발자 라이브러리 Getting Started With 3D Touch를 번역한 글입니다.


iOS 9부터 새 아이폰 모델들의 유저인터페이스에 3차원 개념이 도입되었습니다.

  • 사용자는 앱 기능에 즉시 접근하기 위해 홈스크린 아이콘을 press 할수 있습니다.
  • 앱 안에서는, 컨텐츠를 미리보기하거나 특정 기능에 대한 빠르게 접근하기 위해 뷰를 press 할수 있습니다.


샘플코드를 참고하려면, 다음 Xcode 프로젝트를 다운로드 하세요.


홈스크린 퀵액션

사용자는 앱을 시작하기 위해 아이콘을 탭할 수 있고, 홈스크린을 편집하기 위해 앱아이콘을 터치 앤 홀드할수 있습니다. iPhone 6s, iPhone 6s Plus 에서는 앱 아이콘을 pressing 해서 퀵액션 집합을 볼수 있습니다. 사용자가 퀵액션을 선택하면 앱은 활성화되어 실행되고 app delegate는 퀵액션 메시지를 수신합니다.

가장 좋은 퀵액션은 사용자와 앱과의 인터랙션을 예측가능하게 합니다. iOS 9 SDK는 새 아이폰 모델에서 사용가능한 정적,동적 퀵액션을 정의하는 API들을 제공합니다.

두 타입의 퀵액션 모두 최대 두줄의 텍스트 라인과 옵셔널 아이콘을 노출합니다.


Peek 과 Pop

다양한 강도의 사용자 press 에 반응하도록 뷰컨트롤러들을 활성화할수 있습니다. 사용자가 더 깊이 press 할수록 3단계의 인터렉션이 처리됩니다.

  • 1단계: 컨텐츠 preview 사용가능이 표시됨
  • 2단계: 직접 수행할수 있는 옵션들(peek quick action)과 함께 preview가 보여짐(peek)
  • 3단계: preview 에서 보여졌던 뷰로 네비게이션됨(pop)

peek과 pop를 적용하면 시스템이 다음단계 전환에 대한 압력의 강도를 결정합니다. 사용자는 Settings > Accessibility > 3D Touch 에서 전환에 대해 조정할수 있습니다.


Peek 가용유무 표시
  • 가볍게 누르면, 주변 컨텐츠가 블러처리 되면서 추가컨텐츠 미리보기 가능함이 표시됩니다.



Peek
  • 좀더 깊게 누르면, preview 로 전환됩니다. preview는 일반적으로 더 많은 컨텐츠를 보여주도록 구성됩니다.(그림은 메일앱)
  • 사용자가 이 시점에 터치를 멈추면 peek 은 사라지고, 앱은 이전 상태로 되돌아갑니다.
  • 대신 좀더 깊게 누르면, 시스템이 제공하는 pop 전환을 사용하여 preview가 네비게이션되고, pop 뷰는 앱 루트뷰를 채우고 네비게이션 백버튼이 노출됩니다. (마지막 단계의 pop view 사진은 표시하지 않았음)


Peek quick actions
  • 터치를 멈추지 않고 preview 위쪽으로 스와이프하면, 시스템은 preview 와 연관있는 peek 퀵액션을 노출합니다.
  • 각 peek 퀵액션은 앱에 대한 딥링크입니다.
  • 일단 peek 퀵액션이 화면에 노출되면, 사용자가 터치를 멈추어도 preview 는 퀵액션을 탭 할때까지 화면에 남아있습니다.


웹뷰 내의 링크들에 대한 peek과 pop도 활성화 할수 있습니다. Web View Peek and Pop 를 참고하세요.


Force Properties

  • iOS 9 부터 UITouch 클래스는 3D Touch 커스텀 구현을 지원하기 위한 2개의 새로운 속성인 forcemaximumPossibleForce 를 갖습니다.
  • iOS 디바이스에 최초로, 이 속성들은 앱이 수신하는 UIEvent 객체안에서 터치압력을 탐지하고 반응할수 있게해줍니다.
  • 터치의 force는 매우 동적인 범위를 갖으며 부동소수로 표현됩니다.


접근성과 3D Touch 휴먼인터페이스 가이드라인

  • 모든 사용자가 앱의 피처에 접근할수 있도록 보장하기 위해서, 3D Touch 가용유무에 따라 코드를 브랜치하세요.
  • Checking for 3D Touch Availability 를 참고하세요.

3D Touch 는 3D Touch 디바이스들, 그리고 활성화 되었을 때에만 사용가능합니다. iOS 9 에서 3D 터치는 기본으로 활성화되어 있으며 Setting > Accessibility > 3D Touch 메뉴에서 비활성화 할수 있습니다.

  • 3D Touch 가 사용가능하면, 기능의 장점을 활용하세요. 사용가능하지 않다면, touch and hold 와 같은 대안을 제공하세요.
  • 3D Touch 기능은 VoiceOver를 지원합니다. VoiceOver 를 배우려면, Accessibility Programming Guide for iOS 를 참고하세요.
  • 3D Touch 와 함께 사용가능한 새로운 기능에 대한 가이드는 iOS Human Interface Guidelines 문서의 3D Touch 를 참고하세요.


개발환경

  • Xcode7 은 3D Touch 개발을 지원합니다. Xcode의 모든 디버깅 기능은 새 기능을 구현하는데 사용할 수 있습니다.
  • Xcode7과 함께 3D Touch 를 지원하는 디바이스 상에서 개발해야합니다. 시뮬레이터는 3D Touch를 지원하지 않습니다.
  • 코드로 peek 과 pop 뷰컨트롤러들을 구현해야합니다. 인터페이스 빌더는 3D Touch 전환과 뷰컨트롤러들 설정에 대한 그래피컬한 지원을 하지 않습니다.

  • 모든 기능을 모든 사용자들이 사용할수 있도록 3D Touch 활성화상태와 비활성상태 모두 테스트 해야합니다.

  • 3D Touch 디바이스의 Settings > General > Accessibility > 3D Touch 메뉴에서 비활성화 할수 있습니다.




[swift] Swift 코딩환경 Playground

Swift 2015. 10. 5. 02:12

Playground는 xcode6 부터 지원된 인터랙티브한 swift 코딩환경입니다. 애플 공식문서에서는 Playground 를 다음과 같이 소개하고 있습니다.


  • 프로젝트 전체를 컴파일하지 않고도 코드를 실행하고 결과를 보여주는 인터랙티브한 스위프트 코딩환경.
  • Swift 언어를 학습하고, 앱의 일부분을 프로토타이핑하고, 다른 사람들을 위한 학습환경을 만드는데 활용가능.
  • 알고리즘과 시스템 API를 실험하고 커스텀뷰 구현이 가능함.
  • 학습결과에 리치 코멘트로 노트와 가이드 작성가능.


참고

  • 이글은 Playground_Help 문서를 참고하여 작성했습니다.
  • Playground_Help


1. Playground 에디터 구성

playground 에디터는 아래 그림과 같이 구성되어 있습니다.



  • Source editor 에 타이핑한 결과는 즉시 Results sidebar 에서 확인할수 있습니다.
  • Result sidebar의 Quick Look  버튼을 누르면 팝업으로 결과를 확인할수 있습니다. 



  • Result  버튼을 누르면 Source editor 영역에 결과를 인라인으로 삽입할 수 있습니다. 



  • Console 영역에는 print, NSLog 함수가 출력한 문자열을 확인할수 있습니다.

    View > Show Debug Area 메뉴를 통해 표시.



  • Timeline 영역은 XCPlayground 모듈의 렌더링 결과를 표시합니다.

    View > Assitant Editor > Show Assistant Editor 메뉴를 통해 표시. 



2. 결과뷰 Display 방식 변경하기

  • 인라인으로 삽입된 결과뷰의 Display 방식을 변경할 수 있습니다.
  • 삽입된 결과뷰를 선택하고 오른쪽 클릭을 하면 지원하는 display 방식을 표시합니다.

Lasest Value: 최종 결과만 표시.
Value History: 결과를 리스트로 출력.
Graph: 결과를 그래프로 출력.


3. 보조파일 추가하기

  • playground 파일의 Sources 폴더에 보조코드 파일을 추가 할수 있습니다.
  • Sources 폴더의 swift 파일들은 framework 로 미리 컴파일되어 메인 playground 파일에 module 로 자동으로 import 됩니다.
  • 주의할 점으로 playground 메인파일에서 접근할 수 있도록 클래스, 메서드, 함수, 변수, 프로토콜들은 public 으로 선언되어여야합니다.



4. 리소스 추가하기

  • playground 또는 page 에 이미지, 정적데이터 파일, 사운드와 같은 리소스를 포함할수 있습니다.



5. 커스텀 프레임워크 임포트

  • UIKit 과 같은 시스템 프레임워크 외에 커스텀 프레임워크의 import도 가능합니다.
  • 커스텀 프레임워크를 import 하기 위해서는 프로젝트와 playground 파일이 동일한 워크스페이스에 존재해야합니다.
  • 프로젝트의 워크스페이스가 존재하지 않으면 워크스페이스를 생성하고, playground 파일을 워크스페이스에 포함시킵니다.
  • playground 에서 접근하기 위해서 프로젝트의 클래스, 메서드, 함수, 변수, 프로토콜들은 public 으로 선언되어여야합니다.



6. UIView 렌더링 

  • playground의 멋진 기능 중의 하나로 UIView 를 구현하고 그 렌더링 결과를 확인할 수 있습니다.
let view = UIView(frame: CGRectMake(0,0,320,320))
view.backgroundColor = UIColor.magentaColor()

let label = UILabel(frame: CGRectMake(100, 100, 100, 30))
label.textAlignment = .Center
label.text = "Welcome!"
view.addSubview(label)

code 에 렌더링 결과를 삽입하거나, quick look 으로도 확인이 가능합니다.



7. 비동기 처리

  • playground는 디폴트로 위에서 아래로 코드를 실행하며 흐름을 종료합니다.
  • 비동기 api 통신의 경우 결과를 출력하지 못하고 바로 실행을 종료하게 됩니다.
  • XCPSetExecutionShouldContinueIndefinitely(true)를 상위해서 호출하면 비동기 요청이 완료할 때까지 playground 를 종료하지 않고 대기시킬 수 있습니다.
  • XCPSetExecutionShouldContinueIndefinitely 함수를 사용하려면 XCPlayground 프레임워크를 import 해야합니다.
import XCPlayground

// 비동기 처리하기 

XCPSetExecutionShouldContinueIndefinitely(true)

var imageSearch = "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=girl"

let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var task = session.dataTaskWithURL(NSURL(string: imageSearch)!) {
    (data:NSData?, response:NSURLResponse?, error:NSError?) in
    if let res = response as? NSHTTPURLResponse {
        print("\(res.statusCode)", terminator:"\n")
        print("\(res.allHeaderFields)", terminator:"\n")
    }
    if let data = data {
        if let json = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) {
            print("\(json)")
        }
    }
}
task.resume()


8. 마크다운 사용하기

  • 마크다운 문법을 지원하여 rich한 playground 문서를 작성할 수 있습니다.
  • 코드의 실행결과도 삽입할 수 있기 때문에 시각적인 컨텐츠를 작성할 수 있습니다.
  • swift 주석문 // 과 /* */ 구문에 콜론을 사용하면 마크다운 문법으로 인식합니다.
  • Editor > Show Rendered Markup 메뉴를 통해 마크다운 렌더링 결과를 확인할수 있습니다.
// 1. 마크다운 사용하기

//: This line will have **bold** and *italic* text.

/*:
## Headers of All Sizes

### Lists of Links

- [NSHipster](http://nshipster.com)
- [ASCIIwwdc](http://asciiwwdc.com)
- [SwiftDoc](http://swiftdoc.org)

### Images, Too

![Remote Image](http://nshipster.s3.amazonaws.com/alert.gif)


*Images in the Resources directory can be referenced locally*
*/

렌더링 결과를 확인해 볼까요?



8. 페이지 추가하기

  • playground에 페이지를 추가하여 모듈화된 학습환경을 만들 수 있습니다.

    File > New > Playground Page 메뉴.

  • Page 마다 Sources, Resources 폴더가 생성됩니다.
  • 가장 위의 Page 부터 빠른 순서를 갖으며, 페이지간 네비게이션을 추가할 때 의미를 가지게 됩니다.


9. 페이지간 네비게이션 추가하기

  • rich comment를 사용하여 페이지 간의 네비게이션을 추가할 수 있습니다.
  • 네비게이션은 rich comment 렌더링 모드에서 페이지 링크로 표시됩니다.
  • 3가지 종류의 네비게이션, 다음 페이지, 이전 페이지, 특정 페이지로 이동이 존재하며 아래와 같이 표현합니다.
// 다음 페이지로 이동하며, 마지막 페이지에서는 무시됩니다.
//: [Go to Next Page](@next)

// 이전 페이지로 이동하며, 첫 페이지에서는 무시됩니다.
//: [Go to Previous Page](@previous)

// 특정 페이지로 이동합니다. 공백은 %20으로 표기합니다.  
//: [Go to The End](Page%20Third)  
  • Page Second 에 네비게이션을 추가하고


  • 렌디링 모드에서 다음과 같은 결과를 확인할 수 있습니다.