I don't remember when this issue was first discovered, but I'm having trouble logging in to xcode with my Apple ID, but beta version works. (seems to be related to macOS big sur beta 11)

 

To submit app i need to figure this out asap. I googled and found some kind of trick.

developer.apple.com/forums/thread/100020

 

Your session has expired, please l… | Apple Developer Forums

I log into my Apple Id account under Pref->Account. I AM able to login, it shows me as a team memeber. Then I exit xcode and when I run it, I go to Pref->Account, it says 'Your session has expired, please login'. I can do this over and over. Thanks Ralph

developer.apple.com

 

Because binary built with beta version couldn't upload to app store, I need to build binary with Xcode 11.7 and upload with beta.

It seems very odd but I have a no choice for now. Actually I haven't tried this way yet. If there is another problem, I will update it.

In-App Purchases ?

디지털 컨텐츠나 서비스를 앱에서 바로 구매할 수 있도록 제공되는 기능

유형 상품은 해당되지 않는다

 

support 4 content types

Consumable 게임 등에서 화폐처럼 계속해서 구입할 수 있는 아이템

Non-Consumable 한번 구입하면 디바이스를 옮겨 다녀도 계속 소유할 수 있는 아이템, 게임에서 무기라던가 무료앱을 제공하고 여기에 기능을 덧붙여서 유료로 제공하는 pro app 등

Non-Renewing Subscriptions 자동으로 재결제 되지 않기 때문에 사용자가 직접 재결제할 필요가 있음

Auto-renewable Subscriptions 자동 재결제

 

 

Load In-App Identifiers

앱에서 어떤 상품을 파는지 목록을 가져옴

 

Fetch Product Info

id 목록을 가져왔으면 App Store에서 해당 id를 가진 프로덕트의 localized된 정보를 가져옴

말 그대로 글로벌 마켓을 지원하기 때문에 지원하고자 하는 앱스토어에 맞게 localized information을 가져올 수 있다.

SKProductRequest를 생성할 때 상품 id 목록을 넘겨서 관심있는 상품을 알려줌

let request = SKProductRequest(productIdentifiers: ids)

request.delegate = self //상품 로딩 되면 알려줌

request.start()

전달된 id를 가진 상품의 localized 정보를 앱스토어로부터 가져와서 내려줌

앱스토어에 있는 currency 를 변경하지 말고 사용할 것.

 

Show In-App UI

당신의 앱, 당신의 서비스이기 때문에 서비스에 맞게 상품을 구입할 수 있는 최선의 UI를 보여줘라

 

Make Purchase

사용자가 상품 구매한 payment를 생성해서 paymentQueue에 추가한다

 

Process Transaction

paymentQueue에 payment가 등록되면 observer가 상품 구매 트랜잭션 상태를 트래킹해서 콜백으로 알려주고 어떻게 대응할지 구현하면 된다.

purchasing continue; 구매 flow 진행중
purchased 구매 완료 finishedTransaction 호출
deferred 이 컨텐츠를 사용할 수 있도록 허가 기다리는 중. 업데이트되면 다시 콜백 불릴거라 다른 처리 할 필요 없음.
restored 복구 finishedTransaction 호출
failed 실패 finishedTransaction 호출

 

Sandbox test

샌드박스 환경에서 테스트 가능한데 deferred 상태를 확인하기 위해 가족용 계정을 만드는 불필요한 작업을 하지 않도록 옵션을 제공한다

payment.simulatesAskToBuyInSandbox flag

 

Make Asset Available

payment completed되면 구매한 상품 정보를 업데이트하거나 사용자에게 어떤 상품을 구매했는지 알려주기

On-demand resources : 구매 후 더 필요한 리소스 있으면 받을 수 있게? apple에서 host 제공함 콜백으로 받으면 됨

 

 

Finish Transaction

paymentQueue에게 모든 payment 트랜잭션이 끝났다는 것을 알려줌

