【问题标题】:iOS in-app purchase subscription get free trial period length from SKProductiOS 应用内购买订阅从 SKProduct 获得免费试用期长度
【发布时间】:2018-01-31 04:19:32
【问题描述】:

我正在通过订阅进行应用内购买。 在 swift 中,您可以像这样从 SKProduct 获取价格和价格区域设置:

weeklyProduct.price.doubleValue  
weeklyProduct.priceLocale.currencySymbol

weeklyProduct 是一个 SKProduct。

是否可以获得免费试用期?例如,我为该产品指定了两周的免费试用期。我可以从 SKProduct 获得这个吗?

【问题讨论】:

  • 没有。您可以从receipt获取信息,表明订阅当前是否处于免费试用期,而不是免费试用期
  • 啊,好的,谢谢。明白了。

标签: ios in-app-purchase storekit in-app-subscription skproduct


【解决方案1】:

如果你使用SwiftyStoreKitlocalizedSubscriptionPeriod是最简单的方法

import SwiftyStoreKit

product.introductoryPrice?.localizedSubscriptionPeriod // "1 week"

这是实现: https://github.com/bizz84/SwiftyStoreKit/blob/master/Sources/SwiftyStoreKit/SKProductDiscount+LocalizedPrice.swift

【讨论】:

    【解决方案2】:

    目标C

    #import "SKProduct+SKProduct.h"
    
    -(NSString*_Nullable)localizedTrialDuraion{
    
    if (@available(iOS 11.2, *)) {
        
        NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
        [formatter setUnitsStyle:NSDateComponentsFormatterUnitsStyleFull]; //e.g 1 month
        formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;
        NSDateComponents * dateComponents = [[NSDateComponents alloc]init];
        [dateComponents setCalendar:[NSCalendar currentCalendar]];
        
        switch (self.introductoryPrice.subscriptionPeriod.unit) {
            case SKProductPeriodUnitDay:{
                formatter.allowedUnits = NSCalendarUnitDay;
                [dateComponents setDay:self.introductoryPrice.subscriptionPeriod.numberOfUnits];
                break;
            }
            case SKProductPeriodUnitWeek:{
                formatter.allowedUnits = NSCalendarUnitWeekOfMonth;
                [dateComponents setWeekOfMonth:self.introductoryPrice.subscriptionPeriod.numberOfUnits];
                break;
            }
            case SKProductPeriodUnitMonth:{
                formatter.allowedUnits = NSCalendarUnitMonth;
                [dateComponents setMonth:self.introductoryPrice.subscriptionPeriod.numberOfUnits];
                break;
            }
            case SKProductPeriodUnitYear:{
                formatter.allowedUnits = NSCalendarUnitYear;
                [dateComponents setYear:self.introductoryPrice.subscriptionPeriod.numberOfUnits];
                break;
            }
            default:{
                return nil;
                break;
            }
                break;
        }
        [dateComponents setValue:self.introductoryPrice.subscriptionPeriod.numberOfUnits forComponent:formatter.allowedUnits];
        return [formatter stringFromDateComponents:dateComponents];
    } else {
        // Fallback on earlier versions
    }
    
    return nil;
    

    }

    【讨论】:

      【解决方案3】:

      这里是 swift 5 的更紧凑和更短的使用版本,扩展了 SKProductSubscriptionPeriod

      用法:

      print("\(period.localizedDescription) free trial")
      
      //Printed example "1 week free trial"
      

      实施:

      extension SKProductSubscriptionPeriod {
          public var localizedDescription: String {
              let period:String = {
                  switch self.unit {
                  case .day: return "day"
                  case .week: return "week"
                  case .month: return "month"
                  case .year: return "year"
                  @unknown default:
                      return "unknown period"
                  }
              }()
          
              let plural = numberOfUnits > 1 ? "s" : ""
              return "\(numberOfUnits) \(period)\(plural)"
          }
      }
      

      【讨论】:

        【解决方案4】:
        + (NSString*)localizedTitleForSKPeriod:(SKProductSubscriptionPeriod*)period{
            NSDateComponents *comps = [NSDateComponents new];
            NSDateComponentsFormatter *fmt = [NSDateComponentsFormatter new];
            switch (period.unit) {
                case SKProductPeriodUnitDay:{
                    fmt.allowedUnits = NSCalendarUnitDay;
                    comps.day = period.numberOfUnits;
                }break;
                case SKProductPeriodUnitWeek:{
                    fmt.allowedUnits = NSCalendarUnitWeekOfMonth;
                    comps.weekOfMonth = period.numberOfUnits;
                }break;
                case SKProductPeriodUnitMonth:{
                    fmt.allowedUnits = NSCalendarUnitMonth;
                    comps.month = period.numberOfUnits;
                }break;
                case SKProductPeriodUnitYear: {
                    fmt.allowedUnits = NSCalendarUnitYear;
                    comps.year = period.numberOfUnits;
                }break;
            }
            // 1 Day, 1 Week, 2 Weeks, 1 Month, 2 Months, 3 Months, 6 Months, 1 Year
            fmt.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
            // One Day, One Week, Two Weeks, etc
            //fmt.unitsStyle = NSDateComponentsFormatterUnitsStyleSpellOut;
            NSString *s = [[fmt stringFromDateComponents:comps] capitalizedString];
            return s;
        }
        

        【讨论】:

          【解决方案5】:

          斯威夫特 5

          使用Eslam'sScott's 的答案作为灵感:

          import StoreKit
          
          extension SKProduct {
              func priceString() -> String {
                  let period:String = {
                      switch self.subscriptionPeriod?.unit {
                      case .day: return "day"
                      case .week: return "week"
                      case .month: return "month"
                      case .year: return "year"
                      case .none: return ""
                      case .some(_): return ""
                      }
                  }()
          
                  let price = self.localizedPrice!
                  let numUnits = self.subscriptionPeriod?.numberOfUnits ?? 0
                  let plural = numUnits > 1 ? "s" : ""
                  return String(format: "%@ for %d %@%@", arguments: [price, numUnits, period, plural])
              }
          }
          

          使用方法:

          let price = product.priceString()
          print(price)
          

          结果:

          THB 89.00 for 7 days
          THB 149.00 for 1 month
          

          【讨论】:

            【解决方案6】:

            Eslam's answer 为灵感,我为 SKProduct.PeriodUnit 创建了一个扩展

            extension SKProduct.PeriodUnit {
                func description(capitalizeFirstLetter: Bool = false, numberOfUnits: Int? = nil) -> String {
                    let period:String = {
                        switch self {
                        case .day: return "day"
                        case .week: return "week"
                        case .month: return "month"
                        case .year: return "year"
                        }
                    }()
            
                    var numUnits = ""
                    var plural = ""
                    if let numberOfUnits = numberOfUnits {
                        numUnits = "\(numberOfUnits) " // Add space for formatting
                        plural = numberOfUnits > 1 ? "s" : ""
                    }
                    return "\(numUnits)\(capitalizeFirstLetter ? period.capitalized : period)\(plural)"
                }
            }
            

            使用方法:

            if #available(iOS 11.2, *),
                let period = prod?.introductoryPrice?.subscriptionPeriod
            {
                let desc = period.unit.description(capitalizeFirstLetter: true, numberOfUnits: period.numberOfUnits)
            } else {
                // Fallback
            }
            

            这将创建一个格式良好的字符串(例如 1 天、1 周、2 个月、2 年)

            【讨论】:

            【解决方案7】:

            我已经使用 DateComponentsFormatter 解决了这个问题,这可以为您节省大量使用不同语言本地化和处理复数等等的时间。 这可能看起来像很多代码,但我希望它会在未来节省我的时间。

            import Foundation
            
            class PeriodFormatter {
                static var componentFormatter: DateComponentsFormatter {
                    let formatter = DateComponentsFormatter()
                    formatter.maximumUnitCount = 1
                    formatter.unitsStyle = .full
                    formatter.zeroFormattingBehavior = .dropAll
                    return formatter
                }
            
                static func format(unit: NSCalendar.Unit, numberOfUnits: Int) -> String? {
                    var dateComponents = DateComponents()
                    dateComponents.calendar = Calendar.current
                    componentFormatter.allowedUnits = [unit]
                    switch unit {
                    case .day:
                        dateComponents.setValue(numberOfUnits, for: .day)
                    case .weekOfMonth:
                        dateComponents.setValue(numberOfUnits, for: .weekOfMonth)
                    case .month:
                        dateComponents.setValue(numberOfUnits, for: .month)
                    case .year:
                        dateComponents.setValue(numberOfUnits, for: .year)
                    default:
                        return nil
                    }
            
                    return componentFormatter.string(from: dateComponents)
                }
            }
            

            需要将 SKProduct 周期单位转换为 NSCalendarUnit

            import StoreKit
            
            @available(iOS 11.2, *)
            extension SKProduct.PeriodUnit {
                func toCalendarUnit() -> NSCalendar.Unit {
                    switch self {
                    case .day:
                        return .day
                    case .month:
                        return .month
                    case .week:
                        return .weekOfMonth
                    case .year:
                        return .year
                    @unknown default:
                        debugPrint("Unknown period unit")
                    }
                    return .day
                }
            }
            

            您可以像这样从 SubscriptionPeriod 调用它:

            import StoreKit
            
            @available(iOS 11.2, *)
            extension SKProductSubscriptionPeriod {
                func localizedPeriod() -> String? {
                    return PeriodFormatter.format(unit: unit.toCalendarUnit(), numberOfUnits: numberOfUnits)
                }
            }
            

            您可以像这样从 SKProductDiscount 依次调用。请注意,我暂时没有实现其他 PaymentMode。

            import StoreKit
            
            @available(iOS 11.2, *)
            extension SKProductDiscount {
                func localizedDiscount() -> String? {
                    switch paymentMode {
                    case PaymentMode.freeTrial:
                        return "Free trial for \(subscriptionPeriod.localizedPeriod() ?? "a period")"
                    default:
                        return nil
                    }
                }
            }
            

            【讨论】:

            • 最优雅的解决方案,因为它适当地考虑了本地化。
            【解决方案8】:

            不错的@scott Wood。我会将其设为SKProduct.PeriodUnit 的属性,而不是函数。这将使行为与枚举更加一致:

            @available(iOS 11.2, *)
            extension SKProduct.PeriodUnit {
            
                var description: String {
                    switch self {
                    case .day: return "day"
                    case .week: return "week"
                    case .month: return "month"
                    case .year: return "year"
                    // support for future values
                    default:
                        return "N/A"
                    }
                }
            
                func pluralisedDescription(length: Int) -> String {
                    let lengthAndDescription = length.description + " " + self.description
                    let plural = length > 1 ?  lengthAndDescription + "s" : lengthAndDescription
                    return plural
                }
            }
            

            然后是一个返回复数的函数,基于description 属性。

            是的,正如其他人指出的那样,如果您的应用程序支持其他语言,您应该本地化复数形式。

            【讨论】:

              【解决方案9】:

              您可以获取它,但如上所述,它仅从 iOS 11.2 开始有效,对于其他版本,您必须通过 API 从您的服务器获取它。

              这是我使用过的示例代码:

              if #available(iOS 11.2, *) {
                if let period = prod.introductoryPrice?.subscriptionPeriod {
                   print("Start your \(period.numberOfUnits) \(unitName(unitRawValue: period.unit.rawValue)) free trial")
                }
              } else {
                // Fallback on earlier versions
                // Get it from your server via API
              }
              
              func unitName(unitRawValue:UInt) -> String {
                  switch unitRawValue {
                  case 0: return "days"
                  case 1: return "weeks"
                  case 2: return "months"
                  case 3: return "years"
                  default: return ""
                  }
              }
              

              【讨论】:

                【解决方案10】:

                从 iOS 11.2 开始,您可以使用 SKProductintroductoryPrice 属性获取有关试验的信息。

                它包含SKProductDiscount 类的实例,描述了包括免费试用在内的所有折扣期。

                【讨论】:

                  【解决方案11】:

                  试用期不包含在 SKProduct 信息中,必须硬编码到应用程序中或存储在您的服务器上。获取此类信息的唯一可用选项(当前)是从收据本身。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2014-10-26
                    • 2011-12-26
                    • 2013-03-01
                    • 2013-01-20
                    • 2021-07-06
                    • 2013-05-26
                    • 2021-05-02
                    • 1970-01-01
                    相关资源
                    最近更新 更多