336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


info.plist 내용 코드로 가져오기


설명

안녕하세요. 앱이름, 앱버전, 앱빌드정보등 앱을 만들면서 필수적으로 필요한 정보들이 있습니다. 물론 앱을 만들면서 적절히 활용도 할 수 있습니다. 기본적인 내용은 info.plist 파일에 있는 내용들을 코드로 가져와서 사용하는 부분입니다. 왼쪽에 file list에서 info.plist 파일을 선택한 후 XCode에 오른쪽 상단에 있는 버튼 중에 Comparison이라는 버튼을 클릭 하면 코드 형식으로 info.plist를 볼 수 있습니다. 그 내용을 코드로 가지고 와서 필요한 부분 꺼내서 쓰는 코드 입니다. 세팅값에 따라 가져 올수 있는 정보의 양이 많기 때문에 이 포스트에서는 앱이름, 앱버전, 앱빌드버전 그리고 앱이 지원하는 미니멈SDK버전만 가지고 오겠습니다.


사용환경

* Swift 4.0
* XCode 9.2

코드

옵셔널 형태로 가져오기 때문에 if문을 통해서 옵셔널을 벗겨서 사용을 해야 앱이 튕기지 않고 잘 사용 하실수 있습니다.

if let infoDic: [String: Any] = Bundle.main.infoDictionary {
    if let appName: String = infoDic["CFBundleName"] as? String { print("appName = \(appName)") }
    if let appVersion: String = infoDic["CFBundleShortVersionString"] as? String { print("appVersion = \(appVersion)") }
    if let appBuild: String = infoDic["CFBundleVersion"] as? String { print("appBuild - \(appBuild)") }
    if let minimumOSVersion: String = infoDic["MinimumOSVersion"] as? String { print("minimumOSVesion - \(minimumOSVersion)") }
} else {
    print("Not Info")
}
//출력 결과
appName = 노스모킹
appVersion = 1.0.0
appBuild - 1
minimumOSVesion - 9.0

맞치며

info.plist 파일에 있는 내용은 매우 중요한 정보들이 들어 있습니다. 코드로 가져왔던 앱버전,앱이름등등 사실 앱을 만들면서 코드로 가져와서 사용되는 부분이 극히 적을수 있습니다. 필요한 정보만 가지고 와서 활용을 하면 됩니다. 감사합니다. 틀린점이나 궁금한점 있으시면 댓글 남겨주세요 :)



336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


리뷰 페이지로 이동하기

설명

앱을 평가받는 중요한 요소중 하나인 리뷰입니다. 리뷰를 쓸수 있게 유도하는것도 좋은 방법이고, iRate 라는 오픈소스를 통해 알트 창을 띄어 유도하는 방법도 있습니다. 앱스토어 리뷰 페이지로 이동 시켜주는 코드 입니다. 고유 아이디 부분에 어플 고유 아이디를 삽입 한후 실행 시키면 리뷰 페이지로 이동합니다. 앱 아이디는 아이튠즈 웹페이지에 들어가셔서 주소를 확인하면 뒤에 id부터 ?까지가 앱 아이디 입니다. 개발자 사이트에 나와있는 고유 아이디와 같습니다.


사용환경

* Swift 4.x
* Xcode 9.2

소스코드

YourAppID 부분에 아이튠즈에 나와있는 고유ID를 넣어주시면 됩니다. 출시전에도 앱의 고유아이디는 받아 올 수 있습니다.

if let reviewURL = URL(string: "itms-apps://itunes.apple.com/app/itunes-u/id\(YourAppID)?ls=1&mt=8&action=write-review"), UIApplication.shared.canOpenURL(reviewURL) { // 유효한 URL인지 검사합니다.
    if #available(iOS 10.0, *) { //iOS 10.0부터 URL를 오픈하는 방법이 변경 되었습니다.
        UIApplication.shared.open(reviewURL, options: [:], completionHandler: nil)
    } else {
        UIApplication.shared.openURL(reviewURL)
    }
}

맞치며

