Skip to content

Commit 1b06563

Browse files
committed
add more delegate methods about apphud screen present/dismiss logic. Minor improvements to refresh logic and documentation improve
1 parent 7494e98 commit 1b06563

11 files changed

+129
-45
lines changed

ApphudSDK.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'ApphudSDK'
3-
s.version = '0.6.3'
3+
s.version = '0.6.5'
44
s.summary = 'Track and control iOS auto-renewable subscriptions.'
55

66
s.description = 'Track, control and analyze iOS auto-renewable subscriptions with Apphud.'

Source/Apphud.swift

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ import UserNotifications
1212

1313
public typealias ApphudEligibilityCallback = (([String : Bool]) -> Void)
1414

15+
16+
17+
// MARK:- Delegate
18+
1519
@objc public protocol ApphudDelegate {
1620

1721
/**
18-
Reports when subscription status has been changed (for example, from `trial` to `expired`).
22+
Returns array of subscriptions that user ever purchased. Empty array means user never purchased a subscription. If you have just one subscription group in your app, you will always receive just one subscription in an array.
1923

20-
In most cases you shouldn't use this method, it's just informational.
24+
This method is called when any subscription in an array has been changed (for example, status changed from `trial` to `expired`).
2125

22-
`Apphud.hasActiveSubscription()` – is what you need.
26+
In most cases you don't need this method because you already have completion blocks in `purchase`, `purchasePromo` and `submitReceipt` methods. However this method may be useful to detect whether subscription was purchased in Apphud's puchase screen.
2327
*/
2428
@objc optional func apphudSubscriptionsUpdated(_ subscriptions: [ApphudSubscription])
2529

@@ -44,8 +48,27 @@ public typealias ApphudEligibilityCallback = (([String : Bool]) -> Void)
4448
Returns array of StoreKit products. Note that you have to add all product identifiers in Apphud settings.
4549
*/
4650
@objc optional func apphudDidFetchStoreKitProducts(_ products: [SKProduct])
51+
52+
/**
53+
Pass your own modal presentation style to Apphud Screens. This is useful since iOS 13 presents in page sheet style by default.
54+
55+
To get full screen style you should pass `.fullScreen` or `.overFullScreen`.
56+
*/
57+
@objc optional func apphudScreenPresentationStyle() -> UIModalPresentationStyle
58+
59+
/**
60+
Notifies that Apphud Screen is about to dismiss
61+
*/
62+
@objc optional func apphudWillDismissScreen()
63+
64+
/**
65+
Notifies that Apphud Screen did dismiss
66+
*/
67+
@objc optional func apphudDidDismissScreen()
4768
}
4869

