StoreKit를 이용한 리뷰 요청 글은 여기입니다.
*****제가 이 글을 쓴 이후로 버그가 발견되었습니다, 버그를 수정한 코드로 블로그 글도 수정했습니다만, 이 링크를 참고하는 것이 더 좋을 것 같습니다.*****
이번엔 WWDC2021에서 발표된 StoreKit 2와 WWDC2022 내용을 참고하여 인 앱 펄체이스를 구현해보도록 할 것이다.
본론으로 들어가기 전에, IAP(In-App-Purchase) 기능에는 하나의 논란이 있었는데,
개발자에게 팁을 주는 기능이 불가능한가? 이다.
사실 이전까지의 자료들을 찾아보면 나오는 결과는 '결제를 요구하는 상품이 실제로 이용자에게 실익을 주어야한다'는 내용이었다.
혹시 기부를 받거나 하게 된다면 애플에서는 실제 기부를 담당하는 기관과 연결되어 있도록 요구하고 각종 가이드라인을 따르도록 요구했었다(해당내용) 그리고 16년에 나온 애플의 기사에서는 유관한 내용을 다루기도 했었다.
근데 새로운 앱스토어 리뷰 가이드라인에서는 해당 내용이 빠지고, 다음과 같은 내용이 추가되면서 개발자에게 직접 기부하는 것이 허가되었다!!!!!
본론으로 넘어가서, 앱스토어엔 다음 네가지 상품 카테고리로 나누어 등록할 수 있다.
1. 소모성의 상품 - ex) 게임의 목숨, 게임머니 등
2. 비소모성의 상품 - ex) 한 번 사면 기한이 없이 이어지는 카메라앱의 필터
3. 갱신불가한 구독 상품 - ex) 교육 앱의 기간제 무제한 시청 강의
4. 갱신가능한 구독 상품 - ex) 트위터의 Verification Mark(blue check)
이번 글에선 목적을 뚜렷하게 광고를 없애는 Subscription 상품(4번), 그리고 평생 광고를 삭제해주는 기능을 In-App-Purchase(2번)로 구현하고 공유하려고 한다.
우선, App Store Connects에서 상품을 등록해준다.
Features에 In-App-Purchase, Subscription에 각각 상품을 등록해주었다.
그 후엔 StoreKit Configuration File을 추가해준다. - 파일 생성할 때, AppStore Connects와 연동에 체크해준다
(사실 위에 과정을 생략하고, config 파일에서 바로 추가할 수 있는데, plist를 만들거나 하기 때문에 사실 수동이 덜 귀찮았다)
그러면, 다음과 같이 좌측 하단에 Sync 버튼이 있을 것이다. 그걸 클릭해주면 다음과 같이 정상적으로 불러와진다.
그리고 위와같이 상품이 구성되어있다. 상품의 이름, ID(번들id 형태처럼 임의적으로 만들어주었다), 가격, 그리고 내 앱은 아랍어로 Full-localized 되었기 때문에 아랍어로도 추가해준 것을 확인 할 수 있다.
그리고 다음의 코드 조각들이 필요하다
import StoreKit
// 상품ID 리스트
let productIds = ["removeAdsLifeTime", "removeAdsMonthly", "removeAdsYearly"]
// 상품을 담아줄 그릇
@State private var products: [Product] = []
// 상품을 load해주는 함수
private func loadProducts() async throws {
self.products = try await Product.products(for: productIds)
}
// 상품 구매 함수
private func purchase(_ product: Product) async throws {
let result = try await product.purchase()
switch result {
case let .success(.verified(transaction)):
// 구매성공
await transaction.finish()
case let .success(.unverified(_, error)):
// 구매에 성공했지만, transaction이 이루어지지 않은 상태
break
case .pending:
// 상품 구매 요청을 서버에 보내는 중
break
case .userCancelled:
//
break
@unknown default:
break
}
}
위의 코드 조각들을 ObservableObject 프로토콜을 따르는 클래스에 적절히 작성해주고,
ForEach(self.products) { product in
Button {
Task {
do {
try await self.purchase(product)
} catch {
print(error)
}
}
} label: {
Text("\(product.displayPrice) - \(product.displayName)")
}
}
}.task {
do {
try await self.loadProducts()
} catch {
print(error)
}
위와 같이 같이 제품 리스트를 뿌려준다.
Edit Scheme에 들어가서 우리가 만든 스토어킷 컨피겨레이션을 Scheme에 등록해주자
그러고 빌드를 하면 :
다음과 같은 화면이 상품 목록이 잘 나오고, 성공적으로 구매가 잘 이루어지는 것을 볼 수 있다.
이후엔, Transaction(거래) 상태를 업데이트 해주는 function이 필요한데, 관련 내용은 다음의 RevenueCat이라는 오픈소스가 올려둔 튜토리얼의 소스코드를 참고하였기 때문에, 이 링크의 코드를 참고해주길 바란다.
참고로 RevenueCat은 이러한 정기구독 구현의 복잡함을 단순화하는 라이브러리로 유투브에 관련 영상을 찾아보면 영상길이가 지금 과정의 몇배가 차이가 날 정도로 간단하기 때문에, RevenueCat을 이용하는 것도 좋은 방법이라고 생각한다.
그리고 다음으로 App Store Review Guideline을 충족시키기 위해선 아주 중요한 스텝이다.
바로, 구매 복원 버튼을 만드는 것이다.
사실, StoreKit2 에서는 자동으로 구매복원이 되고 있으나, restore mechanism을 구현하는 것을 권장하고 있다.
Task {
do {
try await AppStore.sync()
} catch {
print(error)
}
}
따라서 위와같이 복원 기능 또한 버튼으로 만들어주자.
그리고 @AppStorage 를 이용하여 유저 정보에 저장을 해주어야 하는데, 해당 코드는 필자가 쓴 코드가 Firebase와 함께 작성하므로 생략하고, 위에 주었던 링크의 Step7을 참고해서 작성해주길 바란다.
그렇게 한다면, 다음과 같이 광고가 사라지는 것을 확인 할 수 있다.
결론
StoreKit2를 이용하여 IAP와 Subscription을 구현해 본 결과, App Store Configuration의 Sync 기능으로 구현이 한 층 더 간편해진 것을 확인했다.
******꼭 이 링크를 확인하고 구현에 심혈을 기울여 주세요! 해당 글은 맨위의 애플 공식 가이드라인과, StoreKit2의 소개글 정도로 간편하게 읽어주시길 바랍니다!*****
'iOS' 카테고리의 다른 글
WWDC 2023) SwiftData (vs. CoreData) (0) | 2023.06.11 |
---|---|
Firebase FireStore Error(message: "Missing or insufficient permissions.") 해결법 (0) | 2023.06.10 |
오류해결 공유) The address you submitted couldn't be verified. Review the address, edit if needed, and click Add. (0) | 2023.06.10 |
앱 수익화 - SwiftUI 앱에 광고넣기(AdMob vs Meta Audience Network) (0) | 2023.06.09 |
TCA - Reducer -> ReducerProtocol (0) | 2023.06.09 |