앱 개발자로서 좋은 리뷰를 받으면 정말 기분이 좋습니다. 내 앱에 대해서 평가가 안 좋을수도 있지만 리뷰를 통해 많은 분들이 앱이 나아가야 하는 방향등 여러가지를 가르쳐줍니다. 리뷰를 통해 발전된 앱을 유저들에게 제공하는 것도 앱개발자로서 역할인거 같습니다. 틀린점이나 궁금한 점 있으시면 언제든지 댓글로 알려주시면 대답해 드리겠습니다. 감사합니다.



336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


ARC(Automatic Reference Counting) 자동 참조 카운팅


ARC란?

메모리를 모니터링하고 관리해주는 자동참조카운팅(ARC)라는 기능을 제공한다. ARC의 역할은 프로그램에서 사용한 메모리를 추적해서 인스턴스가 사용한 메모리가 더 이상 필요 없는 경우 자동으로 해제 역할을 한다.


ARC 기능

클래스를 정의 한 후에 인스턴스를 생성하게 되면 ARC는 자동으로 그 인스턴스에 대한 정보를 저장할 메모리를 할당한다. 이 메모리는 해당 인스턴스가 가진 속성들의 값들을 비롯한 인스턴스의 타입에 대한 정보를 저장하고 있다. 인스턴스가 해제될 때 ARC는 자동으로 인스턴스가 사용한 메모리를 해제한다. ARC의 동작은 클래스에서만 동작한다. 클래스는 인스턴스를 생성할 때 참조 타입(reference type)으로 생성하기 때문이다. 구조체나 열거형은 값 타입(value type)이기 때문에 ARC입장에서는 메모리를 관리 할 필요가 없다.


인스턴스 생성과 해제

인스턴스의 속성, 상수, 변수들이 각 클래스의 인스턴스에서 몇번 참조되었는가를 계속 트래킹하고 있다가 참조하는 수가 0이 되면 해당 인스턴스를 해제하고 0보다 크면 참조를 계속 유지하는 방식이다.

import UIKit

class MyGrade {
    var score: Int
    init(score: Int) {
        self.score = score
        print("초기화 되었습니다.")
    }
    deinit {
        print("해제 되었습니다")
    }
}

var ref: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.

ref = MyGrade(score: 100) //인스턴스를 생성하고 score에 대한 메모리가 할당한다.
ref = nil //다시 nil값을 할당하여, 인스턴스를 해제하는 코드입니다.

//콜솔에서의 결과
초기화 되었습니다.
해제 되었습니다

Strong reference

클래스의 인스턴스를 다른 클래스의 속성으로 만드는 경우를 스토롱참조라고 한다. 스트롱 참조의 경우는 사용자가 편하고 코드를 간결하게 만들수 있다는 장점이 있다.

import UIKit

class MyGrade {
    var score: Int
    init(score: Int) {
        self.score = score
        print("초기화 되었습니다")
    }
    deinit {
        print("해제 되었습니다")
    }
}

var ref_1: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.
var ref_2: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.
var ref_3: MyGrade? //옵서녈 타입으로 nil값이 할당되어, 메모리가 할당되기 전이다.

ref_1 = MyGrade(score: 100) //인스턴스를 생성하고 score에 대한 메모리가 할당한다.
ref_2 = ref_1 //ref_2에 ref_1를 Strong reference
ref_3 = ref_1 //ref_3에 ref_1를 Strong reference

//순차적으로 nil값을 대입해보면
ref_1 = nil //ref_2, ref_3 메모리가 할당되어 있기 때문에 해제되지 못한다.
ref_2 = nil //ref_3 메모리가 할당되어 있기 때문에 해제되지 못한다.
ref_3 = nil //메모리에 할당된 참조 카운트가 0이되기에 인스턴스가 해제 된다.

//콜솔에서의 결과
초기화 되었습니다.
해제 되었습니다.

하지만 여러 클래스에서 스트롱참조를 사용한다보면 예상하지 못한 문제에 부딪힐수 있다. 문제로는 Strong reference cycle이 있다.

