SKProduct获取iOS应用内购买订阅免费试用期长度

25

我正在开发应用内购买的订阅功能。

在Swift中,你可以通过SKProduct获取价格和价格所属地区,方法如下:

weeklyProduct.price.doubleValue  
weeklyProduct.priceLocale.currencySymbol

其中 weeklyProduct 是一个 SKProduct。

是否可以获取免费试用的长度?例如,我为该产品指定了两周的免费试用。我能从 SKProduct 中获取吗?


1
不可以。您可以从收据中获取信息,以确定订阅是否目前处于免费试用期,但无法获取免费试用期的长度。 - Paulw11
啊,好的,谢谢。我明白了。 - mjpablo23
11个回答

30
我已经使用 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中调用它。请注意,我现在没有实施其他的PaymentModes。

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

2
最优雅的解决方案,因为它正确考虑了本地化。 - Niels Mouthaan
不错,但这将返回设备语言的字符串。如果应用程序没有本地化到该语言,则会出现两种语言的UI文本。为解决此问题,请使用以下代码替换 dateComponents.calendar = Calendar.current:let interfaceLanguage = Bundle.preferredLocalizations(from:Bundle.main.localizations).first; let locale = Locale(identifier: interfaceLanguage ?? "en"); dateComponents.calendar = locale.calendar; - davidisdk

20

你可以得到它,但如上所述,它只适用于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 ""
    }
}

12

借鉴Eslam的答案的灵感,我创建了一个扩展来处理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年)


2
很棒的答案。如果你想本地化,使用字符串字典而不是 plural 变量:https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html - vmeyer
男人,它只适用于英语。每种语言可能有自己的结束方式。例如,在俄语中,您使用不同的结束方式来表示1、2-4和5+个单位计数,并且5+的“年”换成了另一个词。 - Gargo

2
很好的建议@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属性返回复数形式。

如果您的应用程序可用于其他语言,则应本地化复数形式,正如其他人指出的那样。


1

0

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


0

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

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


0

Swift 5

EslamScott 的答案为灵感:

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

0
+ (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;
}

0

Objective-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;

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接