도서2016.12.27 23:56

초고수 야곰님이 쓴 swift3 책이 출시했습니다. 이번에는 ebook이 아니라 종이책입니다.


제가 베타리딩에 참여하고 서평도 남겼어요. 새로운 경험을 하게 해준 야곰 고마워요~


http://www.yes24.com/24/Goods/34717018?Acode=101






스위프트 프로그래밍
국내도서
저자 : 야곰
출판 : 한빛미디어 2017.01.02
상세보기


신고
Posted by 초프(초보 프로그래머)
Programming/iOS2016.10.25 23:46

앱을 실행해면 전면 광고를 실행해서 보여주게 하였다. 


그러던 중 admob 으로 부터 메일 한통을 받았다.



경고를 받은 앱은 Android 앱이었지만 iOS에서도 동일한 방식으로 나오고 있었다. 


iOS도 수정이 필요한 상황이었다. 그래서 정책을 알아 보기로 했다. 여러 가지 정책이 있어서 모든 것을 다 소개할 수도 없고 내가 필요한 것은 기존 상태를 유지하면서 고칠 수 있는 방법이었다.


https://support.google.com/admob/answer/6201362?hl=ko&ref_topic=2745287


`예기치 않게 실행되는 삽입 광고` > `허용되지 않는 광고 구현의 예: 앱을 열 때 삽입 광고 실행` 


이 경우가 경고를 받았을 때의 경우이다. 


이걸 이제 어떻게 수정해야 할까..



이 방법대로 해보자~!!!


iOS Launch screen 이 나오고 메인 화면이 나오기 전에 광고가 떠야 한다.


그렇다면... AppDelegate 의 didFinishLaunchingWithOptions 에서 띄우면 될것 같아!


1. AppDelegate 에서 전면광고 띄우기


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        interstitial = GADInterstitial(adUnitID: "********")

        let request = GADRequest()
        // Requests test ads on test devices.
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }
}

앱 실행시 광고를 띄우는 코드는 모두 작성되었다. 이제 실행을 해보자~!



음...? 메인화면이 먼저 뜨고 전면광고가 뜬다. 위에 안 되는 경우가 바로 이런 경우이다.


이렇게 하면 정지를 받을게 뻔하다... 이건 아님...



전면광고를 로딩하는 시간 때문에 어쩔 수 없이 메인 화면이 노출되는 것 같다. 그러면... 만족스럽지 못하지만 자주 쓰는 방법으로 페이크 LaunchScreen 을 이용해 보자. 


Launch screen 과 같은 VC를 바로 띄워서 전면고를 보여주면...? 문제가.. 없겠는데?!! 그리고 전면광고를 닫을때 메인 화면으로 이동해주자.


2. Fake launch screen 을 이용하여 전면광고 띄우기


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if let viewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController() {
            window?.rootViewController = viewController
        }

        interstitial = GADInterstitial(adUnitID: "******")
        interstitial?.delegate = self

        let request = GADRequest()
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }

    func interstitialWillDismissScreen(_ ad: GADInterstitial!) {
        if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() {
            window?.rootViewController = viewController
        }
    }
}

이렇게 하면 완벽히 동작할 것이다. 이제 전면광고로 부터 자유로워 질 것이다.


실행을 해보자. 밑에 동영상을 확인해 보자.




우리가 원하는 동작이다!! 와우~!!


그런데 뭔가가... 좀... 부족하다..


전면 광고가 사라지는 애니메이션이 사라졌다. willDismiss에서 rootViewController를 변경해서 그런가 보다..


그렇다고 didDismiss 에서 rootViewController 를 변경하면 애니메이션이 끝나기 전까지 전면광고가 보인다. 이렇게 하면 다음과 같은 경우로 허용되지 않는다.



Launch screen에서 광고를 띄워야 하는 것은 유지되어야 하고 광고가 사라지는 애니메이션이 유지되면서 메인화면이 보여야 한다. 


일단 window의 rootViewController를 변경하면 애니메이션을 유지하지 못한다. rootViewController를 변경하는 방법을 제외한다.


그럼... Launch screen 을 VC 형태로 하는게 아니라 view 로 보여주는건 어떨까? view면 바로 추가하고 제거하면 되지 않을까?


음? 그럴듯하다. 한번 해보자. 생각하면 답이 안 나온다. 일단 삽질하다 보면 답이 나오겠지. 어서 해보자.


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?
    var launchScreenView: UIView?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if let view = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()?.view {
            launchScreenView = view
            view.translatesAutoresizingMaskIntoConstraints = false
            
            if let rootView = window?.rootViewController?.view {
                rootView.addSubview(view)
                
                var constraints = [NSLayoutConstraint]()
                constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]-0-|", options: [], metrics: nil, views: ["view": view])
                constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]-0-|", options: [], metrics: nil, views: ["view": view])
                rootView.addConstraints(constraints)
            }
        }

        interstitial = GADInterstitial(adUnitID: "******")
        interstitial?.delegate = self

        let request = GADRequest()
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }

    func interstitialWillDismissScreen(_ ad: GADInterstitial!) {
        launchScreenView?.removeFromSuperview()
    }
}


후후 코드는 완벽하다.


이제 실행 동영상을 보자.





완벽하게 내가 원했던 동작이다.


축하~ 축하!!



여러분의 광고 수익에 조금이나마 도움이 되었으면 좋겠다.





