اشتراكات SwiftUI و Auto-Renewable

صورة


تحية! دينيس متصلة من Apphud - خدمة لتحليل الاشتراكات المتجددة لتطبيقات iOS.


كما تعلمون ، في WWDC 2019 ، أعلنت Apple عن إطار SwiftUI التعريفي الجديد. سأحاول في هذه المقالة معرفة كيفية استخدام SwiftUI لإجراء شاشات الدفع وتنفيذ وظائف اشتراكات التجديد التلقائي.


إذا لم تكن معتادًا على SwiftUI ، يمكنك قراءة مقال تمهيدي قصير. وإذا كنت تريد معرفة المزيد حول الاشتراكات وكيفية تنفيذها بشكل صحيح ، فاقرأ هذه المقالة .

تحتاج Xcode 11 للعمل . أنشئ مشروعًا جديدًا وتأكد من وجود علامة اختيار بجوار "استخدام SwiftUI".


SwiftUI هو إطار عمل لكتابة واجهة ، وبالتالي لا يمكننا إنشاء مدير شراء باستخدامه. لكننا لن نكتب مديرنا ، ولكن نستخدم حلاً جاهزًا ، والذي سنكمله برمزنا. يمكنك استخدام ، على سبيل المثال ، SwiftyStoreKit . في مثالنا ، سوف نستخدم الفصل من مقالتنا السابقة .


ستتم تهيئة المنتجات على الشاشة الرئيسية ، وسيتم أيضًا عرض تاريخ انتهاء اشتراكاتنا والزر الخاص بالتبديل إلى شاشة الشراء هناك.


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

النظر في فئة SceneDelegate . في ذلك ، نقوم بإنشاء ProductsStore فئة مفردة ، حيث تتم تهيئة المنتجات. بعد ذلك ، قم بإنشاء ContentView الجذر الخاص بنا ContentView singleton كمعلمة إدخال.
النظر في فئة ProductsStore :


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

تعمل هذه الفئة الصغيرة ، وهي نوع من "الوظائف الإضافية" على IAPManager ، على تحديث ContentView عند تحديث قائمة المنتجات. تدعم فئة ProductsStore بروتوكول ObservableObject .


ما هي @Published و @Published ؟


ObservableObject هو بروتوكول خاص لمراقبة الكائنات وتتبع التغييرات في خصائصه. يجب تمييز الخصائص @Published . في المثال ، يتم إرسال إشعار عندما يتغير صفيف products ، ولكن يمكنك إضافة هذا الإشعار لأي طرق وخصائص الكائن.


يمكن أن يتم تحميل المنتج بنفسه بأي طريقة ، ولكن في نهاية هذا الطلب ، يجب عليك تعيين صفيف المنتج لمتغير المنتجات. كيفية الاستماع إلى التغييرات؟ يتم ذلك باستخدام المعلمة الرئيسية @ObservedObject :


 @ObservedObject var productsStore : ProductsStore 

ببساطة ، هذا شيء مشابه لمركز الإشعارات. @ObservedObject يقبل View هذه الإشعارات ، يجب أن يكون لديك متغير من هذا الكائن مع السمة @ObservedObject .


دعنا نعود إلى منطق فئة ProductsStore . الغرض الرئيسي منه هو تنزيل وتخزين قائمة المنتجات. لكن مجموعة المنتجات مخزنة بالفعل في IAPManager ، يحدث التكرار. هذا ليس جيدًا ، لكن أولاً ، في هذا المقال ، أردت أن أوضح لك كيف يتم تنفيذ عملية تخزين الكائنات ، وثانيًا ، لا يمكن دائمًا تغيير الفئة النهائية لمدير المشتريات. على سبيل المثال ، إذا كنت تستخدم مكتبات تابعة لجهات أخرى ، فلن تتمكن من إضافة بروتوكول ObservableObject وإرسال إعلامات.


تجدر الإشارة إلى أنه بالإضافة إلى سمة @ObservedObject هناك أيضًا سمة @State ، والتي تساعد على تتبع التغييرات في المتغيرات البسيطة (على سبيل المثال ، String أو Int ) والمزيد من @EnvironmentObject الأكثر عمومية ، والتي يمكن تحديث الكل View في التطبيق في وقت واحد دون الحاجة إلى تمرير المتغير بين الكائنات.


دعنا ContentView شاشة بدء 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() } } } } 