download 끝나면 끝났다고 알려줌

호출하지 않으면 payment가 큐에 살아 있음

다운로드 중이면 끝날때까지 기다렸다가 호출

 

Restore Transaction

SKPaymentQueue.defaultQueue().restoreCompletedTransactions()

restore api를 호출하면 observer delegate로 restored 상태를 가진 트랜잭션 목록이 전달됨

non-consumable or auto-renewable subscriptions 를 구입한 이력이 있으면 해당 identifier의 트랜잭션이 restored 상태값을 가지고 전달됨

필요에 의해 서버 처리하고 마지막은 무조건 finishedTransaction api를 호출해야 함 -> 트랜잭션 처리가 완료되었음을 알려주어야 함. 

 

 

 

SKPaymentQueue

신뢰도 있는 state를 가지고 있는 유일한 소스

queue가 알려주는 payment가 실제로 유효하고 존재하는 payment이다

개발자가 캐싱할 수는 있지만 믿을 수 있는건 paymentQueue에 있다

앱 시작하자마자 payment queue를 트래킹하는 observer를 생성해라

이유에 대해 설명하기 위해 예를 들면, 리딤 코드를 발급하는 서비스의 경우 사용자는 앱 내가 아닌 앱스토어에서 리딤코드를 사용할 것이다. 만약 앱이 실행 되었을 때 바로 이를 알아채지 못하면 사용자는 해당 상품을 사용할 수 없게 된다. 따라서 앱이 실행되자마자 paymentQueue가 알아챌 수 있게 appDelegate에 launch delegate method에 observer를 생성해야 한다.

 

Error Handling

모든 에러가 같지 않기 때문에 가이드 문서 보고 에러 코드 체크할 것

에러에 대한 얼럿은 비추

사용자가 구매를 취소한 것도 에러 콜백을 호출한다

사용자는 본인이 캔슬한걸 알고 있는데 굳이 얼럿으로 또 확인 시켜줄 필요는 없다

트랜잭션 핸들링은 최대한 스토어킷이 하도록 놓아두자

 

 

Receipt Validation

Server to Server : downloadable service

on device : 단순히 클라 사이드에서만 체크해서 상품을 unlock하는 경우, 예를 들어 잘 만들어진 앱의 예는 아니지만 교육 동영상 목록을 어플 안에 모두 넣어두고 구입한 경우에만 플레이할 수 있도록 구현. 용량이 적고 서버를 구축할 상황이 아니었음. 이때 receipt validation을 클라에서 처리

receipt validation api는 절대 클라에서 직접 호출하지 말 것. 클라에서 보내는 것을 믿을 수 있는지 확인할 길이 없다

 

The receipt

이 디바이스에서 이 유저가 이 상품을 구매했는지 믿을 수 있는 정보를 제공

번들에 저장되어 있는 앱스토어 path로 receipt를 가져올 수 있음

절대 certificate의 expiry date로 만료일 체크하지 말 것

root certificate authority on the receipt로 체크할 것; 애플에서 왔지~

receipt는 물건을 구매하면 발생하는데 renewable하다

 

 

Transaction LifeCycle

Counsumable & Non-renewing Subscriptions : 구매 후 단 한번 트랜잭션 발생 refresh receipt하거나 디바이스를 변경해도 다시 발생하지 않는다

Non-Consumable & Auto-renewable Subscriptions : 언제나 receipt에 구매 이력이 남아 있다 restore api로 가져올 수 있다 

 

 

 

리뷰 통과하려면

You must have a Restore button Should be used only for Non-Consumable & Auto-renewable Subscriptions

Resotre 와 구매 버튼은 따로

Auto-renewable 을 지원할 때 privacy policy URL 제공

앱 올릴때 지원한다느 걸 꼭 언급해야 함

계정로그인이 기본인 서비스가 아닌 경우 Non-renewing subscription은 로그인하지 않아도 구입할 수 있는 옵셔널 상품이어야 한다 