신고
Posted by 초프(초보 프로그래머)
Programming/iOS2016.10.21 00:41

Swift build를 하다가 컴파일이 끝나지 않고 계속되는 현상을 발견했다.





Appcode로 build 중이었는데 swift의 메모리가 끝없이 계속 증가했다.


의심가는 코드를 제거해 보니 빌드가 잘 된다.


아.. 엄청나다.


의심가는 코드를 Playground로 재작성해보았다.


?? 를 남발해서 그런것으로 보이는데... 이래도 되는건가?

let optionalString: String? = nil

let abc: [String:Int] = [
    "a": Int(optionalString ?? "") ?? 0,
    "B": Int(optionalString ?? "") ?? 0,
    "c": Int(optionalString ?? "") ?? 0,
    "d": Int(optionalString ?? "") ?? 0,
    "e": Int(optionalString ?? "") ?? 0
]
print(abc)


관련링크 : http://stackoverflow.com/questions/26151954/sourcekitservice-consumes-cpu-and-grinds-xcode-to-a-halt

신고
Posted by 초프(초보 프로그래머)
TAG Build, swift
Programming/Backend2016.10.18 23:14

프로젝트도 생성했으니 이제 뭔가 해보자. 


음.. 아무래도 Backend 이고 하니... DB가 있어야 겠다. 많이 쓰는 Mysql 로 해보자.


MySQL 을 사용하려면 MySQL용 Database connector 를 추가해야 한다. 


package.swift 의 dependency로 추가 가능하다.

import PackageDescription
 
let package = Package(
    name: "FirstPerfectProject",
    dependencies: [
        .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2, minor: 0),
        .Package(url:"https://github.com/PerfectlySoft/Perfect-MySQL.git", majorVersion: 2, minor: 0)
    ]
)


Xcode에서 build를 해보자. (xcode에서 실행하는 방법은 이전 글에서 생략했다.... 실수로..)




왜.. 오류지?!


음.. 그럼.. 처음에 했던 swift build를 해보자.

$ swift build
Cloning https://github.com/PerfectlySoft/Perfect-MySQL.git
HEAD is now at 53ef1af Fixes ISS-269 - numRows always returns 0
Resolved version: 2.0.1
Cloning https://github.com/PerfectlySoft/Perfect-mysqlclient.git
HEAD is now at 60a82d5 A little cleanup
Resolved version: 2.0.0
Compile Swift Module 'MySQL' (1 sources)
Compile Swift Module 'PerfectLib' (10 sources)
Compile Swift Module 'PerfectHTTP' (9 sources)
Compile CHTTPParser http_parser.c
Linking CHTTPParser
Compile Swift Module 'PerfectHTTPServer' (5 sources)
Compile Swift Module 'FirstPerfectProject' (1 sources)
Linking ./.build/debug/FirstPerfectProject

오!! 빌드했더니 Perfect-MySQL 을 다운로드 받는다~!


이제 run 할 수 있겠다.  Xcode에서 실행해보자.





또 같은 오류!!!!!!


무엇이 문제일까?  Xcode에서 실행하지 말고 터미널에서 실행해볼까?

$ swift build Compile Swift Module 'MySQL' (1 sources) Compile Swift Module 'PerfectLib' (10 sources) Compile Swift Module 'PerfectHTTP' (9 sources) Compile Swift Module 'PerfectHTTPServer' (5 sources) Compile Swift Module 'FirstPerfectProject' (1 sources) Linking ./.build/debug/FirstPerfectProject

오! MySQL이 빌드 되었다. (역시 내 잘못이 아니야... Xcode.. 망할)


MySQL 이 잘 되는지 확인하기 위해 기존 코드에서 MySQL 을 import 해보자.

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
import MySQL
 
// Create HTTP server.
let server = HTTPServer()
 
// Register your own routes and handlers
var routes = Routes()
routes.add(method: .get, uri: "/", handler: {
        request, response in
        response.setHeader(.contentType, value: "text/html")
        response.appendBody(string: "<html><title>Hello, world!<title><body>Hello, world!</body></html>")
        response.completed()
    }
)
 
// Add the routes to the server.
server.addRoutes(routes)
 
// Set a listen port of 8181
server.serverPort = 8181
 
do {
    // Launch the HTTP server.
    try server.start()
} catch PerfectError.networkError(let err, let msg) {
    print("Network error thrown: \(err) \(msg)")
}

직접 사용하지는 않지만 import 했으니 오류 없이 잘 실행 되어야 겠지? MySQL이 제대로 연결 안 되었다면 오류가 뜰거야. (아마도..)


빌드 후 실행해 보자.

build/debug/FirstPerfectProject
[INFO] Starting HTTP server on 0.0.0.0:8181 with document root ./webroot

오오오오... 성공!


이렇게 package.swift 에 dependency 를 추가하고 빌드만 하면 잘 된다.


그럼 xcode 에서는 왜 안 될까?


Xcode 프로젝트는 구성이 좀 까다롭다. project 파일을 지우지 말고 다시 생성해 보자.

$ swift package generate-xcodeproj
generated: ./FirstPerfectProject.xcodeproj

프로젝트 재생성도 했으니 실행을 해보자~



많은 삽질 끝에 방법을 찾았다.



문서를 읽으면 한번 봤을 내용입니다. Xcode 프로젝트를 생성하고 target이 아닌 project 의 build setting 에 추가로 설정을 해줘야 한다.

