我在 iOS 应用程序中提供订阅服务。如果应用程序在 iOS 15 或更高版本上运行,我会StoreKit 2用来处理订阅启动和续订。我的实现紧密遵循Apple 的示例代码。
我的一小部分用户 (<1%) 报告说,他们的有效订阅未被识别- 通常是在续订之后(开始新订阅似乎总是有效)。似乎没有显示任何用于续订的 StoreKit 交易。
经过一些故障排除后我发现:
AppStore.sync()没有帮助。我永远无法在我的设备上重现这个错误。
这是我的实现的要点:我有一个StoreManager类来处理与StoreKit. 初始化后,我立即迭代Transaction.all以获取用户完整的购买历史记录,并启动一个监听Transaction.updates. PurchasedItem是一个自定义结构,我用它来对有关交易的所有相关信息进行分组。购买的物品收集在字典中purchasedItems,我在字典中使用交易identifier作为键。对该字典的所有写入仅发生在绑定updatePurchasedItemFor()到MainActor.
class StoreManager {
static let shared = StoreManager()
private var updateListenerTask: Task<Void, Error>? = nil
init() {
updateListenerTask = listenForTransactions()
loadAllTransactions()
}
func loadAllTransactions() {
Task { @MainActor in
for await …Run Code Online (Sandbox Code Playgroud) 我在 App Store 上提供了一个 iOS 应用程序。随着 Apple 的 M1 Mac 的推出,可以在 macOS 上运行 iOS 应用程序。我想防止我的应用程序在 macOS 上使用,例如在启动后抛出异常或exit(0)稍后调用。
如何检测应用程序是否在 M1 Mac 上运行?
在某种程度上,在 Mac 上运行的 iOS 应用程序似乎将自己报告为 iPad,因此这排除了一些识别设备的常用方法。
提供上下文的一些细节:
我已经从 App Store Connect 上的 Mac App Store 中删除了该应用程序。
该应用程序专为触摸屏而设计和优化。当前版本在 macOS 上运行时会提供非常糟糕的用户体验。要使其成为一款出色的 Mac 应用程序,需要进行许多更改并付出大量努力。我没有任何计划,更不用说这样做的资源了。
从表面上看,这可能类似于“如何检测我的应用程序是否在越狱设备上运行?”的问题。从技术上讲,这可能是正确的,我知道通常不建议实施越狱检测以防止 IAP 黑客等。一个重要的区别是 Apple 正在积极尝试防止越狱,并强烈劝阻用户这样做,这似乎保持了越狱社区相当小且非主流,但另一方面,Apple 显然希望在 Mac 上提供尽可能多的 iOS 应用程序。目前在 Mac 上运行 iOS 应用程序非常容易,即使它们不在 Mac App Store 中提供。MacRumors和9to5mac等流行技术博客上有说明. 我想确保至少防止这种在 mac 上运行应用程序的简单方法。
此应用程序中的许多实现细节都是在假设该应用程序将只在用户无法轻松访问的 iOS 沙箱中运行的假设下开发的。现在,用户使用修改后的资源、用户默认值或目录内容(Application Support包括我认为始终不可变的文件)运行应用程序可能要容易得多。如果用户找到一种方法来访问应用程序中通常需要应用程序内购买或订阅的内容,例如通过.plist以意想不到的方式伪造s …
我正在寻找一种复制"眨眼"动画的方法,即按住home + lock时播放的动画.
有谁知道这个动画是否以某种方式可用?
我正在使用禁用换行的 Xcode 11.3.1。在编辑器中,代码不被包装,但控制台输出被包装。我喜欢这样。
我刚刚在另一台 Mac 上安装了 Xcode 11.5,但无法在那里重新创建这些设置。如果我在“设置”菜单中切换“将行换行至编辑器宽度”选项,则该设置会影响编辑器和控制台。有没有办法为这些视图独立设置此选项?如何重新创建我在 Xcode 11.3.1 中看到的行为?
当我将flurry代码添加到我的活动中时崩溃说flurry sdk没有初始化,我已经检查以确保库已添加到项目库中,下面是我的代码和logcat,它还有活动中的导入乱码
@Override
protected void onStart() {
super.onStart();
FlurryAgent.onStartSession(this,"YOUR_API_KEY" );
FlurryAgent.setLogEnabled(true);
FlurryAgent.setLogEvents(true);
FlurryAgent.setLogLevel(Log.VERBOSE);
}
@Override
protected void onStop() {
super.onStop();
FlurryAgent.onEndSession(this);
}
Run Code Online (Sandbox Code Playgroud)
logcat的
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.stephenh.daytrack.daytrackstephenh/com.stephenh.daytrack.daytrackstephenh.PageActivities.Exercises}: java.lang.IllegalStateException: Flurry SDK must be initialized before starting a session
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2263)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2313)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1246)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5212)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Flurry SDK must be initialized before starting a session
at com.flurry.android.FlurryAgent.onStartSession(SourceFile:328)
at …Run Code Online (Sandbox Code Playgroud) Swift 3 引入了@discardableResult函数注释,用于禁用未使用函数返回值的警告.
我正在寻找一种方法来阻止关闭这个警告.
目前,我的代码如下所示:
func f(x: Int) -> Int -> Int {
func g(_ y: Int) -> Int {
doSomething(with: x, and: y)
return x*y
}
return g
}
Run Code Online (Sandbox Code Playgroud)
在不同的地方,我打电话f一次,以获得一个闭包g,然后我反复调用:
let g = f(5)
g(3)
g(7)
g(11)
Run Code Online (Sandbox Code Playgroud)
在大多数地方,我只对嵌套调用的副作用感兴趣doSomething,而不是关闭的返回值g.使用Swift 3,我的项目中现在有许多警告用于未使用的结果.除了将呼叫转移g到_ = g(...)任何地方之外,还有办法抑制警告吗?我找不到可以放置@discardableResult注释的地方.
我的 iOS 应用程序当前提供自动续订订阅A。我想以折扣价添加第二个订阅选项B。选项B仅适用于过去通过应用内购买购买了某个升级X的用户(该应用内购买不再提供销售)。
如何防止用户在系统设置中订阅A然后切换到B ?没有购买X的用户甚至不应该知道B的存在。
据我了解Apple的文档和App Store Connect Help,用户可以在同一订阅组中的订阅之间升级/降级/交叉升级。我认为通过将订阅添加到两个不同的订阅组,不可能在A和B之间切换,但我找不到对此的明确答案。
如果用户开始任何订阅,他们会自动在系统设置中看到所有订阅组,还是仅看到具有有效订阅的组?
我正在使用AVAssetExportSession在 iOS 应用程序中导出视频。为了以正确的方向呈现视频,我使用AVAssetTrack的是preferredTransform. 对于一些源视频,这个属性似乎有一个错误的值,结果视频出现偏移或全黑。我怎样才能解决这个问题?
我正在使用多个使用旧 iOS 版本的设备在开发过程中在实际硬件上测试我的应用程序(某些使用相机或某些核心图像 API 的功能在模拟器上不起作用)。我的最后一个应用很快就会停止支持 iOS 11,所以我想将运行 iOS 11 的 iPhone SE 升级到 iOS 13。
IPSW 文件可以从ipsw.me 等网站下载,但显然没有官方方法来安装此类升级,因为 Apple 不再对这些固件进行签名。有没有办法绕过这个限制?
在我的 iOS 应用程序的 UI 中,我显示了一个复杂的CALayers层次结构。这些层之一是AVPlayerLayer显示视频并CIFilter实时应用 s(使用AVVideoComposition(asset:, applyingCIFiltersWithHandler:))。
现在我想将此图层合成导出到视频文件。有两个工具AVFoundation似乎很有帮助:
A:AVVideoCompositionCoreAnimationTool允许在(可能是动画的)CALayer层次结构中渲染视频
B : AVVideoComposition(asset:, applyingCIFiltersWithHandler:),我也在 UI 中使用,将CIFilters 应用于视频资产。
但是,这两个工具不能同时使用:如果我启动AVAssetExportSession结合了这些工具的一个,则AVFoundation抛出一个NSInvalidArgumentException:
期望视频合成仅包含
AVCoreImageFilterVideoCompositionInstruction
我尝试解决此限制,如下所示:
解决方法 1
1) 使用AVAssetReader和设置导出AVAssetWriter
2) 从资产读取器获取样本缓冲区并应用CIFilter,将结果保存在CGImage.
3) 将 设置CGImage为content图层层次结构中视频图层的 。现在图层层次结构“看起来像”最终视频的一帧。
4)CVPixelBuffer使用资产编写器获取每个帧的数据,CVPixelBufferGetBaseAddress并CGContext使用该数据创建一个。
5) 使用CALayer.render(in ctx: CGContext).
此设置有效,但速度极慢 - …