Strong reference cycle

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    var mymember: Friend?
    init(number: Int) {
        self.number = number
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개") //인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3) //인스턴스를 생성하고 number에 대한 메모리가 할당된다.

//?는 옵셔널 타입을 가르킨다면, !는 참조 타입의 속성을 가리킨다.
myFriend!.myClass = myClass //다른 클래스의 인스턴스를 참조시킨다.
myClass!.mymember = myFriend //다른 클래스의 인스턴스를 참조시킨다.

myFriend = nil //다른 클래스의 인스턴스를 참조하고 있기 때문에 메로리가 해제되지 않는다
myClass = nil //다른 클래스의 인스턴스를 참조하고 있기 때문에 메로리가 해제되지 않는다

//콜솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.

서로의 인스턴스를 속성으로 연결할 경우 둘다 속성이 남아있기때문에 메모리에서 해제될수 없다. 이런 현상을 String reference cycle이라고 한다. 이문제의 해결 방법으로는 weak reference와 unknown reference가 있다.


weak reference

위크 참조는 인스턴스를 속성값으로 저장해두지 않기 때문에 ARC가 동작하는데 큰 문제가 없도록 한다.

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    weak var mymember: Friend?
    init(number: Int) {
        self.number = number
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개") //인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3) //인스턴스를 생성하고 number에 대한 메모리가 할당된다.

//?는 옵셔널 타입을 가르킨다면, !는 참조 타입의 속성을 가리킨다.
myFriend!.myClass = myClass //다른 클래스의 인스턴스를 참조시킨다.
myClass!.mymember = myFriend //위크 참조이기에 다른 클래스의 인스턴스를 참조시키지 않는다.

myFriend = nil
myClass = nil

//콜솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.
명아무개이 해제 되었습니다.
3반이 해제 되었습니다.

위크 참조로 속성을 선언하면 “값이 없다”고 설정 할 수 있기 때문에 모든 위크 참조는 옵셔널 타입으로 선언되어야 한다. 위크 참조는 인스턴스의 참조를 저장하지 않기 때문에 위크 참조인 속성이 여전히 다른 인스턴스를 참조하고 있다고 하더라도 해당 인스턴스를 해제할 수 있다.


unknown reference

위크 참조하는 달리 언노운 참조는 항상 값을 갖고 있다고 가정한다.

class Friend {
    let name: String
    var myClass: Class?
    init(name: String) {
        self.name = name
        print("\(name)이 초기화 되었습니다")
    }
    deinit {
        print("\(name)이 해제 되었습니다.")
    }
}

class Class {
    let number: Int
    unowned var mymeber: Friend
    init(number: Int, friend: Friend) {
        self.number = number
        self.mymeber = friend
        print("\(number)반이 초기화 되었습니다.")
    }
    deinit {
        print("\(number)반이 해제 되었습니다.")
    }
}

var myFriend: Friend?
var myClass: Class?

myFriend = Friend(name: "명아무개")//인스턴스를 생성하고 name에 대한 메모리가 할당된다.
myClass = Class(number: 3, friend: myFriend!) //언노운 참조이기에 다른 클래스의 인스턴스를 파라미터로 받아서 초기화 한다.

myFriend = nil

//콘솔에서의 결과
명아무개이 초기화 되었습니다
3반이 초기화 되었습니다.
명아무개이 해제 되었습니다.

언노운 참조는 Friend 이니셜라이즈는 두개의 속성에 대해 파라미터로 값을 받아서 초기화를 해야 한다는 점이다.


맞치며

Swift에서는 참조 타입의 속성에 대해 다른 인스턴스를 얼마나 많이 참조하는가를 카운트해두었다가 그 카운트가 0이 되는, 즉 더는 참조하는 인스턴스가 없다고 판단되는 경우에만 인스턴스를 해제한다. 두개 이상의 클래스들이 서로 간의 참조를 속성값으로 갖고 있는 경우에는 참조 카운터가 0이 되지 못하기 때문에 발생한다. 이러한 문제를 스토롱 참조 사이클이라고 하며, 위크참조와 언노운참조를 통해 문제를 해결 할수 있다.

ARC 자체만으로도 개발자에게 메모리 누수라는 문제를 해결 할수 있는 강력한 도구이다. 하지만 잘 알고 사용을 해야 완벽한 메모리 누수를 잡을 수 있다. 이번 ARC를 공부 및 정리를 하다보니 실전도 중요하지만 이론도 중요하다는 결론이 나온다. 부족한 실력으로 정리를 했지만 도움이 되길 바라면서 틀린점이나 궁금한점이 있으면 댓글 남겨주세요. 감사합니다 :)


출처

가장 쉽게 설명하는 Swift



'iOS 프로그래밍 > Swift' 카테고리의 다른 글

스위프트란? (swift)  (0) 2015.11.19
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


SCTableIndex


설명

섹션을 사용하지 않고 테이블에 들어갈 String의 초성을 가지고 인덱스를 만드는 라이브러리입니다. 섹션 인덱스가 필요 할경우 활용해서 사용하실수 있습니다.


Github 주소

https://github.com/myoungsc/SCTableIndex


스크린샷


작업한 환경

* Swift 4
* XCode 9.1
* iOS 9.0 (Min SDK)

설치방법

다른 CocoaPod 처럼 설치 하시면 됩니다.

1. 터미널에서 해당 프로젝트 디렉토리로 이동한 후 'Pod init'
2. 디렉토리에 있는 PodFile에 (pod 'SCTableIndex')를 추가
3. 터미널에서 'pod install'
<

사용방법

import SCTableIndex

arrItem.sort()
sctbindex.delegate = self
//Use Selector Font, HelveticaNeue-Medium is Normal Font
//sctbindex.initialFont = UIFont(name: "HelveticaNeue-Medium", size: 13)!
//Use Selector initial Text Color, black is Normal Color
//sctbindex.initialTextColor = UIColor(red: 50.0/255.0, green: 50.0/255.0, blue: 50.0/255.0, alpha: 1.0)
//String Array item
sctbindex.setView(arrItem)


//MARK: SCTableIndex Delegate
extension ViewController: SCTableIndexDelegate {
    // Move starting point item that select initial text
    func scTableIndexReturnInitialText(_ strInitial: String, index: Int) {
        tbMain.scrollToRow(at: IndexPath(row: index, section: 0), at: .top, animated: true)
    }
}

라이센스

라이센스는 MIT라이센스를 따릅니다. 앱에 적용하실때는 사용하였다고 명시만 해주시면 됩니다.



'iOS 프로그래밍 > CocoaPod' 카테고리의 다른 글

SCCardView  (0) 2017.12.21
SCWebPreview  (0) 2017.08.30
SCPageControl  (0) 2017.08.28
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


SCCardView


설명

뷰 밑에 있는 카드를 선택하여 상단에 있는 전체뷰를 바꿔주는 controller 입니다. 카드영역은 콜렉션뷰를 사용하였고 위에 내용이 표시되는 부분은 커스텀 하여서 원하는 목록을 넣어서 사용 할수 있습니다.

Github 주소

https://github.com/myoungsc/SCCardView

스크린샷


작업한 환경

* Swift 3.0.1
* XCode 8.3.1
* iOS 9.0 (Min SDK)

설치방법

다른 CocoaPod 처럼 설치 하시면 됩니다.

1. 터미널에서 해당 프로젝트 디렉토리로 이동한 후 'Pod init'
2. 디렉토리에 있는 PodFile에 (pod 'SCCardView')를 추가
3. 터미널에서 'pod install'

사용방법

import SCCardView

// Int index, [String: String] key, Value
// Make dummy data (Necessary key Image Card UI)
let dummyData: [Int: [String: Any]] = [0: ["image": UIImage()!,
                                          "title": "Catholic Church",
                                          "url": "https://github.com/myoungsc/SCCardView/blob/master/Example/SCCardView/Images.xcassets/sc0.imageset/sc0.jpg?raw=true",
                                          "description": "Maybe famous Catholic Churchsadl"],
                                       1: ["image": UIImage(named: "sc1")!,
                                           "title": "Beautiful Sea",
                                           "description": "Beautiful sea anywhere on earth"],
                                       2: ["image": UIImage(named: "sc2")!,
                                          "title": "Famous temple",
                                           "description": "A landscape of famous temple"],
                                       3: ["image": UIImage(named: "sc3")!,
                                          "title": "Pretty Flower",
                                          "description": "Pretty Flower\nphoto  by myoung father"],
                                       4: ["image": UIImage(named: "sc4")!,
                                          "title":"Vast Sky",
                                          "description": "Vast korea sky\nphoto by myoung father"],
                                       5: ["image": UIImage(named: "sc5")!,
                                          "title": "Leaf",
                                          "description":"Leaf anywhere on Korea\nphoto by myoung father"]]

sccard.delegate = self
sccard.cardStyle = .onlyTop
//if cardstyle round cutom
/*
 sccard.cardStyle = .custom
 sccard.initCustomCardStyle([.topLeft, .bottomRight])
 */
sccard.initialViewData(dummyData)

// Set initial value
if let dicSubData: [String: Any] = dummyData[0] {
    if let img: UIImage = dicSubData["image"] as? UIImage,
        let title: String = dicSubData["title"] as? String,
        let des: String = dicSubData["description"] as? String {
        sccardSubImg.image = img
        sccardSubTitle.text = title
        sccardSubDes.text = des
    }
}
// Autolayout bottom card ratio
contBottomDes.constant = sccard.bottomInterval


// Delegate
// Selector Card Click. Required Delegate
func SCCardSelectorCard(_ index: Int, dic: [String: Any]) {
    if let img: UIImage = dic["image"] as? UIImage,
        let title: String = dic["title"] as? String,
        let des: String = dic["description"] as? String {
        sccardSubImg.image = img
        sccardSubTitle.text = title
        sccardSubDes.text = des
    }
}

// Refresh content view from down image. Required Delegate
func SCCardURLIndexRefresh(_ img: UIImage) {
    sccardSubImg.image = img
}

// Card Down Gesture. optional Delegate
internal func SCCardDownCardAction(_ indexPath: IndexPath) {
    print("down gesture \(indexPath)")
}

// Card Up Gesture. optional Delegate
internal func SCCardUpCardAction(_ indexPath: IndexPath) {
    print("up gesture \(indexPath)")
}

라이센스

라이센스는 MIT라이센스를 따릅니다. 앱에 적용하실때는 사용하였다고 명시만 해주시면 됩니다.



'iOS 프로그래밍 > CocoaPod' 카테고리의 다른 글

SCTableIndex  (0) 2017.12.21
SCWebPreview  (0) 2017.08.30
SCPageControl  (0) 2017.08.28
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


위도와 경도로 주소 가져오기

설명

안녕하세요. 오랜만에 포스트를 올리네요. 오늘 할 포스트 내용은 iOS에서 위도와 경도를 가지고 해당 주소를 알아내는 소스코드입니다. 사진을 찍을때 위치정보가 같이 저장된다면 사진첩 어플에서 지도에 사진이 위치별로 표시되는 기능이 있습니다. 사진에 저장된 위치를 기반으로 표현을 하는겁니다. 사진에 있는 위치를 알아오는 법은 나중에 한번더 포스트해서 정리 하겠습니다. 코드 자체는 매우 간단합니다. CLLocation에 해당 위도,경도를 넣어주신 다음에 CLGeocoder 객체를 사용하셔서 받아오시면 됩니다.


사용환경

* Swift4.x
* Xcode 9.1

소스 코드

import CoreLocation

//latitude: 위도, 도: 경도
let findLocation = CLLocation(latitude: 37.576029, longitude: 126.976920)
let geocoder = CLGeocoder()
let locale = Locale(identifier: "Ko-kr") //원하는 언어의 나라 코드를 넣어주시면 됩니다.
geocoder.reverseGeocodeLocation(findLocation, preferredLocale: locale, completionHandler: {(placemarks, error) in
    if let address: [CLPlacemark] = placemarks {
        if let name: String = address.last?.name { print(name) } //전체 주소
    }
})

CLPlacemark에 들어있는 정보들

CLPlacemark에 대한 정보는 밑에 있는 도큐멘트에서 알아 볼 수 있습니다. 필요한 부분만 가져다가 쓰시면 될거 같습니다.

CLPlaceMark에 대한 애플 도큐멘트

맞치며

위치기반 시스템이 들어간 앱이라면 많이 사용 하시는 부분입니다. 포스트를 하면서 드는 생각인데 추가적으로 현재 위치를 가져오는 부분도 추가로 포스팅 작업을 하겠습니다. 틀린점이나 궁금한점 있으시면 언제든지 댓글로 말해주세요 :)