Library Search Paths 에 $(PROJECT_DIR) recursive 로 설정한다.



이제 빌드하면 성공적으로 완료한다. 


문제는 이게 dependency 를 추가하고 xcode project를 생성할 때마다 설정해줘야 한다는 것이다.

왜 저 설정을 default로 넣지 않는지 모르겠다..


잊지 말자 $(PROJECT_DIR)



dependency 설정 끝~




Getting Started From Scratch https://www.perfect.org/docs/gettingStartedFromScratch.html


Building with Swift Package Manager https://www.perfect.org/docs/buildingWithSPM.html

신고
Posted by 초프(초보 프로그래머)
Programming/Backend2016.10.15 21:12

Swift 서버 프레임워크가 몇가지 있는데 그중에 가장 먼저 접하게 된 것이 Perfect(https://www.perfect.org) 이다. 처음에는 문서도 부족해서 자세히 알아 볼 기회가 없었다. 오늘 새로운 서버 기능을 추가하기 위해 어떤 프레임워크를 써볼까 하다 갑자기 Perfect가 떠올라 문서를 읽어 보기로 했다.


새로 프로젝트를 시작하는 것은 어려운 일이 아니다. 알고 보니 정말 간단하다. 그 간단한것을 다시 한번  정리해 본다.


Swift3 에 맞춰 Perfect 2가 출시되었다. 이 내용도 Swift3, Perfect2 에 맞춰 작성한다.


새로운 프로젝트를 생성하는 방법에는 크게 2가지가 있다.

두가지 방법도 해보았지만 그래도 역시 처음은 새로 작성하는 것이 좋다고 생각한다. 그래서 그 방법에 따라 작성한다.

프로젝트 폴더를 생성한다. 

$ mkdir FirstPerfectProject
$ cd FirstPerfectProject


Package.swift 파일을 생성한다.

$ vi Package.swift

다음 코드를 붙여 넣는다.

import PackageDescription
 
let package = Package(
    name: "FirstPerfectProject",
    dependencies: [
        .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2, minor: 0)
    ]
)

name 은 프로젝트 이름이며 빌드 후 생성되는 실행파일 명이다.


dependencies 는 다른 라이브러리를 추가하는 부분이다. 아직 예제에 나온 라이브러리들만 추가해 봤음. gradle, cocoapods 를 사용해봤다면 낯설지 않은 부분이다.


이제 소스 파일을 작성할 폴더를 만들고 간단한 코드를 작성해 보자.

$ mkdir Sources
$ echo 'print("Well hi there!")' >> Sources/main.swift

이제 실행 가능한 조건은 모두 충족되었다. 이제 실행을 해보자.

$ swift build
$ .build/debug/MyAwesomeProject

swift build 를 통해서 프로젝트를 빌드한다. 빌드를 하게 되면 .build 폴더가 생기며 실행 파일은 package 의 name으로 생성된다.

빌드를 하면 dependecies 에 설정된 git에서 소스 파일들을 다운로드 받아서 빌드를 하게 된다. 빌드가성공하면 실행해 보자.

$ .build/debug/FirstPerfectProject
Well hi there!

실행이 되었다. 그런데 이건 REST 가 아니네? 물론 테스트를 위한 코드입니다.

이제 REST 를 테스트해보자.  main.swift를 다시 수정하자.

$ vi Sources/main.swift

기존 코드는 지우고 다음 코드를 붙여넣는다. 

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
 
// Create HTTP server.
let server = HTTPServer()
 
// Register your own routes and handlers
var routes = Routes()
routes.add(method: .get, uri: "/", handler: {
        request, response in
        response.setHeader(.contentType, value: "text/html")
        response.appendBody(string: "Hello, world!Hello, world!</body></html>")
        response.completed()
    }
)
 
// Add the routes to the server.
server.addRoutes(routes)
 
// Set a listen port of 8181
server.serverPort = 8181
 
do {
    // Launch the HTTP server.
    try server.start()
} catch PerfectError.networkError(let err, let msg) {
    print("Network error thrown: \(err) \(msg)")
}

1초만에 코드를 다 작성했으니 이제 빌드를 해보자.

$ swift build
$ .build/debug/FirstPerfectProject
[INFO] Starting HTTP server on 0.0.0.0:8181 with document root ./webroot

오..! 서버가 실행됐다! localhost:8181 로 접속해 보자.



오.... 코드에 나와 있는 대로 uri 가 / 일때  Hello, world! 가 출력됐다.



이제 문서를 더 읽고 제대로 만들어 보자!!


https://www.perfect.org/docs/handlingRequests.html


https://github.com/PerfectlySoft/Perfect


문서 내용을 다 읽는데 많은 시간이 걸리지 않는다. 2~3시간이면 모든 내용을 읽을 수 있다.


아직 초기 단계라 아쉬운 부분이 많이 있다. ORM, auth 라든지 여러 부분이 아직 지원되지 않는다.


하지만 없다고 사용 못할 수준은 아니다. 마이크로 프레임워크라고 생각하고 사용하자.


실제 프로젝트에 적용할 수 있을지 없을지... 제가 한번 해보겠습니다.


후후....



오늘 내용이 많이 도움이 되었나요? 

더 자세한 내용은...


당신의 몫