دعونا معرفة الرمز. باستخدام ForEach نقوم بإنشاء View نصية ، وعددها يساوي عدد المنتجات. نظرًا لربط متغير productsStore ، سيتم تحديث View كلما تغيرت مجموعة المنتجات في فئة ProductsStore .


تعد طريقة subscriptionStatus جزءًا من SKProduct فئة SKProduct وتقوم بإرجاع النص المرغوب اعتمادًا على تاريخ انتهاء الاشتراك:


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

هذه هي شاشة البدء لدينا
هذه هي شاشة البدء لدينا


اذهب الآن إلى شاشة الاشتراك. نظرًا لقواعد Apple ، يجب أن تحتوي شاشة الدفع على نص طويل من شروط الشراء ، فمن الحكمة استخدام 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) } 

في هذا المثال ، أنشأنا عرضين للنص بخط مختلف. علاوة على ذلك ، يتم تسليط الضوء على جميع وجهات النظر الأخرى في أساليبها الخاصة. يتم ذلك لثلاثة أسباب:


  1. يصبح الرمز أكثر قابلية للقراءة وفهمًا للدراسة.


  2. في وقت كتابة هذا التقرير ، غالبًا ما يتجمد Xcode 11 Beta ولا يمكنه تجميع التعليمات البرمجية ، كما أن وضع أجزاء من التعليمات البرمجية في وظائف يساعد المترجم.


  3. أظهر أنه يمكن جعل العرض في وظائف منفصلة ، مما يجعل body أسهل.



خذ بعين الاعتبار طريقة 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() } } 

هنا نقوم بإنشاء مكدس أفقي وفي حلقة ForEach نقوم بإنشاء عملية PurchaseButton مخصصة ، والتي ننقل إليها المنتج وكتلة رد الاتصال.


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

هذا هو الزر العادي الذي يخزن ويدعو كتلة مرت عند إنشاء الكائن. يتم تطبيق ضربة دائرية عليها. نعرض سعر المنتج ومدة فترة الاشتراك كنص في طريقة localizedPrice() .


يتم تنفيذ شراء الاشتراك على النحو التالي:


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

كما ترى ، بعد اكتمال عملية الشراء ، يتم handleUpdateStore أسلوب handleUpdateStore ، بمساعدة إرسال إشعار لتحديث ContentView . هذا للتأكد من قيام ContentView حالة الاشتراكات عند إخفاء شاشة مشروط. أسلوب الإغلاق يخفي نافذة مشروط.


نظرًا لأن SwiftUI هو إطار عمل تعريفي ، لا يتم تنفيذ إخفاء نافذة مشروطة كالمعتاد. يجب أن نطلق على أسلوب @Environment dismiss() على مُتغير المتغير presentationMode ، @Environment ذلك باستخدام السمة @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() } ... 

المتغير presentationMode هو جزء من قيم البيئة - مجموعات خاصة من الأساليب والخصائص العالمية. في SwiftUI ، تحدث جميع الإجراءات تقريبًا عند تغيير قيم المتغيرات ، ولا يمكنك فعل أي شيء في وقت التشغيل بالمعنى الحرفي للكلمة - كل شيء مُحدَّد مسبقًا. ومن أجل القيام بشيء ما في وقت التشغيل ، تحتاج إلى استخدام الأغلفة.


شاشة شراء الاشتراك
شاشة شراء الاشتراك


استنتاج


آمل أن تكون هذه المقالة مفيدة لك. تحب Apple عندما يستخدم المطورون أحدث تقنياتها. إذا قمت بإصدار تطبيق لنظام iOS 13 باستخدام SwiftUI ، فهناك احتمال محتمل لأن تكون Apple سيئ السمعة. لذلك لا تخف من التقنيات الجديدة - استخدمها. يمكنك تنزيل رمز المشروع الكامل هنا .


هل تريد تنفيذ اشتراكات في تطبيق iOS الخاص بك في 10 دقائق؟ دمج Apphud و:
  • إجراء عمليات شراء باستخدام طريقة واحدة فقط ؛
  • تتبع تلقائيا حالة اشتراك كل مستخدم ؛
  • دمج عروض الاشتراك بسهولة
  • إرسال أحداث الاشتراك إلى Amplitude و Mixpanel و Slack و Telegram مع مراعاة العملة المحلية للمستخدم ؛
  • خفض معدل Churn في التطبيقات وإرجاع المستخدمين غير المشتركين.


ماذا تقرأ؟


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


All Articles