Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

这篇具有很好参考价值的文章主要介绍了Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

准备工作

  1. 苹果后台设置
  2. 创建工程导入内购插件

需要详细步骤请查看:
Unity 之 接入IOS内购过程解析

Unity内购官方文档

Mac支付和IOS逻辑基本一致,这是我之前做IOS内购时的思维导图,可以看下,先有个概念:
Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)


一,具体实现

1.1 场景搭建

创建四个按钮,分别为购买道具清空日志购买非消耗道具恢复购买 ;为了方便查看日志,我还创建了一个ScrollView组件下面放了一个Text接受日志输出。

创建完成效果如下:
Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)


1.2 代码实现

完整代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.UI;

/// <summary>
/// IAP管理类
/// </summary>
public class IAPManagerTest : MonoBehaviour, IStoreListener
{
    public Text riZhiText;
    
    /// <summary>
    /// 需要换成对应游戏后台的key
    /// </summary>
    private string[] goodsList = new string[]
    {
        "com.Czhenya.zuan10",
    };
    
    /// <summary>
    /// 非消耗型道具 -- 去除广告的id
    /// </summary>
    private string removedsId = "com.Czhenya.delad";

    private bool isRestore = false;
    
    // 控制器
    private IStoreController controller;

    // 苹果扩展
    private IAppleExtensions appleExtensions;

    // 谷歌商店扩展
    private IGooglePlayStoreExtensions googlePlayStoreExtensions;
    
    private static IExtensionProvider extensionProvider;

    // 是否可以发起购买
    private bool isCanOnClickBubBtn = false;

    void Start()
    {
        Application.targetFrameRate = 60;
        Init();
    }

    /// <summary>
    /// 初始化
    /// </summary>
    private void Init()
    {
        // 没有网络,IAP会一直初始化
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            Debug.Log("----- 用户没有连接网络 IAP不可用 ------");
            riZhiText.text += "----- 用户没有连接网络 IAP不可用 ------\n";
        }

        var module = StandardPurchasingModule.Instance();
        ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
        // builder.AddProduct("商品id1", ProductType.Consumable); 
        // ProductType :和后台说明对应
        // consumable:可消费的,如游戏中的金币,用完还可以再购买。
        // non-consumable:不可销毁的,一次购买,永久生效。比如去广告,解锁游戏关卡,这种商品只能购买一次。
        // subscription:订阅的,这种一般用于新闻、杂志、或者app里面的月卡。可以按月或者按年收费。
        for (int i = 0; i < goodsList.Length; i++)
        {
            builder.AddProduct(goodsList[i], ProductType.Consumable);
        }
        
        // 不可销毁的,一次购买,永久生效。比如去广告,解锁游戏关卡,这种商品只能购买一次。
        builder.AddProduct(removedsId, ProductType.NonConsumable);