신고
Posted by 초프(초보 프로그래머)
Programming/iOS2015.07.24 00:21
Swift version : 1.2
iOS version : 8.4


Swift 문법도 익숙치 않은데 NSNotificationCenter 사용하려는데 막히는 부분이 많고...

Notificaiton 에서 keyboard 관련 정보 가져오려는데.... 왜이리 오류가 많은지..

다음과 코드가 동작함을 확인하였으니 참고하세요.


 NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)


    func keyboardWillShow(notification: NSNotification) {
        let userInfo = notification.userInfo!

        let beginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
        let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
        let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        let animationOptions = UIViewAnimationOptions((userInfo[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).unsignedLongValue << 16)

        UIView.animateWithDuration(duration, delay: 0.0, options: animationOptions, animations: { () -> Void in
            
        }, completion: nil)
    }


Swift version : 2.0
iOS version : 9.0


animationOptions 를 구하는 방법이 변경되었습니다.


    func keyboardWillShow(notification: NSNotification) {
        let userInfo = notification.userInfo!

        let beginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
        let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
        let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        let animationOptions = UIViewAnimationOptions(rawValue: (userInfo[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).unsignedLongValue)

        UIView.animateWithDuration(duration, delay: 0.0, options: animationOptions, animations: { () -> Void in
            
        }, completion: nil)
    }


신고
Posted by 초프(초보 프로그래머)
TAG ios, swift
Programming/iOS2014.06.20 00:31

변수/상수

변수와 상수는 이름을 가지고 있다. 상수는 값을 변경할 수 없다. 변수는 값을 변경할 수 있다.


변수/상수 선언

변수의 type은 한번 정해지면 변경될 수 없다.

상수는 let 키워드로 선언하고 변수는 var 키워드로 선언한다.

  • let maximumNumberOfLoginAttempts = 10
  • var currentLoginAttempt = 0

한번에 여러 변수/상수를 선업할 수 있다.

  • var x = 0.0, y = 0.0, z = 0.0


타입 지정 (Type Annotations)

변수/상수를 선언할때 type을 명확하게 하기 위해 type을 지정할 수 있다.

welcomeMessage 이름의 변수가 String값을 가지게 된다면 

  • var welcomeMessage: String

(:)이 의미하는건 `... 의 type이 ... 이다` 

즉, welcomeMessage 의 type이 String 이다.

이건 String의 값만 저장할 수 있다는 말이다. nil도 허용되지 않는다.


변수/상수 네이밍

유니코드에 포함된 모든 문자로 네이밍할 수 있다. 즉, 한글도 가능!

  • let π = 3.14159
  • let 你好 = "你好世界"
  • let 개발자 = "초프"

이름에는 수학기호, 화살표, 내부에서 사용하는 유니코드, line-drawing, box-drawing등은 포함할 수 없다. 이름에 수가 포함될 수 있지만 수로 시작할 수 없다. test1234는 가능하지만 1234test는 불가능하다.

한번 선언된 이름으로 다시 선언할 수 없다.

변수는 호환 가능한 type이라면 대입하여 현재 값을 대체할 수 있다.

  • var friendlyWelcome = "Hello!"
  • friendlyWelcome = "Bonjour!"
  • // friendlyWelcome is now "Bonjour!"

상수는 변수와 다르게 한번 정해진 값은 변경할 수 없다. 변경을 시도하면 컴파일 오류가 발생한다.

  • let languageName = "Swift"
  • languageName = "Swift++"
  • // this is a compile-time error - languageName cannot be changed


변수/상수 출력

print, println 전역 함수를 사용하여 값을 출력할 수 있다. print 는 단순 출력이고 println은 출력 후 개행문자를 출력하여 다음줄로 이동한다.

  • println(friendlyWelcome)
  • println("This is a string")

문자열 안에 변수/상수 값을 출력하고 싶으면 변수명을 소괄호() 로 감싸고 앞에 sbackslash(\) 를 붙이면 된다.

  • println("The current value of friendlyWelcome is \(friendlyWelcome)")
  • // prints "The current value of friendlyWelcome is Bonjour!"


주석

C, Objective-C 의 주석과 동일하다. 한줄 주석은 slash(/) 두개로 시작한다.

  • // this is a comment

블럭 주석은 /* 로 시작하여 */ 로 끝난다.

  • /* this is also a comment,
  • but written over multiple lines */

기존 언어와 다른 점은 블럭 주석안에 블럭 주석도 허용한다는 것이다. 가장 바깥에 있는 주석이 적용된다.


Semicolons

다른 많은 언어들과 다르게 세미콜론(;)이 필요하지 않다. 다른 언어들은 명령 한줄이 끝나면 세미콜론으로 줄로 끝을 알려준다. 하지만 Swift에서는 필요없다. 하지만... 원한다면 사용해도 된다. 여러 명령을 한줄에 쓰고 싶다면 써야 한다.

  • let cat = "A"; println(cat)
  • // prints "A"


정수

소수부분이 없고 부호를 포함하는 수이다. 부호를 포함하는 signed(음수,0,양수)와 부호가 없는 unsigned(0, 양수)가 있다.

Swift는 8, 16, 32, 64 bit의 정수를 제공한다. 이 정수들의 네이밍은 C와 유사하다. 예를 들어 8bit unsigned 정수이면 UInt8, 32bit signed 정수는 Int32 이다. 대문자를 구분한다.


정수 범위

각 정수 type에 min, max 속성이 있어 최소,최대 값을 알 수 있다.

  • let minValue = UInt8.min // minValue는 unsigned 정수 최소값인 0이다.
  • let maxValue = UInt8.max // maxValue는 unsigned 정수 최대값인 255이다.


Int

대부분의 경우 정수의 사이즈를 지정하지 않아도 된다. Swift는 추가적으로 Int type을 제공한다. 현재 플랫폼의 native word size와 같은 크기의 정수 type의 크기가 된다.

32bit 플랫폼에서는 Int는 Int32와 같다.

64bit 플랫폼에서는 Int는 Int64와 같다.


UInt

Int와 같이 unsigned int인 UInt도 제공한다. 크기는 Int와 똑같은 방법으로 정해진다.

32bit 플랫폼에서 UInt는 UInt32와 같다.

64bit 플랫폼에서 UInt는 UInt64와 같다.


부동 소수점 수

소수부분이 포함되는 수이다. 3.14159, -273.15

정수보다 더 큰 범위의 수를 나타낼 수 있다. Swift에는 2개의 부동 소수점 수 type을 제공한다.

Double - 64bit 부동 소수점 수. 매우 큰 수, 특별히 정확해야 할때 사용한다.

Float - 32bit 부동 소수점 수. 64bit 의 정확도가 필요 없을때 사용한다.


Type Safety and Type Inference

Swift는 type safe한 언어이다. 값 type을 명확하게 해준다. String인 변수에 Int값을 대입할 수 없다.

Type-checking은 다른 type의 값을 사용하면서 발생할 수 있는 오류를 피하게 해준다. 그렇지만 모든 변수, 상수 선언에 type을 지정해야 하는 것은 아니다. 원한다면 type을 지정하지 않아도 된다. Swift는 알아서 적절한 type을 추론해준다. 

C, Objective-C보다 변수/상수 선언이 간단해졌지만 여전히 명확한 type이 존재한다. type을 지정하는 일을 대신해준다.

type 추론은 변수/상수 초기값을 지정한 선언에서 유용하다. 선언할때 값만 넣어주면 된다. 

예를 들어 type지정 없이 42로 초기화 하면 수처럼 보이기 때문에 Swift는 Int로 추론한다.

  • let meaningOfLife = 42
  • // meaningOfLife is inferred to be of type Int

만약 type 지정없이 부동 소수점 수로 초기화 하면 Double로 추론된다.

  • let pi = 3.14159
  • // pi is inferred to be of type Double

Float이 아닌 항상 Double로 추론된다.

만약 정수와 부동 소수점 수 를 같이 사용한다면 Double로 추론된다.

  • let anotherPi = 3 + 0.14159
  • // anotherPi is also inferred to be of type Double

3은 type이 지정되어 있지 않다. 이 식의 type은 소수부분이 존재하므로 Double로 추론된다.


Numeric Literals

정수는 다음과 같이 쓰여진다.

  • 10진수는 접두사 없음
  • 2진수는 점두사 0b
  • 8진수는 접두사 0o
  • 16진수는 접두사 0x

10진수 정수 17은 다음과 같이 표현될 수 있다.

  • let decimalInteger = 17
  • let binaryInteger = 0b10001
  • let octalInteger = 0o21
  • let hexadecimalInteger = 0x11

부동소수점 수는 10,16진수로 표현 가능하다. 선택적으로 승을 표현할 수 있다. 10진수에는 밑이 10인 대소문자 e, 16진수에는 밑이 2인 대소문자 p로 승을 표현한다.

1.25e2 는 1.25 x 10^2 또는 125.0 이다.
1.25e-2 는 1.25 x 10^(-2) 또는 0.0125 이다.

0xFp2 는 15 x 2^2 또는 60.0 이다.
0xFp-2 는 15 x 2^(-2) 또는 3.75 이다.

아래 3개의 상수는 모두 12.1875 값을 가진다.

  • let decimalDouble = 12.1875
  • let exponentDouble = 1.21875e1
  • let hexadecimalDouble = 0xC.3p0

Numeric literals에는 읽기 쉽게 하기 위해 여분의 형식이 존재한다. 정수, 실수 모두 숫자 앞에 여분의 0 을 붙이거나 밑줄을 포함할 수 있다. 밑줄은 무시되고 앞에 있는 0또한 무시된다.

  • let paddedDouble = 000123.456
  • let oneMillion = 1_000_000
  • let justOverOneMillion = 1_000_000.000_000_1


Numeric Type Conversion

일반적인 경우 정수 변수, 상수에서 음수가 되지 않는다고 해도 Int를 사용해라. 모든 경우 기본 정수 type을 사용하면 플랫폼에 상관없이 동일한 크기의 type이 지정되므로 호환성이 좋아진다. 

  • var numberA : Int8 = 1
  • var numberB : Int16 = 2
  • numberB = numberA
  • numberA = numberB

numberA, numberB 가 모두 정수라고 해도 8bit, 16bit 크기가 다르므로 서로 호환가능하지 않다. 서로 다른 타입이라고 보면 된다. 그래서 b = a, a = b는 컴파일 오류가 발생한다. Int8을 Int16으로 변환할 수 없고 Int16을 Int8로 변한할 수 없기 때문이다.

성능, 메모리 사용, 최적화 등 직접 다뤄야하는 특별한 상황일때만 기본 Int, UInt가 아닌 다른 정수 type을 사용하자.


정수 변환

각 정수 type에 따라 저장될 수 있는 값의 범위가 다르다. 

Int8 : -128 ~ 127
UInt8  : 0 ~ 255

값이 유요한 범위에 맞지 않는다면 컴파일 오류가 발생한다.

  • let cannotBeNegative: UInt8 = -1
  • let tooBig: Int8 = Int8.max + 1

케이스별로 정수변환을 허용해줘야 한다. 이 방법은 변환중 발생할 수 있는 에러를 방지한다.

다른 type으로 변환하기 위해서는 원하는 타입으로 새로운 수를 초기화 해줘야 한다.

  • let twoThousand: UInt16 = 2_000
  • let one: UInt8 = 1
  • let twoThousandAndOne = twoThousand + UInt16(one)

twoThousand + one 은 서로 다른 type이라 계산이 불가능하다. 하지만 UInt16(one)을 해주므로써 one의 값을 UInt16 type의 값으로 새로 만든다. twoThousand 와 type이 같아져서 계산이 되고 twoThousandAndOne은 UInt16으로 추론된다.

SomeType(ofInitialValue)는 Swift에서 변환하는 기본적인 방법이다. UInt16에는 UInt8의 값을 받을 수 있는 초기화가 있어서 기존에 있던 UInt8의 값으로 새로운 UIn16을 만들 수 있다. 여기에 아무 type이나 넣으면 안된다. 초기화에 정해진 type만 가능하다. 


정수와 부동소수점 수 변환

정수와 부동소수점 수 사이에 변환도 정수변환과 같이 명확하게 type을 지정해 줘야 한다.

  • let three = 3
  • let pointOneFourOneFiveNine = 0.14159
  • let pi = Double(three) + pointOneFourOneFiveNine
  • // pi는 3.14159 이 되고 Double type으로 추론된다.

three가 새로운 Double로 만들어 진다. 그럼 pointOneFourOneFieNine과 같은 Double type이 된다. 변환없이는 계산이 허용되지 않는다.

부동소수점 수 에서 정수로 변환도 가능하다. 정수로 변환하면 소수점 부분은 모두 버려진다.

  • let integerPi = Int(pi)
  • // integerPi equals 3, and is inferred to be of type Int


Type 별칭 (Type Aliases)

존재하는 type 을 다른 이름으로 별칭을 정의할 수 있다. C 계열의 typedef와 같다. typealias 키워드를 이용하면 된다.

존재하는 type을 좀더 적절한 이름으로 변경할때 유용하다.

  • typealias AudioSample = UInt16
  • var maxAmplitudeFound = AudioSample.min
  • // maxAmplitudeFound is now 0

AudioSample.min 은 UInt16.min과 같다.


Booleans

Swift에는 기본 Boolean type을 제공하며 Bool이라고 한다.  true, false 값만 가질 수 있다.

  • let orangesAreOrange = true
  • let turnipsAreDelicious = false

bool 값으로 초기화 되서 orangesAreOrange, turnipsAreDelicious 는 모두 Bool로 추론된다.

Bool은 조건문에서 유용하다. 

  • if turnipsAreDelicious {
  • println("Mmm, tasty turnips!")
  • } else {
  • println("Eww, turnips are horrible.")
  • }
  • // prints "Eww, turnips are horrible."

C, Objective-C와 다르게 true,false가 아닌 값이 true,false를 대신할 수 없다. Java와 같이 Bool은 Bool이어야 한다.

  • let i = 1
  • if i {
  • // i는 Bool이 아니라 Int이기 때문에 컴파일 오류가 발생한다.
  • }
  • let i = 1
  • if i == 1 {
  • // == 로 논리 비교를 하였기 때문에 결과는 Bool이 되어서 문제가 없다.
  • }


Tuples

single compound value안에 여러개의 값을 그룹화 시켜준다. 

  • let http404Error = (404, "Not Found")
  • // http404Error is of type (Int, String), and equals (404, "Not Found")

http404Error는 (Int, String) type의 튜플이다.

튜플은 많은 타입을 포함할 수 있다.

튜플의 값들은 변수/상수로 분해될 수 잇다.

  • let (statusCode, statusMessage) = http404Error
  • println("The status code is \(statusCode)")
  • // prints "The status code is 404"
  • println("The status message is \(statusMessage)")
  • // prints "The status message is Not Found"

튜플 중 특정 값만을 원한다면 무시할 부분에는 밑줄(_)을 이용하면 된다.

  • let (justTheStatusCode, _) = http404Error
  • println("The status code is \(justTheStatusCode)")
  • // prints "The status code is 404"

array처럼 각각의 element에 접근할 수 있다. 시작은 0부터이다.

  • println("The status code is \(http404Error.0)")
  • // prints "The status code is 404"
  • println("The status message is \(http404Error.1)")
  • // prints "The status message is Not Found"

각각의 element에 이름을 정의할 수 있다.

  • let http200Status = (statusCode: 200, description: "OK")
  • println("The status code is \(http200Status.statusCode)")
  • // prints "The status code is 200"
  • println("The status message is \(http200Status.description)")
  • // prints "The status message is OK"

함수의 return값으로 쓰기에 유용하다. 하나의 type을 return받는 것 보다 더 많은 유용한 정보를 받을 수 있다.


나머지는.... 다른 곳에 번역이 너무 잘 되어 있어서 이만 한다...

나도 이제 번역된거 봐야지~ ㅋ

신고
Posted by 초프(초보 프로그래머)
TAG ios, swift
Programming/iOS2014.06.18 12:21

개인적으로 공부하기 위해 번역해서 남겨둔다.


Swift에는 2가지 type이 있다.

  • named types
    • 이름이 정해진 type이다.
    • class, struct, enumeration, protocol 를 포함한다.
    • 사용자가 만든 임의의 class이름을 Person 이라고 하면 Person또한 named type이며 user-defined named type이라 한다.
    • standard library에는 array, dictionary 등 많은 named type들이 제공된다.
    • data type은 number, character, string 등의 named type 을 말한다. Swift standard library에 structur로 구현되어 있다. 필요에 따라서 원하는 동작을 extend할 수 있다. (objc에 있는 extend기능을 말한다. swift에도 있음)
  • compound types
    • 이름이 없다.
    • named type과 다른 compound type을 포함한다.
      예) (Int, (Int,Int)) -> named type Int와 compound type(Int,Int)로된 compound type
    • 두가지의 compound type이 있다.
      • function type
      • tuple type


Type Annotation

변수나 식의 타입을 명시적으로 지정한다.

  • let someTuple: (Double, Double) = (3.14159, 2.71828)

someTuple은 (Double, Double)의 tuple type으로 지정되었다.

  • func someFunction(a: Int) { /* ... */ }

parameter a는 Int type으로 지정되었다.


Type Identifier

type identifier는 named type이거나 named/compound type의 별칭(alias)이다.

(C계열의 typedef 와 유사)

  • typealias Point = (Int, Int)

(Int, Int) tuple type을 Point라는 이름으로 type identifier를 지정함.

  • let origin: Point = (0, 0)

하나의 type형태로 사용할 수 있음. 다음과 같은 의미이다.

  • let origin: (Int, Int) = (0, 0)
  • var someValue: ExampleModule.MyType

다른 모듈이나 다른 type에 중첩된 type일 경우 . 을 사용하여 identifier를 이용할 수 있다.


Tuple Type

중괄호 안에서 comma(,)로 구분된 0개 이상의 type list 이다.

ex) (), (Int), (Int, Double) 