should works!!

[발단]

주로 사용하는 개인 메일을 지난 회사에서 Apple Developer Program Membership에 추가해서 쓰고 있었다.

그러다 동료가 퇴사하면서 내 계정이 자연스레 팀의 placeholder가 되었다. 팀명이 전 개발자의 이름이었는데 그때는 그러려니 했는데 이직하니 문제가 생겼다. 우선 새로운 회사에서 새 membership에 메일을 추가하니 내 계정에 team이 두개가 되었고 내가 certificate를 revoke할 때마다 내 이름 대신 그 분의 이름(팀명)이 뜨면서 사람들이 누가 revoke 했는지 계속 확인하게 만든 것이다.

 

회사 옮길 때마다 이럴 수는 없기에 애플에 삭제 또는 팀명 변경이 가능한지 물어보니 상황을 이해는 하지만 불가능하단다.

개발 프로그램마다 하나의 이메일을 쓰라고 한다.

이런, 이직할 때마다 하나의 메일을 버리게 되다니... 이럴 줄 알았다면 회사 메일로 등록할 것을 그랬다.

 

[전개]

메인 메일을 새로 파자 결심하고 우선 개발 메일을 회사의 것으로 변경하기로 한다.

1. Apple Developer Program Membership에 추가된 이전 계정을 지우고 새 계정을 추가했다.

2. Xcode의 Preferences > Accounts 에서 Apple Id를 새로 업데이트했다.

 

[위기 & 결말]

잉? Xcode를 오픈할때마다 계정정보가 사라진다.

구글링해보니 키체인에 남아 있어서라고 해서 KeyChain Access > All items에서 이전 계정을 지우고 다시 시도했다.

오, 변경된 아이디로 뜬다.

 

 

앗, Update Signing에 에러가 떠있다.

Team ZXXXXXXXXX (Automatic) | Platform iOS

The operation couldn’t be completed. Unable to log in with account 0000@gmail.com'. (The login details for account '0000@gmail.com' were rejected.)

 

Code Sign이라... 한참 생각하다가 certificate 갱신을 떠올렸다.

새로 받아 설치하니 에러도 사라졌다.

 

그런데 문제가 하나 더 있었다.

Xcode Server로 CI를 돌리고 있는데 자꾸 한번 돌리고 나면 developer teams 에서 빠진다.

'your server has been removed from this team and needs to be added again' 에러가 뜬다.

 

이것저것 다 수정해도 이 이슈만은 고쳐지지 않는다.

beta 버전에서 버그였다는 글이 보이긴 하는데 아직 고쳐지지 않은 건지...

계속 모니터링 해야겠다.

regular expression을 이용해서 해시태그를 가져오는 메소드가 이모지가 있는 텍스트에서 제대로 동작하지 않는다.

NSRegularExpression을 사용하기 위해서는 NSString으로 형변환해야 하는데 NSRange에 문자열 길이를 NSString의 length가 아니라 String의 count로 넘겨주고 있었다.

 

NSString과 String은 유니코드에 대한 동작 방식이 달라서 반환하는 문자열 길이가 다르다. String은 Extended Grapheme Clusters 사용해서 이모지 하나를 문자 한개로 계산하고 NSString은 이모지 하나를 유니코드 문자 두개로 계산하기 때문이다.

(뒤늦게 깨닫기 있기?)

 

p (textView.text as NSString).length // print 6

p textView.text.count // print 2

 

NSString의 length를 인자로 넘기도록 수정하면 이슈 해결!! 개발할 때 주의하자... 

optional value일 때 뜨는건 알겠는데 

 guard let lastEvent = self.playerItem?.accessLog()?.events.last else { return }

 log(.info, "\(lastEvent.description)")

여기에선 왜 뜨는걸까?

 

lastEvent 가진 데이터 중에 옵셔널 데이터가 있긴 한데

description은 String을 리턴하는데 왜 그렇지?