        riZhiText.text += "----- 开始初始化... ------\n";
        // 开始初始化
        UnityPurchasing.Initialize(this, builder);
    }

    /// <summary>
    /// 初始化成功 -- 接口函数
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="extensions"></param>
    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        Debug.Log("【Unity IAP】初始化成功 IAP initialize success");
        riZhiText.text += "【Unity IAP】初始化成功 IAP initialize success\n";
        isCanOnClickBubBtn = true;
        this.controller = controller;

        // 回调赋值
        extensionProvider = extensions;
        appleExtensions = extensions.GetExtension<IAppleExtensions>();
        googlePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();

        //登记 购买延迟 监听器
        appleExtensions.RegisterPurchaseDeferredListener(OnDeferred);
    }

    //购买延迟提示
    private void OnDeferred(Product item)
    {
        Debug.Log("【Unity IAP】 网速慢.................");
        riZhiText.text += "【Unity IAP】 网速慢.................\n";
    }

    /// <summary>
    /// 初始化失败回调 -- 接口函数
    /// </summary>
    /// <param name="error"></param>
    public void OnInitializeFailed(InitializationFailureReason error)
    {
        Debug.LogError("【Unity IAP】初始化失败 OnInitializeFailed, reason:" + error.ToString());
        riZhiText.text += "【Unity IAP】初始化失败 OnInitializeFailed, reason:" + error.ToString() + "\n";
    }

    /// <summary>
    /// 购买失败回调 -- 接口函数
    /// </summary>
    /// <param name="i"></param>
    /// <param name="p"></param>
    public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
    {
        Debug.LogError("【Unity IAP】购买失败 OnPurchaseFailed,reason:" + p.ToString());
        riZhiText.text += "【Unity IAP】购买失败 OnPurchaseFailed,reason:" + p.ToString() + "\n";
        if (this.onPurchaseFailed != null)
        {
            this.onPurchaseFailed();
            this.onPurchaseFailed = null;
        }
    }

    /// <summary>
    /// 购买成功回调 -- 接口函数
    /// </summary>
    /// <param name="e"></param>
    /// <returns></returns>
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    {
        Debug.Log("【Unity IAP】购买成功 purchase finished, apple return receipt:" + e.purchasedProduct.receipt);
        //riZhiText.text += "【Unity IAP】购买成功 purchase finished, apple return receipt:" + e.purchasedProduct.receipt + "\n";
        riZhiText.text += "【Unity IAP】购买成功 e.purchasedProduct.definition.id:" + e.purchasedProduct.definition.id + "\n";
        riZhiText.text += "【Unity IAP】恢复购买成功 isRestore: " + isRestore + "\n";

        if (isRestore) // 恢复购买
        {
            Debug.Log("恢复购买成功 isRestore " + isRestore);
   
            // 判断是否是去除广告id
            if (removedsId.Equals(e.purchasedProduct.definition.id))
            {
                Debug.Log("恢复购买成功");
                // todo... 恢复成功回调
                isRestore = false;
            }
            else
            {
                onPurchaseFailed?.Invoke();
            }

            return PurchaseProcessingResult.Complete;
        }
        
        if (this.onPurchaseSuccess != null)
        {
            this.onPurchaseSuccess(e.purchasedProduct.receipt);
            this.onPurchaseSuccess = null;
        }

        return PurchaseProcessingResult.Complete;
    }

    /// <summary>
    /// 支付失败回调
    /// </summary>
    private Action onPurchaseFailed;

    /// <summary>
    /// 支付成功回调
    /// </summary>
    private Action<string> onPurchaseSuccess;

    /// <summary>
    /// 购买产品
    /// </summary>
    /// <param name="productId">产品ID</param>
    /// <param name="onFailed">失败回调</param>
    /// <param name="onSuccess">成功回调</param>
    public void PurchaseProduct(string productId, Action onFailed, Action<string> onSuccess)
    {
        this.onPurchaseFailed = onFailed;
        this.onPurchaseSuccess = onSuccess;

        if (controller != null)
        {
            var product = controller.products.WithID(productId);
            if (product != null && product.availableToPurchase)
            {
                Debug.Log("【Unity IAP】开始购买");
                riZhiText.text += "【Unity IAP】开始购买... \n";

                controller.InitiatePurchase(productId);
            }
            else
            {
                Debug.LogError("【Unity IAP】失败回调 no product with productId:" + productId);
                riZhiText.text += "【Unity IAP】失败回调 no product with productId:" + productId + " \n";
                if (this.onPurchaseFailed != null)
                {
                    this.onPurchaseFailed();
                }
            }
        }
        else
        {
            Debug.LogError("【Unity IAP】失败回调 controller is null,can not do purchase");
            riZhiText.text += "Unity IAP】失败回调 controller is null,can not do purchase \n";
            if (this.onPurchaseFailed != null)
            {
                this.onPurchaseFailed();
            }
        }
    }

    /// <summary>
    /// 发起购买函数  -- 商城按钮监听
    /// </summary>
    /// <param name="i"></param>
    public void OnClickPurchase(int i)
    {
        // 正式项目时需限制 -- 不允许多次点击 

        Debug.Log("【Unity IAP】发起购买函数 " + Application.internetReachability);
        riZhiText.text += "【Unity IAP】发起购买函数  "+Application.internetReachability+" \n";
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            Debug.Log("【Unity IAP】用户没网... ");
            return;
        }

        PurchaseProduct(goodsList[0], OnBuyFailed, OnBuySuccess);
    }

    #region 购买回复非消耗道具

    /// <summary>
    /// 购买非消耗道具 -- 商城按钮监听
    /// </summary>
    public void OnClickRemoved()
    {
        // 正式项目时需限制 -- 不允许多次点击 

        Debug.Log("【Unity IAP】购买一次性道具 " + Application.internetReachability);
        riZhiText.text += "【Unity IAP】购买一次性道具  "+Application.internetReachability+" \n";
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            Debug.Log("【Unity IAP】用户没网... ");
            return;
        }

        PurchaseProduct(removedsId, OnBuyFailed, OnBuySuccess);
    }
    
    /// <summary>
    /// 恢复购买非消耗道具 -- 商城按钮监听
    /// </summary>
    public void OnClickRecover()
    {
        // 正式项目时需限制 -- 不允许多次点击 

        Debug.Log("【Unity IAP】恢复购买 " + Application.internetReachability);
        riZhiText.text += "【Unity IAP】恢复购买  "+Application.internetReachability+" \n";
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            Debug.Log("【Unity IAP】用户没网... ");
            return;
        }

        if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer)
        {
            Debug.Log("发起恢复请求");
            isRestore = true;
            IAppleExtensions apple = extensionProvider.GetExtension<IAppleExtensions>();
            apple.RestoreTransactions(HandleRestored);
        }
        else
        {
            Debug.Log("恢复购买失败. 不支持这个平台. 当前平台 = " + Application.platform);
        }

    }
    
    // 恢复购买之后,会返回一个状态,如果状态为true,
    // 之前购买的非消耗物品都会回调一次购买成功(ProcessPurchase)
    // 然后在这里个回调里面进行处理
    void HandleRestored(bool result)
    {
        // 返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase)
        Debug.Log("恢复购买继续: " + result + ". 如果没有进一步的消息,则没有可恢复的购买。");
        isRestore = result;
        riZhiText.text += "【Unity IAP】恢复购买继续  " + result + ". 如果没有进一步的消息,则没有可恢复的购买。 \n";
        if (result)
        {
            riZhiText.text += "【Unity IAP】恢复购买成功! \n";
            Debug.Log("恢复购买成功!");
        }
        else
        {
            riZhiText.text += "【Unity IAP】恢复购买失败! \n";
            Debug.Log("恢复购买失败!");
        }
        // todo...回调处理
    }

    #endregion
    
    /// <summary>
    /// 购买失败回调
    /// </summary>
    void OnBuyFailed()
    {
        Debug.Log("【Unity IAP】购买失败回调 OnBuyFailed...");
        riZhiText.text += "【Unity IAP】购买失败回调 OnBuyFailed... \n";
    }

    /// <summary>
    /// 购买成功回调
    /// </summary>
    /// <param name="str"></param>
    void OnBuySuccess(string str)
    {
        Debug.Log("【Unity IAP】购买成功回调 OnBuySuccess..." + str);
        riZhiText.text += "【Unity IAP】购买成功回调 OnBuySuccess... \n";
        riZhiText.text += "【Unity IAP】购买成功...收据: " + str + " \n";
        //会得到下面这样一个字符串
        //{"Store":"AppleAppStore",
        //"TransactionID":"1000000845663422",
        //"Payload":"MIIT8QYJKoZIhvcNAQcCoIIT4jCCE94CAQExBBMMIIBa ... 还有N多 ..."}
    }

    public void ClearRiZhi()
    {
        riZhiText.text = "清空数据\n";
    }
}