tuple type은 함수 return에서 사용한다. 함수에서 2개 이상의 값을 하나의 tuple type로 return할 수 있다.

element name은 colon(:) 다음에 identifier로 되어 있다. 

ex) (name : String, age: Int)

Void 는 비어있는 tuple type이다. ()

하나의 element만 있다면 tuple type이 아닌 element type이 된다.

(Int)는 (Int)가 아니라 Int이다.


Function Type

(->) 로 구분되는 parameter, return type으로 구성된 function, method, closure 의 type을 function type이라 한다.

  • parameter type -> return type

parameter type, return type은 tuple type도 가능하다. 일반 function, method와 같이 0개 이상의 parameter, 0개 이상의 return value을 지원한다. (parameter, return이 일반 function, method와 같다고 보면 됨)

parameter type이 ()이고 return이 있을 경우 auto_closure attribute를 적용할 수 있다.

autoclosure function은 특정 expression을 캡쳐 한다.

  • func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
  • if !condition() {
  • println(message)
  • }
  • }
  • let testNumber = 5
  • simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

(testNumber %2 == 0 이 캡쳐되어 condition이 된다)

가변 인자 매개변수(variadic parameter) 는 parameter type중 가장 마지막에 위치해야 한다. 문법적으로 가변 인자 매개변수는 기본 type name과 그 뒤에 점 세개 (...) 로 구성된다. ex) Int...  
가변 인자 매개변수는 array로 취급된다. Int... 은 Int[] 로 취급된다.

