Подписка на покупку в приложении для iOS получает бесплатный пробный период от SKProduct

Я работаю над покупками в приложении с подписками. В Swift вы можете получить цену и локаль цены из SKProduct следующим образом:

weeklyProduct.price.doubleValue  
weeklyProduct.priceLocale.currencySymbol

где weeklyProduct — это SKProduct.

Можно ли получить бесплатную пробную версию? Например, я указал для продукта двухнедельную бесплатную пробную версию. я могу получить это от SKProduct?


person mjpablo23    schedule 22.08.2017    source источник
comment
Нет. Вы можете получить информацию из квитанции это указывает, находится ли подписка в настоящее время в бесплатном пробном периоде, но не продолжительность бесплатного пробного периода   -  person Paulw11    schedule 23.08.2017
comment
а ок, спасибо. понятно.   -  person mjpablo23    schedule 23.08.2017


Ответы (9)


Я решил эту проблему с помощью 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 следующим образом. Обратите внимание, что я пока не реализовал другие способы оплаты.

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
        }
    }
}
person Roel van der Kraan    schedule 03.07.2019
comment
Самое элегантное решение, так как правильно учитывает локализацию. - person Niels Mouthaan; 02.02.2021
comment
Согласен, очень красиво сделано Роэл. - person vomi; 27.03.2021

Получить его можно, но, как было сказано выше, он работает только начиная с 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 ""
    }
}
person Binshakerr    schedule 30.05.2018

Используя ответ Эслама в качестве вдохновения, я создал расширение для 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 года)

person Scott Wood    schedule 02.08.2018
comment
Отличный ответ. Если вы хотите локализовать, используйте stringdicts вместо переменной plural: developer.apple.com/library/archive/documentation/MacOSX/ - person vmeyer; 08.10.2018

Хорош @Скотт Вуд. Я бы сделал это свойством 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.

И да, как заметил кто-то другой, вам следует локализовать множественное число, если ваше приложение доступно на других языках.

person kakubei    schedule 16.04.2019

Продолжительность пробной версии не включена в информацию SKProduct и должна быть жестко запрограммирована в приложении или сохранена на вашем сервере. Единственный доступный вариант получения информации такого типа (в настоящее время) — это сама квитанция.

person Garrett Cox    schedule 11.10.2017

Начиная с iOS 11.2 вы можете получать информацию о пробных версиях, используя introductoryPrice свойство SKProduct.

Он содержит экземпляр класса SKProductDiscount, который описывает все периоды скидок, включая бесплатные испытания.

person DanSkeel    schedule 06.01.2018

Свифт 5

Использование Эслама и Скотта отвечает как вдохновение:

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
person Ty Lertwichaiworawit    schedule 15.05.2020

ЦЕЛЬ С

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

}

person Ofir Malachi    schedule 13.12.2020

Вот более компактная и короткая в использовании версия для 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)"
    }
}
person bodich    schedule 13.12.2020