Hello,
I am working on a small app to easily create time relative reminders. Meaning I can quickly create reminder that will remind me about something after 45 minutes from now..
I want to add configurable shortcuts, so users can use this app via Siri and the Shortcuts app.
I have created the Intents.intentdefinition file and it's (so far one) shortcut correctly displays in the Shortcuts app with the parametrs.
But I am unsure how should I now handle it? Some tutorials and docs mention the delegate methods in SceneDelegate while others point to Intents Extension which is supposed to handle the shortcut?
swift
override func restoreUserActivityState(_ activity: NSUserActivity) {
}
This shortcut what I want does not need more user interaction than just providing the two input parameters.
So the simplest way to handle that would be nice.
Ideally if that work could happen in background without opening my app? Because after the reminder is added, there is nothing to do in my app. Is that possible?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello,
I am unable to use UIApplication.shared.open to open URL to my app's iCloud Drive folder that I am using to export files to.
I am using this method to get the URL:
url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
But when testing this URL with UIApplication.shared.canOpenURL I am getting back false.
-canOpenURL: failed for URL: "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~MyApp/Documents/" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"
Since my app's local document folder can be opened by prefixing the URL with shareddocuments:// I tried that for iCloud Drive URL, but didn't work either.
Is this even possible?
Hello,
with new App Store Connect API I wanted to prototype a couple of ideas, but when I wanted to get a API key, this dialog made me question the validity:
So this sounds like we are not supposed to create apps or web apps that would help other developers with App Store Connect tasks?
For example if I wanted to create a web app that lets people manage their TestFlight, that is against the rules? Because it would presumably involve them getting an API key which my web app would use to talk to ASC API?
On the other hand there are services like RevenueCat, Bitrise and similar, that presumably "access ASC on behalf of their users"?
I would really appreciate if someone can explain this to me.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Connect API
Hello,
our app uses the frameworks FamilyControls, ManagedSettings and DeviceActivity. It runs smoothly in like 99% cases but we have some support requests that mention the app being incredibly slow - like seconds to register button presses.
These reported incidents deal with calls to these APIs mentioned above. One of the frequent complaint is that code that manipulates with ManagedSettingsStore is slow and the whole app becomes unresponsive.
Unfortunately I am not able to reproduce this on my devices so cannot run it through the Instruments.
Anyone has experience with this?
PS: These reports are frequently from new and fast devices - like iPhone 13 & iPhone 14 so that should not be the case.
Topic:
App & System Services
SubTopic:
General
Tags:
Managed Settings
Family Controls
Device Activity
Screen Time
Hello,
is this just me or does it seem lately that apps spend quite long time in "In Review" state?
One of my work app has been "In Review" since April 20th, which is already 9 days.
I have recently submitted minor update for my hobby app and it has been "In Review" for over 24 hours - while in the past it took just a couple of hours or even less to be approved.
I understand that "Waiting for Review" can take time if there is long queue of apps but "In Review" this long?
Surely the reviewer is not testing my app for 24 hours straight? Did they forget? Are they waiting for someone else to take a look?
It just seems quite strange.
Hello,
I am building contact form that allows to attach screenshots and screen recordings. The PhotosPicker part is relatively straightforward but I am not sure how to properly import the selected items.
The binding is of type [PhotosPickerItem] which requires (at least for my current implementation) to first know if the item is image or video.
I have this not so pretty code to detect if the item is video:
let isVideo = item.supportedContentTypes.first(where: { $0.conforms(to: .video) }) != nil || item.supportedContentTypes.contains(.mpeg4Movie)
Which for screen recordings seems to work only because I ask about .mpeg4Movie and then I have this struct:
struct ScreenRecording: Transferable {
let url: URL
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(contentType: .mpeg4Movie) { video in
SentTransferredFile(video.url)
} importing: { received in
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).mp4")
try FileManager.default.copyItem(at: received.file, to: copy)
return Self.init(url: copy)
}
}
}
Notice here I have just the .mpeg4Movie content type, I couldn't get it to work with more generic ones like movie and I am afraid this implementation could soon break if the screen recordings change video format/codec.
And finally my logic to load the item:
if isVideo {
if let movie = try? await item.loadTransferable(type: ScreenRecording.self) {
viewModel.addVideoAttachment(movie)
}
} else {
if let data = try? await item.loadTransferable(type: Data.self) {
if let uiImage = UIImage(data: data) {
viewModel.addScreenshotAttachment(uiImage)
}
}
}
I would like to make this more "future proof" and less error prone - particularly the screen recordings part.
I don't even need the UIImage since I am saving the attachments as files, I just need to know if the attachment is screenshot or video and get its URL.
Thanks!
Please delete this, I made wrong conclussions. The issue wasn't with UISearchController as it is happening on other screens also where I dont even use the seach.
Hello,
I have the following subclass of UICompositionalCollectionViewLayout to get the stretchy header effect as shown below.
It works quite well, but I don't really have an experience with creating custom layouts so I thought I'd ask if maybe my implementation doesn't have some important flaws.
I once ran into persistent layout loop crash with this and I am not sure what exactly I changed but it stopped happening. However since I am using this layout on important screen, I would like to make sure there isn't obvious potential for the layout loop crash happening in App Store version.
I am particularly unsure about the shouldInvalidateLayout implementation. Originally I was returning true all the time, but decided to change it and only force invalidation for negative content offset which is when my header is supposed to stretch.
Here is the full code:
final class StretchyCompositionalLayout: UICollectionViewCompositionalLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var attrs = super.layoutAttributesForElements(in: rect) ?? []
guard let collectionView = collectionView else {
return attrs
}
let contentOffset = collectionView.contentOffset.y
guard contentOffset < 0 else {
return attrs
}
var newAttributes: UICollectionViewLayoutAttributes?
attrs.forEach({ attribute in
if attribute.indexPath.section == 0 && attribute.indexPath.item == 0 {
let startFrame = attribute.frame
newAttributes = attribute.copy() as? UICollectionViewLayoutAttributes
let newFrame: CGRect = .init(x: 0, y: contentOffset, width: startFrame.width, height: startFrame.height - contentOffset)
newAttributes?.frame = newFrame
}
})
if let new = newAttributes {
attrs.removeAll { attr in
return attr.indexPath.section == 0 && attr.indexPath.item == 0
}
attrs.insert(new, at: 0)
}
return attrs
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let attributes = super.layoutAttributesForItem(at: indexPath) else {
return nil
}
let contentOffset = collectionView?.contentOffset.y ?? 1
guard contentOffset < 0 else {
return attributes
}
if indexPath.section == 0 && indexPath.item == 0 {
let attributes = attributes.copy() as? UICollectionViewLayoutAttributes ?? attributes
let startFrame = attributes.frame
let newFrame: CGRect = .init(x: 0, y: contentOffset, width: startFrame.width, height: startFrame.height - contentOffset)
attributes.frame = newFrame
return attributes
} else {
return super.layoutAttributesForItem(at: indexPath)
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
let contentOffset = collectionView?.contentOffset.y ?? 1
// There is visual glitch when 0 is used in this condition
if contentOffset < 1 {
return true
} else {
return super.shouldInvalidateLayout(forBoundsChange: newBounds)
}
}
}
Any feedback welcome!
Hello,
In a new app I am working on I noticed the FamilyActivityTitleView that displays "ApplicationToken" has wrong (black) color when phone is set to light mode but app is using dark mode via override.
We display user's selected apps and the labels are rendered correctly at first, but then when user updates selection with FamilyActivityPicker, then those newly added apps are rendered with black titles.
The problem goes away when I close the screen and open it again. It also doesn't happen when phone is set to dark theme.
I am currently noticing the issue on iOS 18.4.1.
I have tried various workarounds like forcing white text in the custom label style, forcing re-render with custom .id value but nothing helped.
Is there any way how to fix this?
Hello,
I am trying to use AVAudioFile to save audio buffer to .wav file. The buffer is of type [Float].
Currently I am able to successfully create the .wav files and even play them, but they are blank - I cannot hear any sound.
private func saveAudioFile(using buffer: [Float]) {
let fileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("\(UUID().uuidString).wav")
let fileSettings = [
AVFormatIDKey: Int(kAudioFormatLinearPCM),
AVSampleRateKey: 15600,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
guard let file = try? AVAudioFile(forWriting: fileUrl, settings: fileSettings, commonFormat: .pcmFormatInt16, interleaved: true) else {
print("Cannot create AudioFile")
return
}
guard let bufferFormat = AVAudioFormat(settings: settings) else {
print("Cannot create buffer format")
return
}
guard let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: AVAudioFrameCount(buffer.count)) else {
print("Cannot create output buffer")
return
}
for i in 0..<buffer.count {
outputBuffer.int16ChannelData!.pointee[i] = Int16(buffer[i])
}
outputBuffer.frameLength = AVAudioFrameCount(buffer.count)
do {
try file.write(from: outputBuffer)
} catch {
print(error.localizedDescription)
print("Write to file failed")
}
}
Where should I be looking first for the problem? Is it format issue?
I am getting the data from the microphone with the AVAudioEngine.
Its format is created like this:
let outputFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(15600), channels: 1, interleaved: true)!
And here is the installTap implementation with the buffer callback:
input.installTap(onBus: 0, bufferSize: AVAudioFrameCount(sampleRate*2), format: inputFormat) { (incomingBuffer, time) in
DispatchQueue.global(qos: .background).async {
let pcmBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate * 2.0))
var error: NSError? = nil
let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return incomingBuffer
}
formatConverter.convert(to: pcmBuffer!, error: &error, withInputFrom: inputBlock)
if error != nil {
print(error!.localizedDescription)
}
else if let pcmBuffer = pcmBuffer, let channelData = pcmBuffer.int16ChannelData {
let channelDataPointer = channelData.pointee
self.buffer = stride(from: 0, to: self.windowLengthSamples, by: 1).map { Float(channelDataPointer[$0]) / 32768.0 }
onBufferUpdated(self.buffer)
}
}
}
The onBufferUpdated is the block that provides [Float] for the saveAudioFile method above.
I have tried some experiements with different output formats, but that ended up with unplayable audio files.
Hello,
I am not quite sure, how the shielding of entire categories of apps is supposed to work. The FamilyActivitySelection contains tokens for apps, websites and categories.
But the shield property of ManagedSettingsStore has only attributes applications and webDomains where I can configure the tokens from the family activity selection.
shield.applications = selection.applicationTokens
shield.webDomains = selection.webDomainTokens
I would expect there to be the property categories that expects Set<ActivityCategoryToken> and based on this shields apps in that category.
Hello,
I have added new non-consumable in-app to my existing app. Initially I messed up and sent it to review by itself (since in-apps and regular submissions seem to be handled by different teams).
It got rejected and the review notes said that the review couldn't be done, because they couldn't test it in the app. Makes sense.
So I submitted new build to app review and then submitted the in-app again. Same rejection reason. The build got approved, the in-app got rejected.
After my newest build was approved. I added note in the review notes that the in-app is available in approved build. But after almost 48 hours "In Review" I got another rejection:
We have returned your in-app purchase products to you as the required binary was not submitted. When you are ready to submit the binary, please resubmit the in-app purchase products with the binary.
I have no idea how to submit "in-app purchase products with the binary".
When preparing new version for review, there isn't any option to add the in-app as a single submission.
Please help :/
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
In-App Purchase
App Review
App Store Connect
Hello,
I watched the EventKit session from WWDC 23 (https://developer.apple.com/videos/play/wwdc2023/10052/) that details the new permission changes.
The ability to have just write access to the calendar is great, however my app also offers the user to view the already created event and to possibly edit it further via EKEventEditViewController.
The event in question is release date of a videogame, so there might be needs for edit and based on saved event identifier, I am able to show different icon to let the user know that event exists.
When the user taps on the icon, I use the eventStore.event(withIdentifier:. So I guess I will need the new "full access" with new prompt API to keep my functionality?
Is there any possibility that this will change and apps would be able to query event they created?
Thanks
Hello,
I have collection view with context menu using contextMenuConfigurationForItemAt and I wanted to customize the preview, when user long presses and context menu is shown. Something maybe like in the Music app when you long press on an album it shows the album in bigger size...
I found some snippets online for highlightPreviewForItemAt and dismissalPreviewForItemAt but it just doesn't work. As soon as a implemented these delegate methods, nothing happens when I long press, not even the standard preview with context menu.
These two methods aren't being called at all. Do I need to do something else? Do I need the previewProvider when creating the context menu? It is my understanding that that is needed only when the item should also open further detail on tap - which is something I don't need and want.
Below is my relevant implementation:
private func shareMenuConfiguration(for itemAt: URL, indexPath: IndexPath) -> UIContextMenuConfiguration {
let share = UIAction(title: "Share".localized(), image: UIImage(systemName: "square.and.arrow.up")) { [unowned self] _ in
let shareVC = UIActivityViewController(activityItems: [itemAt], applicationActivities: nil)
if let cell = collectionView.cellForItem(at: indexPath) {
shareVC.popoverPresentationController?.sourceView = cell.contentView
shareVC.popoverPresentationController?.sourceRect = cell.contentView.bounds
}
self.present(shareVC, animated: true)
}
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in
UIMenu(title: "", children: [share])
}
}
func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? {
guard let item = datasource.itemIdentifier(for: indexPath), let cell = collectionView.cellForItem(at: indexPath) as? GalleryImageCell else {
return nil
}
let parameters = UIPreviewParameters()
let visibleRect = cell.contentView.bounds.insetBy(dx: 1/3, dy: 1/3)
let visiblePath = UIBezierPath(roundedRect: visibleRect, cornerRadius: 4)
parameters.visiblePath = visiblePath
return UITargetedPreview(
view: cell.contentView,
parameters: parameters,
target: .init(container: collectionView, center: collectionView.convert(cell.contentView.center, from: cell.contentView))
)
}
func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, dismissalPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? {
guard let item = datasource.itemIdentifier(for: indexPath), let cell = collectionView.cellForItem(at: indexPath) as? GalleryImageCell else {
return nil
}
let parameters = UIPreviewParameters()
let visibleRect = cell.contentView.bounds.insetBy(dx: 1/3, dy: 1/3)
let visiblePath = UIBezierPath(roundedRect: visibleRect, cornerRadius: 4)
parameters.visiblePath = visiblePath
return UITargetedPreview(
view: cell.contentView,
parameters: parameters,
target: .init(container: collectionView, center: collectionView.convert(cell.contentView.center, from: cell.contentView))
)
}
Thanks!
Hello,
my indie app has somewhat significant traction in China (both downloads and subscription purchases), but I also have high refund rate there, like multiples of other countries... Perhaps even as 80 % or more of my refunds are in China although the share of downloads and purchases is way lower.
I am curious if someone else also has this issue?
My app has the "Blinkist trial" onboarding which shows the timeline and has notification on day 5 (for weekly trial), that paid subscription starts soon. However even after stopping showing this onboarding in China for new users I still have most of my refunds from China.
I am trying to understand whether this may be broader "phenomenon" or maybe something super specific to my app. As far as I know Apple doesn't share the refund request reasons with developers and I did not get any support emails that would hint at why people are asking for refund.
Thanks!
Since Apple can terminate your developer account if you have "high" refund rate, I would like to get to the bottom of this.
Topic:
App Store Distribution & Marketing
SubTopic:
General
Tags:
App Store
Subscriptions
In-App Purchase