in-out parameter를 사용하기 위해서 parameter type 접두사로 inout keyword를 붙여준다. 가변 인자 매개변수에는 inout keyword를 쓸수 없다.

커리함수(curried function)은 중첩 function type과 같다. 예를 들어 

  • func addTwoNumbers(a: Int)(b: Int) -> Int {
  • return a + b
  • }
  • addTwoNumbers(4)(5) // Returns 9

커리함수의 function types은 오른쪽에서 왼쪽으로 그룹화된다. 예를 들어 Int->Int->Int 는 Int->(Int->Int)이다. parameter로 Int를 받아서 (Int->Int) 라는 함수를 return한다. 


Array Type

named type Array<T>를 사용한다. 문법적으로 간단하게 type 이름 뒤에 대괄호([])를 이용합니다. 

아래 두개의 문법은 동일하다.

  • let someArray: String[] = ["Alex", "Brian", "Dave"]
  • let someArray: Array<String> = ["Alex", "Brian", "Dave"]

someArray[0] 은 index 0인 element 인 "Alex"이다. (다른 언어 array와 동일)


Optional Type

named type Optional<T>를 문법적으로 간단하게 하기 위해 Swift에서는 접미사 (?)를 정의했다. 아래 두개의 문법이 동일하다.

  • var optionalInteger: Int?
  • var optionalInteger: Optional<Int>

