SwiftUI- und automatisch erneuerbare Abonnements

Bild


Hallo! Connected Denis von Apphud - ein Dienst zur Analyse erneuerbarer Abonnements für iOS-Anwendungen.


Wie Sie wissen, hat Apple auf der WWDC 2019 sein neues deklaratives SwiftUI-Framework angekündigt. In diesem Artikel werde ich versuchen zu erklären, wie SwiftUI verwendet wird, um Zahlungsbildschirme zu erstellen und die Funktionalität von automatisch erneuerbaren Abonnements zu implementieren.


Wenn Sie mit SwiftUI noch nicht vertraut sind, können Sie einen kurzen Einführungsartikel lesen. Wenn Sie mehr über Abonnements und deren korrekte Implementierung erfahren möchten, lesen Sie diesen Artikel .

Sie benötigen Xcode 11, um zu arbeiten . Erstellen Sie ein neues Projekt und stellen Sie sicher, dass neben "Use SwiftUI" ein Häkchen angezeigt wird.


SwiftUI ist ein Framework zum Schreiben einer Benutzeroberfläche. Daher können wir damit keinen Einkaufsmanager erstellen. Wir werden unseren Manager jedoch nicht schreiben, sondern eine vorgefertigte Lösung verwenden, die wir mit unserem Code ergänzen. Sie können beispielsweise SwiftyStoreKit verwenden . In unserem Beispiel verwenden wir die Klasse aus unserem vorherigen Artikel .


Die Produkte werden auf dem Hauptbildschirm initialisiert, das Ablaufdatum unserer Abonnements und die Schaltfläche zum Umschalten auf den Kaufbildschirm werden ebenfalls dort angezeigt.


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() } 

Betrachten Sie die SceneDelegate Klasse. Darin erstellen wir einen ProductsStore Singleton-Klasse, in dem die Produkte initialisiert werden. Erstellen Sie anschließend unsere Stamm- ContentView und geben Sie Singleton als Eingabeparameter an.
Betrachten Sie die ProductsStore Klasse:


 class ProductsStore : ObservableObject { static let shared = ProductsStore() @Published var products: [SKProduct] = [] @Published var anyString = "123" // little trick to force reload ContentView from PurchaseView by just changing any Published value func handleUpdateStore(){ anyString = UUID().uuidString } func initializeProducts(){ IAPManager.shared.startWith(arrayOfIds: [subscription_1, subscription_2], sharedSecret: shared_secret) { products in self.products = products } } } 

Diese kleine Klasse, eine Art „Add-On“ über den IAPManager , dient zum Aktualisieren der ContentView beim Aktualisieren der ContentView . Die ProductsStore Klasse unterstützt das ObservableObject Protokoll.


Was sind ObservableObject und @Published ?


ObservableObject ist ein spezielles Protokoll zum Beobachten von Objekten und zum Verfolgen von Änderungen in seinen Eigenschaften. Eigenschaften müssen mit dem Attribut @Published . In diesem Beispiel wird eine Benachrichtigung gesendet, wenn sich das Produktarray ändert. Sie können diese Benachrichtigung jedoch für alle Methoden und Eigenschaften des Objekts hinzufügen.


Das Laden des Produkts selbst kann auf beliebige Weise erfolgen. Am Ende dieser Anforderung müssen Sie jedoch das Produktarray der Produktvariablen zuweisen. Wie höre ich Änderungen? Dies erfolgt mit dem Schlüsselparameter @ObservedObject :


 @ObservedObject var productsStore : ProductsStore 

Einfach ausgedrückt ist dies etwas Ähnliches wie das Benachrichtigungscenter. Damit Ihre View diese Benachrichtigungen akzeptiert, müssen Sie eine Variable dieses Objekts mit dem Attribut @ObservedObject haben.


Kehren wir zur Logik der ProductsStore Klasse zurück. Der Hauptzweck ist das Herunterladen und Speichern einer Produktliste. Das IAPManager ist jedoch bereits im IAPManager gespeichert. IAPManager kommt zu einer Duplizierung. Das ist nicht gut, aber erstens wollte ich Ihnen in diesem Artikel zeigen, wie das Binning von Objekten implementiert wird, und zweitens ist es nicht immer möglich, die fertige Klasse des Einkaufsmanagers zu ändern. Wenn Sie beispielsweise Bibliotheken von Drittanbietern verwenden, können Sie das ObservableObject Protokoll nicht hinzufügen und keine Benachrichtigungen senden.


Es ist erwähnenswert, dass es neben dem Attribut @ObservedObject auch das Attribut @State , mit dessen Hilfe Änderungen an einfachen Variablen (z. B. String oder Int ) verfolgt werden können, und das globalere @EnvironmentObject , mit dem alle @EnvironmentObject in der Anwendung gleichzeitig aktualisiert werden können, ohne dass die Variable zwischen Objekten übergeben werden muss.


ContentView wir ContentView Startbildschirm fort:


 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() } } } } 