70+
//MARK:- Initialization
71+
4972
final public class Apphud: NSObject {
5073

5174
/**
@@ -92,6 +115,8 @@ final public class Apphud: NSObject {
92115
ApphudInternal.shared.delegate = delegate
93116
}
94117

118+
//MARK:- Make Purchase
119+
95120
/**
96121
Purchases product and automatically submits App Store Receipt to Apphud.
97122

@@ -128,6 +153,8 @@ final public class Apphud: NSObject {
128153
ApphudInternal.shared.submitReceipt(productId: productIdentifier, callback: callback)
129154
}
130155

156+
//MARK:- Handle Subscriptions
157+
131158
/**
132159
Returns true if user has active subscription.
133160

@@ -147,7 +174,7 @@ final public class Apphud: NSObject {
147174

148175
*/
149176
@objc public static func subscription() -> ApphudSubscription? {
150-
return ApphudInternal.shared.currentUser?.subscriptions?.first
177+
return ApphudInternal.shared.currentUser?.subscriptions.first
151178
}
152179

153180
/**
@@ -173,6 +200,8 @@ final public class Apphud: NSObject {
173200
ApphudInternal.shared.restoreSubscriptions(callback: callback)
174201
}
175202

203+
//MARK:- Push Notifications
204+
176205
/**
177206
Submit device push token to Apphud.
178207
- parameter token: Push token in Data class.
@@ -192,6 +221,8 @@ final public class Apphud: NSObject {
192221
return ApphudNotificationsHandler.shared.handleNotification(apsInfo)
193222
}
194223

224+
//MARK:- Eligibility Checks
225+
195226
/**
196227
Checks whether the given product is eligible for purchasing any of it's promotional offers.
197228

@@ -247,6 +278,8 @@ final public class Apphud: NSObject {
247278
ApphudInternal.shared.checkEligibilitiesForIntroductoryOffers(products: products, callback: callback)
248279
}
249280

281+
//MARK:- Other
282+
250283
/**
251284
Enables debug logs. Better to call this method before SDK initialization.
252285
*/

Source/ApphudFeedbackController.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class ApphudFeedbackController: UIViewController {
158158

159159
@objc private func cancelTapped(){
160160
self.textView.resignFirstResponder()
161-
dismiss(animated: true, completion: nil)
161+
self.dismiss()
162162
}
163163

164164
@objc private func buttonTapped(sender: ApphudInquiryButton){
@@ -168,8 +168,15 @@ class ApphudFeedbackController: UIViewController {
168168
let alertController = UIAlertController(title: "Thank you for feedback!", message: "Feedback sent", preferredStyle: .alert)
169169
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
170170
self.textView.resignFirstResponder()
171-
self.dismiss(animated: true, completion: nil)
171+
self.dismiss()
172172
}))
173173
present(alertController, animated: true, completion: nil)
174174
}
175+
176+
private func dismiss(){
177+
ApphudInternal.shared.delegate?.apphudWillDismissScreen?()
178+
(self.navigationController ?? self).dismiss(animated: true) {
179+
ApphudInternal.shared.delegate?.apphudDidDismissScreen?()
180+
}
181+
}
175182
}

Source/ApphudHttpClient.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ public class ApphudHttpClient {
9494

9595
}
9696

97-
#if DEBUG
9897
apphudLog("Start \(method) request \(request?.url?.absoluteString ?? "") params: \(params ?? [:])")
99-
#endif
10098

10199
return request
102100
}