PS:此代码为上图使用的测试代码,按钮点击监听赋值,在Inspector面板下拖拽赋值。正式使用时可自行删除注释或者点击获取源码。


1.3 打包设置

将包名修改为与后台一致,其他属性默认即可。若需要更多设置,可参考:Unity 之 打包参数 – Player面板属性详解
Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)


二,打包测试

2.1 实现步骤说明

Mac内购流程打包步骤

  1. 使用正式包名
    和苹果后台创建的对应上,直接在Unity里面设置好。
  2. 签名app并打包为pkg
    若在Unity中没有设置正确包名,也可以直接在打包处理的app,右键显示包内容,找到信息.plist文件并将CFBundleIdentifier字符串更新为应用程序的包名。
    在2.2中还有详细的签名和导出pkg的步骤
  3. 安装pkg并调用初始化内购项
    要正确安装软件包,请删除未打包的。运行新创建的软件包并安装它之前的应用程序文件。
    在3.1中有测试步骤实现过程
  4. 调用购买并尝试购买查看返回数据
    测试结果和支付验证

2.2 Mac签名命令

签名需要两个证书和一个签名文件,若之前都没搞过,则可以参考:Unity 之 上传Mac App Store过程详解
文章中有详细获取证书步骤和签名配置所需文件。

  1. 设置权限
