
Oi Denis conectado da Apphud - um serviço para a análise de assinaturas renováveis para aplicativos iOS.
Como você sabe, na WWDC 2019, a Apple anunciou sua nova estrutura declarativa SwiftUI. Neste artigo, tentarei explicar como usar o SwiftUI para fazer telas de pagamento e implementar a funcionalidade de assinaturas renováveis automaticamente.
Se você ainda não conhece o SwiftUI, pode ler um pequeno artigo introdutório . E se você quiser saber mais sobre assinaturas e como implementá-las corretamente, leia este artigo .
Você precisa do Xcode 11 para funcionar . Crie um novo projeto e verifique se há uma marca de seleção ao lado de "Usar SwiftUI".
SwiftUI é uma estrutura para escrever uma interface e, portanto, não podemos criar um gerente de compras usando-a. Mas não escreveremos para o nosso gerente, mas usaremos uma solução pronta, que complementamos com nosso código. Você pode usar, por exemplo, SwiftyStoreKit . No nosso exemplo, usaremos a classe do artigo anterior .
Os produtos serão inicializados na tela principal, a data de vencimento de nossas assinaturas e o botão para mudar para a tela de compra também serão exibidos lá.
ProductsStore.shared.initializeProducts() if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: ContentView(productsStore: ProductsStore.shared)) self.window = window window.makeKeyAndVisible() }
Considere a classe SceneDelegate
. Nele, criamos uma classe Singleton ProductsStore
, na qual os produtos são inicializados. Depois disso, crie nosso ContentView
raiz e especifique singleton como um parâmetro de entrada.
Considere a classe ProductsStore
:
class ProductsStore : ObservableObject { static let shared = ProductsStore() @Published var products: [SKProduct] = [] @Published var anyString = "123"
Essa classe pequena, um tipo de "complemento" no IAPManager
, serve para atualizar o ContentView
ao atualizar a lista de produtos. A classe ProductsStore
suporta o protocolo ObservableObject
.
O que são ObservableObject
e @Published
?
ObservableObject
é um protocolo especial para observação de objetos e rastreamento de alterações em suas propriedades. As propriedades devem ser marcadas com o atributo @Published
. No exemplo, uma notificação é enviada quando a matriz de products
alterada, mas você pode adicionar essa notificação para quaisquer métodos e propriedades do objeto.
O carregamento do produto em si pode ser feito de qualquer maneira, mas no final desta solicitação, você deve atribuir a matriz do produto à variável de produtos. Como ouvir as mudanças? Isso é feito usando o parâmetro de chave @ObservedObject
:
@ObservedObject var productsStore : ProductsStore
Simplificando, isso é algo semelhante ao Centro de Notificação. E para que o seu View
aceite essas notificações, você deve ter uma variável desse objeto com o atributo @ObservedObject
.
Vamos voltar à lógica da classe ProductsStore
. Seu principal objetivo é baixar e armazenar uma lista de produtos. Mas a matriz de produtos já está armazenada no IAPManager
, ocorre duplicação. Isso não é bom, mas, primeiro, neste artigo, eu queria mostrar como o binning de objetos é implementado e, segundo, nem sempre é possível alterar a classe final do gerente de compras. Por exemplo, se você usar bibliotecas de terceiros, não poderá adicionar o protocolo ObservableObject
e enviar notificações.
Vale ressaltar que, além do atributo @ObservedObject
também existe o atributo @State
, que ajuda a rastrear alterações em variáveis simples (por exemplo, String
ou Int
) e o @EnvironmentObject
mais global, que pode atualizar todo o View
no aplicativo de uma só vez, sem precisar passar a variável entre os objetos.
Vamos ContentView
tela inicial ContentView
:
struct ContentView : View { @ObservedObject var productsStore : ProductsStore @State var show_modal = false var body: some View { VStack() { ForEach (productsStore.products, id: \.self) { prod in Text(prod.subscriptionStatus()).lineLimit(nil).frame(height: 80) } Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present") }.sheet(isPresented: self.$show_modal) { PurchaseView() } } } }
Vamos descobrir o código. Usando o ForEach
criamos View
texto, cujo número é igual ao número de produtos. Como vinculamos a variável productsStore
, a View
será atualizada sempre que a matriz de produtos na classe ProductsStore
alterada.
O método subscriptionStatus
faz parte da SKProduct
classe SKProduct
e retorna o texto desejado, dependendo da data de validade da assinatura:
func subscriptionStatus() -> String { if let expDate = IAPManager.shared.expirationDateFor(productIdentifier) { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .medium let dateString = formatter.string(from: expDate) if Date() > expDate { return "Subscription expired: \(localizedTitle) at: \(dateString)" } else { return "Subscription active: \(localizedTitle) until:\(dateString)" } } else { return "Subscription not purchased: \(localizedTitle)" } }

Esta é a nossa tela inicial
Agora vá para a tela de inscrição. Como, de acordo com as regras da Apple, a tela de pagamento deve conter um texto longo das condições de compra, é aconselhável usar o ScrollView
.
var body: some View { ScrollView (showsIndicators: false) { VStack { Text("Get Premium Membership").font(.title) Text("Choose one of the packages above").font(.subheadline) self.purchaseButtons() self.aboutText() self.helperButtons() self.termsText().frame(width: UIScreen.main.bounds.size.width) self.dismissButton() }.frame(width : UIScreen.main.bounds.size.width) }.disabled(self.isDisabled) }
Neste exemplo, criamos duas visualizações de texto com uma fonte diferente. Além disso, todas as outras visualizações são destacadas em seus próprios métodos. Isso é feito por três razões:
O código se torna mais legível e compreensível para o estudo.
No momento da redação deste artigo, o Xcode 11 Beta geralmente congela e não pode compilar código, e colocar partes do código em funções ajuda o compilador.
Mostre que a vista pode ser transformada em funções separadas, facilitando o body
.
Considere o método purchaseButtons()
:
func purchaseButtons() -> some View {
Aqui, criamos uma pilha horizontal e, em um loop ForEach
, criamos um PurchaseButton
personalizado, no qual transferimos o produto e o bloco de retorno de chamada.
Classe PurchaseButton
:
struct PurchaseButton : View { var block : SuccessBlock! var product : SKProduct! var body: some View { Button(action: { self.block() }) { Text(product.localizedPrice()).lineLimit(nil).multilineTextAlignment(.center).font(.subheadline) }.padding().frame(height: 50).scaledToFill().border(Color.blue, width: 1) } }
Este é um botão normal que armazena e chama o bloco passado ao criar o objeto. Um traçado de arredondamento é aplicado a ele. Exibimos o preço do produto e a duração do período da assinatura como o texto no método localizedPrice()
.
A compra da assinatura é implementada da seguinte maneira:
func purchaseProduct(skproduct : SKProduct){ print("did tap purchase product: \(skproduct.productIdentifier)") isDisabled = true IAPManager.shared.purchaseProduct(product: skproduct, success: { self.isDisabled = false ProductsStore.shared.handleUpdateStore() self.dismiss() }) { (error) in self.isDisabled = false ProductsStore.shared.handleUpdateStore() } }
Como você pode ver, após a conclusão da compra, o método handleUpdateStore
é handleUpdateStore
, com a ajuda da qual uma notificação é enviada para atualizar o ContentView
. Isso é para garantir que o ContentView
status das assinaturas ao ocultar uma tela modal. O método de dismiss
oculta a janela modal.
Como o SwiftUI é uma estrutura declarativa, ocultar uma janela modal não é implementada como de costume. Devemos chamar o método de dismiss()
no wrapper da variável presentationMode
, declarando-o com o atributo @Environment
:
struct PurchaseView : View { @State private var isDisabled : Bool = false @Environment(\.presentationMode) var presentationMode private func dismiss() { self.presentationMode.wrappedValue.dismiss() } func dismissButton() -> some View { Button(action: { self.dismiss() }) { Text("Not now").font(.footnote) }.padding() } ...
A variável presentationMode
faz parte dos Environment Values - conjuntos especiais de métodos e propriedades globais. No SwiftUI, quase todas as ações ocorrem ao alterar os valores das variáveis, você não pode fazer nada em tempo de execução no sentido literal da palavra - tudo é delimitado com antecedência. E para fazer algo em tempo de execução, você precisa usar wrappers.

Tela de compra de assinatura
Conclusão
Espero que este artigo seja útil para você. A Apple adora quando os desenvolvedores usam sua tecnologia mais recente. Se você lançar um aplicativo para iOS 13 usando o SwiftUI, há uma probabilidade potencial de ser uma Apple infame. Portanto, não tenha medo de novas tecnologias - use-as. Você pode baixar o código completo do projeto aqui .
Deseja implementar assinaturas no seu aplicativo iOS em 10 minutos? Integre o Apphud e:
- Faça compras usando apenas um método;
- rastreia automaticamente o status da assinatura de cada usuário;
- Integre facilmente as ofertas de assinatura
- enviar eventos de assinatura para Amplitude, Mixpanel, Slack e Telegram, levando em consideração a moeda local do usuário;
- diminua a taxa de rotatividade de aplicativos e retorne usuários não inscritos.
O que ler?