336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


앱스토어 올라가는 스크린샷에 동영상을 추가해서 앱을 설명 할 수 있습니다. 그 동영상이 실제 앱이 구동되는 화면을 가지고 설명되는 동영상이면 상관이 없지만, 앱을 설명하는 동영상에 실제 화면이 포함되지 않는 동영상이 올라갈 경우 사용자에게 명확한 설명이 안되고 추상적이기때문에 애플 측에서 리젝을 합니다.


리젝 사유

Performance - 2.3.4

Your App Preview includes content that does not sufficiently reflect the app in use.
Specifically, your preview:

- Showed footage other than the app in use.

Next Steps

Please revise your App Preview to only use video screen captures of the app,
narration, and textual and design overlays.

해결 방안

동영상을 올릴경우 꼭 전체화면은 아니지만 보여지는 화면에 실제 사용되는 앱의 모습니 있어야 됩니다. 매우 간단한 해결 방안이지만 만약 리젝을 당했던 동영상이라면 동영상을 다시 만들어야 되는 문제가 발생 합니다.


마치며

처음부터 이러한 사실을 알고 있다면 좋겠지만, 보통의 경우 한번당하고 나서 알게됩니다. 직접 동영상을 만들지 않는 것이라면 영상 제작자에게 잘 얘기를 해서 수정해야되는 방향으로 가거나 아예 동영상을 삭제 해야되지 않을까 싶습니다. 리뷰를 맡기면서 느끼는 거지만 요새는 리뷰를 하루면 해주기 때문에 출시하기 일주일전에 개발자가 출시해야되는 옵션을 걸고 리뷰를 맡아 보고나서 출시일에 맞쳐서 앱을 출시 하는 것도 리젝을 피하는 하나의 방법이지 않을까 싶습니다. 틀린점이나 궁금한점 있으면 언제든지 댓글을 남겨 주세요 :)

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