Source/ApphudInquiryController.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ internal class ApphudInquiryController: UIViewController {
6666

6767
let controller = ApphudInquiryController(rule: rule)
6868
let nc = ApphudNavigationController(rootViewController: controller)
69+
70+
if let style = ApphudInternal.shared.delegate?.apphudScreenPresentationStyle?(){
71+
nc.modalPresentationStyle = style
72+
}
73+
6974
nc.setNavigationBarHidden(true, animated: false)
7075
visibleViewController?.present(nc, animated: true, completion: nil)
7176
controller.constructView()
@@ -175,19 +180,26 @@ internal class ApphudInquiryController: UIViewController {
175180

176181
@objc private func dismissTapped(){
177182
apphudLog("dismiss tapped")
178-
dismiss(animated: true, completion: nil)
183+
self.dismiss()
179184
}
180185

181186
@objc private func handleOpenBilling(sender: ApphudInquiryButton){
182187
if let url = URL(string: "https://apps.apple.com/account/billing"), UIApplication.shared.canOpenURL(url){
183188
sender.isEnabled = false
184189
ApphudInternal.shared.trackRuleEvent(ruleID: self.rule.id, params: ["kind" : "update_payment_tapped"]) {
185-
self.dismiss(animated: true, completion: nil)
190+
self.dismiss()
186191
UIApplication.shared.open(url)
187192
}
188193
}
189194
}
190195

196+
private func dismiss(){
197+
ApphudInternal.shared.delegate?.apphudWillDismissScreen?()
198+
(self.navigationController ?? self).dismiss(animated: true) {
199+
ApphudInternal.shared.delegate?.apphudDidDismissScreen?()
200+
}
201+
}
202+
191203
private func loadScreensInAdvance(){
192204
for option in self.rule.options {
193205
if option.optionAction == .presentPurchase {

Source/ApphudInternal.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
import AdSupport
1111
import StoreKit
1212

13-
let sdk_version = "0.6.3"
13+
let sdk_version = "0.6.5"
1414

1515
final class ApphudInternal {
1616

@@ -126,11 +126,7 @@ final class ApphudInternal {
126126

127127
@objc private func handleDidBecomeActive(){
128128

129-
#if DEBUG
130-
let minCheckInterval :Double = 10
131-
#else
132-
let minCheckInterval :Double = 5*60
133-
#endif
129+
let minCheckInterval :Double = 5
134130

135131
if Date().timeIntervalSince(lastCheckDate) > minCheckInterval{
136132
self.checkForUnreadNotifications()
@@ -249,7 +245,7 @@ final class ApphudInternal {
249245

250246
if finalResult {
251247
if hasSubscriptionChanges {
252-
self.delegate?.apphudSubscriptionsUpdated?(self.currentUser!.subscriptions!)
248+
self.delegate?.apphudSubscriptionsUpdated?(self.currentUser!.subscriptions)
253249
}
254250
if UserDefaults.standard.bool(forKey: self.requiresReceiptSubmissionKey) {
255251
self.submitReceiptRestore(allowsReceiptRefresh: false)
@@ -382,7 +378,7 @@ final class ApphudInternal {
382378
}
383379

384380
let exist = performWhenUserRegistered {
385-
self.submitReceipt(receiptString: receiptString, notifyDelegate: false) { error in
381+
self.submitReceipt(receiptString: receiptString, notifyDelegate: true) { error in
386382
callback?(Apphud.subscriptions()?.first(where: {$0.productId == productId}), error)
387383
}
388384
}
@@ -418,7 +414,7 @@ final class ApphudInternal {
418414
if self.parseUser(response) {
419415
// do not call delegate method only purchase, use callback instead
420416
if notifyDelegate {
421-
self.delegate?.apphudSubscriptionsUpdated?(self.currentUser!.subscriptions!)
417+
self.delegate?.apphudSubscriptionsUpdated?(self.currentUser!.subscriptions)
422418
}
423419
}
424420
}
@@ -495,10 +491,10 @@ final class ApphudInternal {
495491
let didSendReceiptForPromoEligibility = "ReceiptForPromoSent"
496492

497493
// not found subscriptions, try to restore and try again
498-
if self.currentUser?.subscriptions?.count ?? 0 == 0 && !UserDefaults.standard.bool(forKey: didSendReceiptForPromoEligibility){
494+
if self.currentUser?.subscriptions.count ?? 0 == 0 && !UserDefaults.standard.bool(forKey: didSendReceiptForPromoEligibility){
499495
if let receiptString = receiptDataString() {
500496
apphudLog("Restoring subscriptions for promo eligibility check")
501-
self.submitReceipt(receiptString: receiptString, notifyDelegate: false, callback: { error in
497+
self.submitReceipt(receiptString: receiptString, notifyDelegate: true, callback: { error in
502498
UserDefaults.standard.set(true, forKey: didSendReceiptForPromoEligibility)
503499
self._checkPromoEligibilitiesForRegisteredUser(products: products, callback: callback)
504500
})
@@ -566,10 +562,10 @@ final class ApphudInternal {
566562

567563
let didSendReceiptForIntroEligibility = "ReceiptForIntroSent"
568564

569-
if self.currentUser?.subscriptions?.count ?? 0 == 0 && !UserDefaults.standard.bool(forKey: didSendReceiptForIntroEligibility){
565+
if self.currentUser?.subscriptions.count ?? 0 == 0 && !UserDefaults.standard.bool(forKey: didSendReceiptForIntroEligibility){
570566
if let receiptString = receiptDataString() {
571567
apphudLog("Restoring subscriptions for intro eligibility check")
572-
self.submitReceipt(receiptString: receiptString, notifyDelegate: false, callback: { error in
568+
self.submitReceipt(receiptString: receiptString, notifyDelegate: true, callback: { error in
573569
UserDefaults.standard.set(true, forKey: didSendReceiptForIntroEligibility)
574570
self._checkIntroEligibilitiesForRegisteredUser(products: products, callback: callback)
575571
})

Source/ApphudNotificationsHandler.swift

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,32 @@ import UserNotifications
1212

1313
internal class ApphudNotificationsHandler {
1414

15-
static let shared = ApphudNotificationsHandler()
16-
17-
var presentingScreenController : Any?
15+
static let shared = ApphudNotificationsHandler()
16+
var handledRules = [String]()
1817

1918
@discardableResult internal func handleNotification(_ apsInfo: [AnyHashable : Any]) -> Bool{
2019

20+
guard let rule_id = apsInfo["rule_id"] as? String else {
21+
return false
22+
}
23+
guard !handledRules.contains(rule_id) else {
24+
return true
25+
}
26+
2127
apphudLog("handle APS: \(apsInfo as AnyObject)")
2228

23-
if let rule_id = apsInfo["rule_id"] as? String {
24-
25-
ApphudInternal.shared.getRule(ruleID: rule_id) { rule in
26-
if rule != nil {
27-
ApphudInquiryController.show(rule: rule!)
28-
}
29+
handledRules.append(rule_id)
30+
ApphudInternal.shared.getRule(ruleID: rule_id) { rule in
31+
if rule != nil {
32+
ApphudInquiryController.show(rule: rule!)
2933
}
30-
31-
return true
3234
}
3335

34-
return false
36+
// allow handling the same push notification rule after 5 seconds. This is needed for testing rules from Apphud dashboard
37+
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
38+
self.handledRules.removeAll()
39+
}
40+
41+
return true
3542
}
3643
}

Source/ApphudScreenController.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,10 @@ class ApphudScreenController: UIViewController{
318318
}
319319
self.dismiss()
320320
} else {
321-
apphudLog("Couldn't purchase error:\(error?.localizedDescription ?? "")", forceDisplay: true)
321+
apphudLog("Couldn't purchase, but will restore and close. Error:\(error?.localizedDescription ?? "")", forceDisplay: true)
322322
// if error occurred, restore subscriptions
323323
Apphud.restoreSubscriptions { subscriptions in
324-
324+
self.dismiss()
325325
}
326326
}
327327
}
@@ -332,7 +332,11 @@ class ApphudScreenController: UIViewController{
332332

333333
private func dismiss(){
334334
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(failedByTimeOut), object: nil)
335-
(self.navigationController ?? self).dismiss(animated: true, completion: nil)
335+
336+
ApphudInternal.shared.delegate?.apphudWillDismissScreen?()
337+
(self.navigationController ?? self).dismiss(animated: true) {
338+
ApphudInternal.shared.delegate?.apphudDidDismissScreen?()
339+
}
336340
}
337341

338342
private func restoreTapped(){
@@ -361,6 +365,7 @@ class ApphudScreenController: UIViewController{
361365

362366
if UIApplication.shared.canOpenURL(navigationURL){
363367
let controller = SFSafariViewController(url: navigationURL)
368+
controller.modalPresentationStyle = self.navigationController?.modalPresentationStyle ?? .fullScreen
364369
present(controller, animated: true, completion: nil)
365370
}
366371
}

Source/ApphudUser.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ internal struct ApphudUser {
1414
/**
1515
Unique user identifier. This can be updated later.
1616
*/
17-
let user_id : String
17+
var user_id : String
1818
/**
1919
An array of subscriptions that user has ever purchased.
2020
*/
21-
let subscriptions : [ApphudSubscription]?
21+
var subscriptions : [ApphudSubscription]
2222

2323
// MARK:- Private methods
2424

@@ -37,13 +37,13 @@ internal struct ApphudUser {
3737
if subs.count > 0 {
3838
self.subscriptions = subs.sorted{ return $0.expiresDate > $1.expiresDate }
3939
} else {
40-
self.subscriptions = nil
40+
self.subscriptions = []
4141
}
4242
}
4343

4444
func subscriptionsStates() -> [String : String] {
4545
var dict = [String : String]()
46-
for subscription in self.subscriptions ?? [] {
46+
for subscription in self.subscriptions {
4747
dict[subscription.productId] = subscription.status.toString()
4848
}
4949
return dict

apphud/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
2020

2121
let APPHUD_API_KEY = "YOUR_API_KEY"
2222
Apphud.start(apiKey: APPHUD_API_KEY)
23-
23+
2424
registerForNotifications()
2525

2626
return true

0 commit comments

Comments
 (0)