chmod -R a+xr "/Users/Czhenya/Desktop/Mac/你的.app"
  1. 文件签名
codesign -o runtime -f --deep -s '3rd Party Mac Developer Application: 证书.' --entitlements "/Users/Czhenya/Desktop/App.entitlements" "/Users/Czhenya/Desktop/Mac/你的.app"
  1. 打包pkg
productbuild --component /Users/Czhenya/Desktop/Mac/你的.app /Applications --sign "3rd Party Mac Developer Installer: 证书." /Users/Czhenya/Desktop/Mac/你的.pkg

三,示例演示

3.1 购买商品

  1. 商品初始化成功:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  2. 输入沙盒账号:(首次使用会有双重认证之类的确保身份安全,可选择跳过或按照提示操作即可)
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  3. 若是第一次登录,需要确认Apple ID 安全,点击继续:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  4. 若出现“保护您的账号”提示,选择不升级即可:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  5. 最后终于到了支付购买界面了,点击购买就可了:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  6. 购买完成后,会弹出操作完成提示,点击“好“即可触发支付成功回调(沙盒会稍微慢一点)
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  7. 支付成功回调:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

  8. 取消支付回调:
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)


3.2 购买非消耗道具

  1. 初始化成功后,点击购买非消耗道具
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

3.3 恢复购买

  1. 购买过一次之后,再次购买会购买失败,这时需要点击恢复购买,执行恢复购买逻辑
    Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)

四,支付验证

若是单机游戏无需服务器进行支付验证,则按照成功回调发放奖励跳过此步骤即可。若需要服务器验证,则将支付成功的Payload传到服务器,获取验证结果后发放奖励或提示支付失败。

4.1 验证返回数据