설명

런처이미지 사이즈에 대해서 포스팅 하겠습니다. 3.5인치에서 4인치 화면만 있다가 4.7인치와 5.5인치가 추가될때 런처이미지를 추가 하지 않으면 4.7과 5.5인치에서는 그냥 확대된 상태로만 나오던 적이 있었습니다. 애플에서 설명을 하는 런처이미지는 이렇습니다.

A launch screen appears instantly when your app starts up.
The launch screen is quickly replaced with the first screen of your app,
giving the impression that your app is fast and responsive.
The launch screen isn’t an opportunity for artistic expression.
It’s solely intended to enhance the perception of your app as quick
to launch and immediately ready for use. Every app must supply a launch screen.

앱이 시작되면 즉시 런처이미지가 나타납니다. 런치이미지는 빠르게 앱의 시작화면으로 바뀌어서 빠르게 반응하는 것 같은 인상을 줍니다. 런처이미지는 예술적으로 표현하는 곳이 아닙니다. 다만 앱이 즉시 사용 준비가 된 상태라는 것을 알려주기 위한 용도로만 사용도비니다. 모든 앱에서는 런처이미지를 제공해야됩니다. (직역입니다. 틀린부분이 있을텐데 그냥 넘어가주세요.. 영어공부 열심히 하겠습니다…)