Lassen Sie uns den Code herausfinden. Mit ForEach erstellen wir ForEach deren Anzahl der Anzahl der Produkte entspricht. Da wir die Variable productsStore binden, wird die View aktualisiert, wenn sich das Produktarray in der ProductsStore Klasse ändert.


Die subscriptionStatus Methode ist Teil der SKProduct Klassenerweiterung und gibt abhängig vom Ablaufdatum des Abonnements den gewünschten Text zurück:


 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)" } } 

Dies ist unser Startbildschirm
Dies ist unser Startbildschirm


Gehen Sie nun zum Abonnementbildschirm. Da der Zahlungsbildschirm gemäß den Apple-Regeln einen langen Text der ScrollView , ist es ScrollView , ScrollView zu verwenden.


 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) } 

In diesem Beispiel haben wir zwei Textansichten mit einer anderen Schriftart erstellt. Darüber hinaus werden alle anderen Ansichten in ihren eigenen Methoden hervorgehoben. Dies geschieht aus drei Gründen:


  1. Der Code wird für das Studium lesbarer und verständlicher.


  2. Zum Zeitpunkt dieses Schreibens friert Xcode 11 Beta häufig ein und kann keinen Code kompilieren. Das Einfügen von Teilen des Codes in Funktionen hilft dem Compiler.


  3. Zeigen Sie, dass die Ansicht in separate Funktionen unterteilt werden kann, um den body erleichtern.



Betrachten Sie die Methode purchaseButtons() :


 func purchaseButtons() -> some View { // remake to ScrollView if has more than 2 products because they won't fit on screen. HStack { Spacer() ForEach(ProductsStore.shared.products, id: \.self) { prod in PurchaseButton(block: { self.purchaseProduct(skproduct: prod) }, product: prod).disabled(IAPManager.shared.isActive(product: prod)) } Spacer() } } 

Hier erstellen wir einen horizontalen Stapel und in einer ForEach Schleife erstellen wir einen benutzerdefinierten PurchaseButton , in den wir das Produkt und den Rückrufblock übertragen.


PurchaseButton Klasse:


 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) } } 

Dies ist eine normale Schaltfläche, die den beim Erstellen des Objekts übergebenen Block speichert und aufruft. Darauf wird ein Rundungshub angewendet. Wir zeigen den Preis des Produkts und die Dauer des Abonnementzeitraums als Text in der localizedPrice() -Methode an.


Der Kauf eines Abonnements erfolgt wie folgt:


 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() } } 

Wie Sie sehen, wird nach Abschluss des Kaufs die handleUpdateStore Methode handleUpdateStore , mit der eine Benachrichtigung zum Aktualisieren der ContentView . Damit soll sichergestellt werden, dass ContentView Status von Abonnements ContentView wenn ein modaler Bildschirm ContentView . Die dismiss verbirgt das modale Fenster.


Da SwiftUI ein deklaratives Framework ist, wird das Ausblenden eines modalen Fensters nicht wie gewohnt implementiert. Wir müssen die dismiss() -Methode im Wrapper der presentationMode Variablen @Environment und sie mit dem Attribut @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() } ... 

Die presentationMode Variable ist Teil der Umgebungswerte - spezielle Sätze globaler Methoden und Eigenschaften. In SwiftUI werden fast alle Aktionen ausgeführt, wenn die Werte von Variablen geändert werden. Sie können zur Laufzeit nichts im wahrsten Sinne des Wortes tun - alles ist im Voraus begrenzt. Und um zur Laufzeit etwas zu tun, müssen Sie Wrapper verwenden.


Bildschirm für den Abonnementkauf
Bildschirm für den Abonnementkauf


Fazit


Ich hoffe, dieser Artikel wird Ihnen nützlich sein. Apple liebt es, wenn Entwickler die neueste Technologie verwenden. Wenn Sie eine App für iOS 13 mit SwiftUI veröffentlichen, besteht die Möglichkeit, dass Sie ein berüchtigter Apple sind. Haben Sie also keine Angst vor neuen Technologien - nutzen Sie sie. Den vollständigen Projektcode können Sie hier herunterladen.


Möchten Sie in 10 Minuten Abonnements in Ihrer iOS-App implementieren? Integrieren Sie Apphud und:
  • Kaufen Sie nur mit einer Methode ein.
  • Verfolgen Sie automatisch den Status des Abonnements jedes Benutzers.
  • Abonnementangebote einfach integrieren
  • Senden Sie Abonnementereignisse an Amplitude, Mixpanel, Slack und Telegram unter Berücksichtigung der lokalen Währung des Benutzers.
  • Verringern Sie die Abwanderungsrate in Anwendungen und geben Sie nicht abonnierte Benutzer zurück.


Was zu lesen?


Source: https://habr.com/ru/post/de458116/


All Articles