服务端验证返回数据
iOS发起票据验证请求后,通过处理AppStore返回数据来验单。服务验证需要注意的地方:不同iOS版本的返回数据不同,服务端验证方式也不同。

  1. iOS7及以上获取的票据返回数据:
{
    receipt =  {
        "adam_id" = 0,
        "app_item_id" = 0,
        "application_version" = 1,
        "bundle_id" = "com.Czhenya",
        "download_id" = 0,
        "in_app" = {
            {
                "is_trial_period" = false,
                "original_purchase_date" = "2022-10-24 01:00:00 Etc/GMT",
                "original_purchase_date_ms" = 1483203661000,
                "original_purchase_date_pst" = "2022-10-24 01:00:00 America/Los_Angeles",
                "original_transaction_id" = 1000000000000001,
                "product_id" = "com.Czhenya.zuan10",
                "purchase_date" = "2022-10-24 01:00:00 Etc/GMT",
                "purchase_date_ms" = 1483203661000,
                "purchase_date_pst" = "2022-10-24 01:00:00 America/Los_Angeles",
                "transaction_id" = 1000000000000001
            }
        },
        "receipt_type" = "ProductionSandbox",
        "request_date" = "2022-10-24 01:00:00 Etc/GMT",
        "request_date_ms" = 1483203661000,
        "request_date_pst" = "2022-10-24 01:00:00 America/Los_Angeles",
        "version_external_identifier" = 0,
    },
    status = 0
}
  1. iOS7以下获取的票据返回数据(不包括iOS7):
{
    receipt = {
        "bid" = "com.Czhenya",
        "bvrs" = 1,
        "item_id" = 573837050,
        "original_purchase_date" = "2022-10-24 01:00:00 Etc/GMT",
        "original_purchase_date_ms" = 1483203661000,
        "original_purchase_date_pst" = "2022-10-24 01:00:00 America/Los_Angeles",
        "original_transaction_id" = 1000000000000001,
        "product_id" = "com.Czhenya.zuan10",
        "purchase_date" = "2022-10-24 01:00:00 Etc/GMT",
        "purchase_date_ms" = 1483203661000,
        "purchase_date_pst" = "2022-10-24 01:00:00 America/Los_Angeles",
        "transaction_id" = 1000000000000001
    },
    status = 0
}

验证订单是否成功,关键看这几个数据

  1. status为 0 表示成功;其他都为失败,表示失败原因。
  2. 根据 receipt.in_app 字段判断iOS版本,验证方法也不同:
  • iOS7及以上:有in_app字段,验证 receipt.bundle_id 是否为你 App 的 bundle id,根据 in_app 处理充值的每一笔订单, 根据 product_id 判断用户充值了哪个档位,同时取出 transaction_id。
  • iOS7以下:没有in_app字段,验证 receipt.bid 是否为你 App 的 bundle id,根据 product_id 判断用户充值了哪个档位,同时取出 transaction_id。
  1. 根据 transaction_id 对比数据库历史订单判断是否已处理过,没有则认为本次充值是有效的。

4.2 状态码说明

AppStore 服务器有两个,对应测试环境(沙盒测试)和正式环境:

  • 沙盒验证地址:https://sandbox.itunes.apple.com/verifyReceipt
  • 正式验证地址:https://buy.itunes.apple.com/verifyReceipt
状态码 说明
21000 未使用HTTP POST请求方法向App Store发送请求。
21001 此状态代码不再由App Store发送。
21002 receipt-data属性中的数据格式错误,或者服务遇到了临时问题。再试一次。
21003 收据无法认证。
21004 您提供的共享密钥与您帐户的文件共享密钥不匹配。
21005 收据服务器暂时无法提供收据。再试一次。
21006 该收据有效,但订阅已过期。当此状态码返回到您的服务器时,收据数据也会被解码并作为响应的一部分返回。仅针对自动续订的iOS 6样式的交易收据返回。
21007 该收据来自测试环境,但是已发送到生产环境以进行验证。
21008 该收据来自生产环境,但是已发送到测试环境以进行验证。
21009 内部数据访问错误。稍后再试。
21010: 找不到或删除了该用户帐户。

源码和步骤都在上面分享过了,若还有什么不明白的,可以点击链接下载,积分不够的童鞋关注下方卡片公号​,回复:Mac内购 即可获得Demo源码~文章来源地址https://www.toymoban.com/news/detail-401917.html