앱이 시작되는 부분에서 나타나는 화면이고, 앱이 사용할 준비가 끝나면서 사라지는 장면이라는 것입니다.


이미지 사이즈 표

Device Portrait size(세로 방향) Landscape size(가로 방향)
12.9” iPad Pro 2048px × 2732px 2732px × 2048px
10.5” iPad Pro 1668px × 2224px 2224px × 1668px
9.7” iPad 1536px × 2048px 2048px × 1536px
7.9” iPad mini 4 1536px × 2048px 2048px × 1536px
iPhone X 1125px × 2436px 2436px × 1125px
iPhone 8 Plus 1242px × 2208px 2208px × 1242px
iPhone 8 750px × 1334px 1334px × 750px
iPhone 7 Plus 1242px × 2208px 2208px × 1242px
iPhone 7 750px × 1334px 1334px × 750px
iPhone 6sPlus 1242px × 2208px 2208px × 1242px
iPhone 6s 750px × 1334px 1334px × 750px
iPhone SE 640px × 1136px 1136px × 640px

맞치며

사실 LaunchScreen.storyboard 여기서 통합적으로 해주면 되기는 합니다. 오토레이아웃도 사용할 수 있고요. 아이폰x에서 탭바가 네이비게이션 이동을 탭바를 사라지게 하는 hidesBottomBarWhenPushed 옵션을 주게 되면 위로 올라갔다가 사라지는 이슈가 있는데,, 아이폰x 런처 이미지를 넣어주면 해결 된다는 글을 본적이 있습니다. (적용을 해봤지만 실패 해서 일단 다른 방법을 사용 하고 있습니다.) 필요로 하면 넣어주는게 맞는거 같습니다. 포스팅에 잘못된점이 있으면 댓글 달아주시면 수정하도록 하겠습니다.