게다가 컴파일 빌드는 되는데 xcode server build가 실패함

궁금 ... 쏘 궁금...

 

 

구글링하다가 찾은 소소한 내용

1) String(describing:) - return string representing given value

 

2) debugdescription vs description

debugDescription는 description를 호출함

다른 점은 debugDescription는 오버라이드 해서 쓸 수 있다

Xcode의 Preferences > Server & Bot > Upgrade server. => (there is a message shown xcode might need to update because xcode use old version)

Remove server and re-add it.

ta~da~ you can use it again.

Submit for review 버튼을 누르면 IDFA 옵션을 선택하는 화면이 나온다.

IDFA를 사용해야 하는 부분이 있어서 YES를 해야 하는데 다른 옵션과 착각해서 NO를 선택했다;;

다시 변경하고 싶은데 바이너리를 재섭밋해야 하는 현실... 두둥!!

 

Wating for review 상태에서 바이너리 리젝하려고 Remove this version from review 링크가 있어서 클릭했는데 현 상태에서는 리젝할 수 없다고 에러 얼럿이 뜬다... 아 왜... 안되는걸까...? 

 

검색해도 잘 안나오고 아이폰 Connect 앱으로 들어가보라길래 들어갔지만 빨간색 reject this binary 버튼이 뜨지 않음...

섭밋을 직접 진행한지 너무 오래돼서 감도 떨어졌는데 UI도 바껴서 긴장에 긴장을 거듭함(룰도 조금씩 변경된다)

지인 개발자를 붙잡고 푸념을 늘어놓다가 2시간 반이 지나서야 Connect 앱에 reject 버튼이 생김을 확인할 수 있었다

진짜 두시간 반이 일주일 같았음

 

후아

 

Remove this version from review 링크 눌렀을 때 떴던 에러 화면.. 순식간에 지나가서 캡처 힘들

 

* reference github : https://github.com/hmhv/YoutubePlayer-in-WKWebView

 

hmhv/YoutubePlayer-in-WKWebView

Helper library for iOS developers looking to add YouTube video playback in their applications via the iframe player in a WKWebView - hmhv/YoutubePlayer-in-WKWebView

github.com

 

* I got an error : An error occurred, please try again later. Learn More

 

* resolved :

- add "origin" : "https://www.youtube.com" to playerVars when playerView load video id.

무거운 gif 파일을 mp4로 저장하기.


* Github Repository



* Error Code Status


-16364 the operation could not be completed: invalid timestamp, timestamp가 업데이트 되지 않아 이전 정보를 가지고 있어서 문제.

@objc 란,

objective-c에서 스위프트로 선언된 클래스, 메소드, 밸류 프로퍼티 등을 참조하고 싶을 때 @objc 프로퍼티를 지정해 주면 된다.

기존에 만들어진 어플에 스위프트로 작업하고 있기 때문에 @objc 프로퍼티를 쓸 일이 잦은데 swift4 부터 @objcMembers 라는 프로퍼티가 생겼다.

기존의 @objc와 다른 점이 무엇인가 찾아보니 다음과 같이 명시되어 있다.

When a Swift class introduces many new methods or properties that require behavior from the Objective-C runtime, use the @objcMembers attribute in the declaration of that class. Applying the @objcMembers attribute to a class implicitly adds the @objc attribute to all of its Objective-C compatible members. Because applying the @objc attribute can increase the compiled size of an app and adversely affect performance, only apply the @objcMembers attribute on declarations when each member needs to have the @objc attribute applied.


@objc는 파일 사이즈와 성능에 영향을 준다니 @objcMembers로 쓰지 않을 이유가 없다.

단, @objcMemebers는 클래스에만 사용할 수 있기 때문에 특정 method나 프로퍼티만 공유하고 싶을 땐 @objc 그대로 사용하면 된다.

이왕이면 전부 Swift로 바꾸고 싶지만 당분간만 유지하기로 해...

+ Recent posts