到了这里,关于Unity 之 Mac App Store 内购过程解析(购买非消耗道具 | 恢复购买 | 支付验证)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Unity】Unity接入内购IAP,提示you are not authorized to set the license key

    接入IAP的时候需要输入谷歌的开发者后台key Unity2020之后有可能会提示:you are not authorized to set the license key 查阅相关内容后(https://forum.unity.com/threads/purchase-you-are-not-authorized-to-set-the-license-key-google-play.954261/) Unity2020后不在Editor上面填写了,改成在Dashboard上输入 打开后输入即

    2024年02月08日
    浏览(46)
  • GPT Store,是否会成为下一个App Store?

    经历了一场风波后,原本计划推出的GPT Store终于成功上线。OpenAI在北京时间1月11日推出了GPT Store,被广泛视为类似于苹果的\\\"App Store\\\",为人工智能应用生态系统迈出了重要一步。然而,OpenAI要想将GPT Store打造成苹果般的成功,还需要一些时间。尽管如此,GPT Store的推出对于国

    2024年02月01日
    浏览(41)
  • iOS App上传App Store的详细流程步骤

    作为一名iOSDeveloper,把开发出来的App上传到App Store是必要的。下面就来详细讲解一下具体流程步骤。 一个已付费的开发者账号(账号类型分为个人(Individual)、公司(Company)、企业(Enterprise)、高校(University)四种类型,每年资费分别为$99、$99、$299、免费。)。 一个已经

    2024年02月05日
    浏览(51)
  • ipa上架App Store流程

    1)提交ipa工具:Transporter(Mac版) 苹果提供 Transporter 来处理大量和差异化数据的操作的工具(可以在 macOS、Windows 和 Linux 操作系统上安装和运行 Transporter。): Transporter 是 Apple 基于 Java 的命令行工具,用于进行大量目录交付。您可以使用 Transporter 将预生成的内容以 Store 数据

    2024年02月05日
    浏览(51)
  • App怎么上架到苹果商店(app store)?上架app的流程。

    1、申请开发者账号 苹果的开发者账号主要分为三种类型:个人账号、公司账号、企业账号,三者的费用以及权限等都不尽相同。开发者账号的申请过程一般需要一个星期左右的时间。一般开发者申请的都是个人或者公司的账号,因为企业账号的应用不能发布到App Store,只能

    2024年02月06日
    浏览(52)
  • 个人建站之域名购买、解析、备案、网站绑定

    从头开始学习个人建站全流程:选择域名服务商(阿里云、腾讯云)、购买域名、解析设置、备案流程详解,以及如何将域名绑定到云服务器。详细教程帮助您轻松搭建个人网站!

    2024年02月02日
    浏览(100)
  • Xcode上传App Store Connect流程

    2024年02月16日
    浏览(35)
  • 2023年iOS App Store上架流程详解(上)

    目录 1.注册开发者账号 2.登录并配置人员 3.申请证书和配置文件 一.证书管理​ 二.新建证书​ 三.使用appuploader服务同步证书​ 1)申请证书 2)添加Identifiers和配置App ID 3)申请配置文件 1.在Xcode项目中配置签名 2.上传应用包和审核 3.发布前的准备工作 4.总结与注意事项 在20

    2024年02月16日
    浏览(47)
  • 优化Unity日志系统的消耗及在ILRuntime模式下双击能跳转到对应的文件行号

    Unity的日志控制: 日志系统打开,但是只打印错误日志。这样其他级别的日志就不会有打印消耗。 但是还是有字符串拼接的消耗。 Conditional属性是一个C#特性,它允许你根据预处理器指令的定义来有条件地执行方法。例如下面的代码: 如果没有OPEN_MAIN_LOG_LOGWARNING宏,编译的时

    2024年02月11日
    浏览(29)
  • iOS踩坑App Store Connect Operation Error

    参考CSND: HTML5 CSS控制Table内外边框、颜色、大小示例_@虎哥132的博客-CSDN博客 Xcode 打包上传 App Store 报错 : 报错如图: App Store Connect Operation Error SDK Version Issue.   This app was built with the iOS 15.5 SDK.   Starting April 2023, all iOS apps submitted to the App Store must be built with the iOS 16.1 SDK or later,

    2024年02月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包