336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


iOS 리젝사유) Apple 소프트웨어 또는 하드웨어에 대한 언급

앱을 업데이트 할때 ‘이 버전에서 업그레이드된 사항’ 이라는 메타데이터 항목이 있습니다. 거기에

  • iOS11 대응 업데이트

라고 적었던 부분이 문제가 되었습니다. 다른 앱들이 업데이트내역을 볼때 ‘iOS11 지원’ 이런식으로 써놨기에 문제가 되지 않는다고 생각했는데 리젝을 맞았습니다.
리젝 사유는 아래와 같습니다.


리젝 사유

Your app or its metadata contains references to a pre-release version of Apple software or hardware.
 Apps with compatibility references to a pre-GM version of iOS SDK or pre-released Apple hardware are not in compliance with the Apple Developer Program License Agreement.

Specifically, section 2.3 states:
"Apple may provide You with pre-release versions of the Apple Software or related services that 
constitute Apple Confidential Information and are subject to the confidentiality obligations of this Agreement."

해결 방안

해결방안은 매우 간단합니다. 앱 및 메타 데이터에서 시험판 버전의 Apple관련 소프트웨어 및 하드웨어 언급을 모두 제거 하거나 변경 하면 됩니다.


마치며

소프트웨어나 하드웨어 관련 언급을 다 제거 하라고 해결 방법을 가르쳐줬는데,, ‘iOS11 지원’ 이라는 문구는 왜 통과가 되는지 애매모호 한거 같습니다. ‘iOS11 지원’으로 변경 한후 다시 심사에 맡겼는데 심사를 한번 지켜 봐야겠습니다. 추후 결과는 이글을 통해 업데이트 하겠습니다  (무사히 통과되었습니다.)

여담이지만 심사기간이 정말 빨라진거 같습니다.



336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


EUC-KR 인코딩 하기

설명