Optional<Int>는 선택적으로 Int를 가질 수 있다. type name 과 (?) 사이에는 빈 공간(whitespace)이 있으면 안된다.

Optional<Int>는 None, Some(T) 두가지 경우가 있다. 모든 type이 optional type으로 선언 가능하다. 중괄호()로 범위를 지정하여 (?)를 사용할 수 있다. ex) (Int[])?

만약 초기값을 지정하지 않는다면 기본으로 nil 값이 된다.

Optional은 LogicValue protocol을 따르므로 Boolean 이 된다. optional type T? 가 type T 의 어떠한 값이라도 가진다면 true와 같고 아니라면 false이다.

(!)연산자를 사용하면 optional 을 해제하여 nil 값을 가지고 있으면 runtime error를 발시킨다. 

즉 optional type은 wrap되어 있어 그 자체로 true/false로 나타낼수 있다. 하지만 정확히 값을 표현하고 싶을때 접미사로 (!) 연산자를 사용하면 optional type이 해제되어 실제 값으로 표현되는데 optional type이 아니므로 nil이 존재할 수 없다. 만약 nil이라면 runtime error가 발생한다.

optional expression 에서 연산자를 조건적으로 수행하기 위해 optional chaining, optional binding 을 사용할 수 있다. 만약 값이 nil이라면 실행할 연산자가 없어 runtime error가 발생된다.