iOS에서 EUC-KR로 인코딩 하는 문제가 프로젝트를 진행하면서 오랜 시간동안 Warning을 발생시켜서 눈에 가시처럼 보였는데 해결방안을 찾아서 포스팅 하게 되었습니다. 일단 먼저 EUC-KR(EUC-KR은 KS X 1001와 KS X 1003을 사용하는 8비트 문자 인코딩, EUC의 일종이며 대표적인 한글 완성형 인코딩이기 때문에 보통 완성형이라고 불린다. - 위키백과사전) 말그대로 한글 완성형 인코딩이기 때문에 오랜 시간 사용해왔습니다. 네이버,네이트,우체국등 오래된 한국기업 웹페이지는 EUC-KR로 되어있습니다. UTF-8을 사용하면 좋겠지만 용량문제, 전환비용등 때문인지 아직도 사용되고 있습니다. 특히 우체국 API를 통해 주소 검색을 구현할때는 쿼리를 EUC-KR로 인코딩 해서 보내야 올바른 값을 받을 수 있습니다.
기존에 사용 하고 있던 이 방식은 안타깝게도 iOS9버전 부터는 Warning을 발생시킵니다. iOS9에서는 더 이상 사용되지 않기 때문에 권장 UTF-8인코딩을 사용하라는 것입니다.

 NSString *queryString = [str stringByAddingPercentEscapesUsingEncoding:-2147481280];

    //first deprecated in iOS 9.0 - Use -stringByAddingPercentEncodingWithAllowedCharacters: instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.

포기하고 있다가 다른분을 도와주다가 우연찮게 찾아서 이렇게 포스팅을 하게되었습니다.


사용환경

* Swift3.x
* Xcode 8.3.3

소스 코드

  • Objective-C
- (NSString *)euckrEncodingObjectiveC:(NSString *)str {

    NSMutableString *outputQuery = [NSMutableString string];

    NSUInteger encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR);
    const char * source = [str cStringUsingEncoding:encoding];
    NSInteger sourceLen = strlen((const char *)source);

    for (int i = 0; i < sourceLen; ++i)
    {
        const unsigned char thisChar = source[i];
        if (thisChar == ' ') {
            [outputQuery appendString:@"+"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' ||
                 (thisChar >= 'a' && thisChar <= 'z') ||
                 (thisChar >= 'A' && thisChar <= 'Z') ||
                 (thisChar >= '0' && thisChar <= '9'))
        {
            [outputQuery appendFormat:@"%c", thisChar];
        } else {
            [outputQuery appendFormat:@"%%%02X", thisChar];
        }
    }
    return outputQuery;
}
  • Swift
func euckrEncoding(_ query: String) -> String { //EUC-KR 인코딩

    let rawEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.EUC_KR.rawValue))
    let encoding = String.Encoding(rawValue: rawEncoding)

    let eucKRStringData = query.data(using: encoding) ?? Data()
    let outputQuery = eucKRStringData.map {byte->String in
        if byte >= UInt8(ascii: "A") && byte <= UInt8(ascii: "Z")
            || byte >= UInt8(ascii: "a") && byte <= UInt8(ascii: "z")
            || byte >= UInt8(ascii: "0") && byte <= UInt8(ascii: "9")
            || byte == UInt8(ascii: "_") || byte == UInt8(ascii: ".") || byte == UInt8(ascii: "-")
        {
            return String(Character(UnicodeScalar(UInt32(byte))!))
        } else if byte == UInt8(ascii: " ") {
            return "+"
        } else {
            return String(format: "%%%02X", byte)
        }
        }.joined()

    return outputQuery
}

마치며

EUC-KR보다 UTF-8을 권장하지만 아직 한국내에서는 불가능한거 같습니다. 위에도 언급해듯이 전환비용, 용량문제등 아직 해결할 문제들이 많은거 같습니다. 오랫동안 눈에 밟히던 Warning을 없앤것만으로도 기분 좋은 일인거 같습니다. 틀린점이나 궁금한점이 있다면 댓글 남겨주시면 감사합니다 :)


참고 사이트

https://stackoverflow.com/questions/41270687/swift-euc-kr-korean-encoding-not-working-but-works-in-python



+ Recent posts