Implicitly Unwrapped Optional Type

named type ImplicitylyUnwrappedOptional<T> 를 간단하게 표현하기 위해 접미사로 (!) 연산자가 정의되어 있다. 아래 두개의 정의는 같다.

  • var implicitlyUnwrappedString: String!
  • var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>

implicitlyUnwrappedString은 암시적 unwrapped optional string으로 선언되어 있다. type과 연산자 (!) 사이에는 공백(whitespace)가 없어야 한다.

암시적 unwrapped optional 은 optional을 사용하는 모든 곳에서 사용 가능하다.

선언할때 초기값을 지정하지 않으면 nil값으로 초기화된다.

암시적 unwrapped optional 은 사용할때 자동적으로 unwrapped 된다. 그러므로 unwrap하기 위해 (!)연산자를 사용하지 않아도 된다.

만약 nil값에 암시적 unwrapped optional 을 사용하면 runtime error가 발생한다.

조건적인 수행을 위해 optional chaining을 사용한다. 만약 값이 nil이면 실행할 연산자가 없어 runtim error가 발생된다.


Protocol Composition Type

지정된 protocol들을 각각 따르는 type..... 즉 지정된 protocol을 다 상속받은 protocol이라고 보면된다.

type annotations이나 generic parameters 에 사용된다.

  • protocol<Protocol 1, Protocol 2>

protocol<ProtocolA, ProtocolB, ProtocolC> 는 ProtocolA, ProtoclB, ProtocolC 를 상속받은 이름없는 protocol이다.

protocol<> 비어있는 protocol composition type이며 모든 type들이 protocol<>에 따르고 있다.


Metatype Type

class type 의 metatype 은 접미사로 .Type을 Protocol 의 metatype은 접미사로 .Protocol을 붙인다.

ex) SomeClass.Type , SomeProtocol.Protocol

.self 를 접미사로 붙이면 인스턴스가 아니라 type 자체를 return 한다. SomeClass.self 을 하는 경우 SomeClass의 인스턴스가 아니라 SomeClass 자체를 리턴한다. .dynamicType 은 인스턴스를 return한다.


Type Inheritance Clause

상속에 관한 내용이다. type 상속은 colon(:)과 함께 시작된다. type identifiers 사이에 comma(,)로 구분한다. 

Class type은 하나의 다른 superclass로 부터 상속받고 0개 이상의 protocol을 구현할 수 있다. 선언 중에는 superclass 의 name이 가장 먼저 와야 하며 뒤에 protocols 이 온다. 만약 다른 class로 부터 상속받지 않는다면 대신 protocol로 시작하게 된다.

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {} class SomeClass: FirstProtocol, AnotherProtocol {}

다른 named type들도 상속받거나 protocols들을 구현했을 것이다. protocol type은 다른 protocols로 부터 상속받았을 것이다. 


Type Inference

type을 생략할 경우 Swift는 타입 추론을 사용한다. 예를 들어 var x: Int = 0 을 쓰는대신 type을 생략하여 var x = 0 을 쓸수 있다. 컴파일러는 정확히 x의 값이 Int라고 추론한다.  비슷하게 모든 타입이 다 추론 가능할때 type을 생략할 수 있다. 예를 들어 let dict: Dictionary = ["A":1] 은 Dictionary<String, Int>로 추론한다. 하지만.. let dict : Dictionary = ["A":1, 2:"B"] 는 <String, Int> 와 <Int, String>이 같이 사용되어 추론할 수 없어 컴파일 오류가 발생한다.

type 에 대한 정보는 expression tree 잎에서서 root 로 전달된다. var x: Int = 0 을 예로 들면 먼저 0의 type을 추론하고 이 정보를 root로 전달하게 된다. (변수 x로..)

type 정보가 반대 방향으로 전달 되는 경우도 있다. root 에서 잎(leaves)로... 예를 들면...

  • let e = 2.71828 // The type of e is inferred to be Double.
  • let eFloat: Float = 2.71828 // The type of eFloat is Float.

e는 기본적으로 leaves -> root 방향을 따라 2.71828 type인 double로 결정된다. 

eFloat 는 2.71828이 double이지만 Float으로 지정된다.


iOS Developer Library - The Swift Programming Language - Language Reference - Types

신고
Posted by 초프(초보 프로그래머)