URLSessionConfiguration's httpMaximumConnectionsPerHost not working in iOS 10? - ios10

Setting this parameter doesn't affect the number of
simultaneous connections to a host. I've found the same issue on apple developer forum. It looks like a bug in iOS 10, iOS 9 works properly.
let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: <identifier>)
sessionConfiguration.httpMaximumConnectionsPerHost = 4
sessionConfiguration.allowsCellularAccess = AppDelegate.shared.userDefaults.allowsCellularAccess
sessionConfiguration.timeoutIntervalForRequest = 7*60
sessionConfiguration.timeoutIntervalForResource = 24*60*60
let session = URLSession(configuration: sessionConfiguration, delegate:self, delegateQueue: operationQueue)
Does anybody have the same problem?
If there an alternative stable way to manage a queue of many uploads/downloads in background?

Related

How do I detect apple silicon (M1) vs. intel in swift 5.0?

I make a call to ffmpeg and ffprobe. This issue I have is that Brew installs them in different locations based on architecture. On an Intel machine the binaries are installed in /usr/local/bin, but on the M1 the binaries are installed in /opt/homebrew/bin.
I'd like to do something like:
let ffprobe = Process()
if intel {
ffprobe.executableURL = URL(fileURLWithPath: "/usr/local/bin/ffprobe")
} else {
ffprobe.executableURL = URL(fileURLWithPath: "/opt/homebrew/bin/ffprobe")
}
I have searched for this and cannot find anything.
I got this answer from the Apple Developer Forums:
var systeminfo = utsname()
uname(&systeminfo)
let machine = withUnsafeBytes(of: &systeminfo.machine) {bufPtr->String in
let data = Data(bufPtr)
if let lastIndex = data.lastIndex(where: {$0 != 0}) {
return String(data: data[0...lastIndex], encoding: .isoLatin1)!
} else {
return String(data: data, encoding: .isoLatin1)!
}
}
print(machine)
Here's the link for reference Apple Developer Forum.
Not sure if things have changed in a year, but I found Apple forums suggested a much simpler approach using ProcessInfo -- ie;
let proc_info = ProcessInfo()
proc_info.isiOSAppOnMac
There are also Catalyst bools. Worked great to detect my iOS app running on MacOS Silicon (non Catalyst).
see
https://developer.apple.com/documentation/foundation/processinfo/3608556-isiosapponmac

Swift Realm issue in iOS 14+

------LE: We ended up removing the encryption of the database because with realm team suggestions it got worse - all we could do was to remove the database and loose all stored info. Now we encrypt in keychain only the fields we need.------
I have an app released in store and after updating their iOS version to 14+, users started to complain about info not being populated from database. Not all users with iOS 14+ have this issue, it appears randomly on some devices.
The issue goes away for awhile if they reinstall the app or after they update it to another version, but after using it for a few minutes it happens again.
My database uses encryption as documented here.
The store version of my app uses Realm 5.4.8, but I tested their last version (10.0.0) and the issue is still present.
I checked this issue but it's not the case for me, I don't have a shared app group container or a share extension.
Here's how the initialisation of realm looks like:
override init() {
super.init()
do {
guard let config = getMigrationAndEncryptionConfiguration() else {
realmConfigured = try Realm()
return
}
realmConfigured = try Realm(configuration: config)
} catch let error as NSError {
// this is where I got the error:
//"Encrypted interprocess sharing is currently unsupported.DB has been opened by pid: 4848. Current pid is 5806."
}
}
func getMigrationAndEncryptionConfiguration() -> Realm.Configuration? {
let currentSchemaVersion: UInt64 = 19
if Keychain.getData(for: .realmEncryptionKey) == nil {
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}
Keychain.save(data: key, for: .realmEncryptionKey)
}
guard let key = Keychain.getData(for: .realmEncryptionKey) else {
return nil
}
let fileUrl = Realm.Configuration().fileURL!.deletingLastPathComponent()
.appendingPathComponent("Explorer.realm")
var config = Realm.Configuration(fileURL: fileUrl,
encryptionKey: key,
schemaVersion: currentSchemaVersion, migrationBlock: { (migration, oldVersion) in
if oldVersion != currentSchemaVersion {
print("we need migration!")
}
})
return config
}
I had another question opened for the same issue on SO, but it was closed because I didn't have enough details. After another release of my app with more logs, I could find the error that appears at initialisation of realm:
"Encrypted interprocess sharing is currently unsupported.DB has been opened by pid: 4848. Current pid is 5806. "
This appears after the app goes to background, it gets terminated (crash or closed by the system/user) and when the users opens it again, realm fails to init.
I read all about encrypted realm not being supported in app groups or in a share extension, but I didn't implement any of that in my app, is there any other reason why this error happens?
I also removed Firebase Performance from my app because I read that this module could generate issues on realm database, but it didn't help.
I opened an issue on realm github page, but I got no answer yet.
Does anyone have any idea how to fix this or why is this happening?
Thank you.

INInteraction donation duplicated in Spotlight IOS 12 Beta 5

I am donating an INInteraction and everything is working ok, however I can't understand why the donation is showing up duplicated in Spotlight.
Is there a property that needs to be set in order to prevent this??
let viewUsageIntent = UsageIntent()
var susbcribers = [INObject]()
for sub in account.subscribers {
let inObject = INObject(identifier: sub.phoneNumber, display: sub.id)
susbcribers.append(inObject)
}
viewUsageIntent.suggestedInvocationPhrase = phrase
viewUsageIntent.ban = account.ban
viewUsageIntent.subs = susbcribers
let interaction = INInteraction(intent: viewUsageIntent, response: nil)
interaction.donate(completion: {
error in
if let err = error {
MyAppServices.Logger.error(tag: "UsageIntentDonation", message: "Donation for ban \(account.ban) could not be completed: \(err.localizedDescription)")
}
})
Anybody dealing with this issue?. Thanks.
This is not a bug. This is the default behaviour when you set your simulator or iPhone to display recent shortcuts in the developer section. I was just confused by it.
Is not a duplication, it is just displaying the newest one over older ones for development sake

The "prefs" URL Scheme is not working in iOS 10 (Beta 1 & 2)

I can't get the "prefs" URL Scheme to work in iOS 10 (Beta 1).
It's set up correctly since the same App works fine on iOS 9.
Is this a bug or did it get renamed / removed?
Code:
let settingsUrl = NSURL(string: "prefs:root=SOMETHING")
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
Update: (Beta 2)
Still not working in Beta 2.
It seams to be an bug. For example if you want do invite someone using GameCenter in iOS 10 and you're not logged into iMessage, you'll get a popup asking you to log in. But the "Settings" button does absolutely nothing.
Just replace prefs to App-Prefs for iOS 10
Below code works for iOS 8,9,10
Swift 3.0 and Xcode >= 8.1
if #available(iOS 10.0, *)
{
UIApplication.shared.openURL(URL(string: "App-Prefs:root=SOMETHING")!)
}
else
{
UIApplication.shared.openURL(URL(string: "prefs:root=SOMETHING")!)
}
Swift 2.2
if #available(iOS 10.0, *)
{
UIApplication.sharedApplication().openURL(NSURL(string:"App-Prefs:root=SOMETHING")!)
}
else
{
UIApplication.sharedApplication().openURL(NSURL(string:"prefs:root=SOMETHING")!)
}
Works for me.
Happy Coding 😊
You can use UIApplicationOpenSettingsURLString to open your own app's settings (this has been available since iOS 8) but any other prefs: URL is now considered a private API and use will result in app rejection.
You can use Prefs:root=SOMETHING
iOS 10 updated URL Scheme for Settings, you need to upcase the "p".
Ref: https://github.com/cyanzhong/app-tutorials/blob/master/schemes.md
NOTICE: It only works on Widgets, not works in Apps. (iOS 10.0.2)
#Saumil Shah's solution works in App, is more useful.
For the record, for Location services App-Prefs:root=Privacy&path=LOCATION worked for me. When I tested on a device and not a simulator.
I won't list the things I tried that did not work, it's a long list.
Usage example that assumes either location services are disabled or permission is denied or not determined:
if !CLLocationManager.locationServicesEnabled() {
if let url = URL(string: "App-Prefs:root=Privacy&path=LOCATION") {
// If general location settings are disabled then open general location settings
UIApplication.shared.openURL(url)
}
} else {
if let url = URL(string: UIApplicationOpenSettingsURLString) {
// If general location settings are enabled then open location settings for the app
UIApplication.shared.openURL(url)
}
}
This is not available on iOS 11, we can just open Settings like:
if let url = URL(string:UIApplicationOpenSettingsURLString) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
If anyone is interested in a "gray area" API, you can use:
//url = "prefs:root=SOMETHING"
[[LSApplicationWorkspace defaultWorkspace] openSensitiveURL:url withOptions:nil];
This will give you what you want. Hide it well, and it works in iOS 10.

iOS: Keep application running in background

How do I keep my application running in the background?
Would I have to jailbreak my iPhone to do this? I just need this app to check something from the internet every set interval and notify when needed, for my own use.
Yes, no need to jailbreak. Check out the "Implementing long-running background tasks" section of this doc from Apple.
From Apple's doc:
Declaring Your App’s Supported Background Tasks
Support for some types of background execution must be declared in advance by the app that uses them. An app declares support for a service using its Info.plist file. Add the UIBackgroundModes key to your Info.plist file and set its value to an array containing one or more of the following strings: (see Apple's doc from link mentioned above.)
I guess this is what you required
When an iOS application goes to the background, are lengthy tasks paused?
iOS Application Background Downloading
This might help you ...
Enjoy Coding :)
Use local notifications to do that. But this will not check every time. You will have to set a time where you will check your specific event, you may shorten this by decreasing your time slot. Read more about local notification to know how to achieve this at:
http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction/Introduction.html
I found a way, to keep app running in background by playing silence
Make sure, that you selected audio playback in background modes
Also, don't use this method for long time, since it consumes CPU resources and battery juice, but I think it's a suitable way to keep app alive for a few minutes.
Just create an instance of SilencePlayer, call play() and then stop(), when you done
import CoreAudio
public class SilencePlayer {
private var audioQueue: AudioQueueRef? = nil
public private(set) var isStarted = false
public func play() {
if isStarted { return }
print("Playing silence")
let avs = AVAudioSession.sharedInstance()
try! avs.setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
try! avs.setActive(true)
isStarted = true
var streamFormat = AudioStreamBasicDescription(
mSampleRate: 16000,
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked,
mBytesPerPacket: 2,
mFramesPerPacket: 1,
mBytesPerFrame: 2,
mChannelsPerFrame: 1,
mBitsPerChannel: 16,
mReserved: 0
)
let status = AudioQueueNewOutput(
&streamFormat,
SilenceQueueOutputCallback,
nil, nil, nil, 0,
&audioQueue
)
print("OSStatus for silence \(status)")
var buffers = Array<AudioQueueBufferRef?>.init(repeating: nil, count: 3)
for i in 0..<3 {
buffers[i]?.pointee.mAudioDataByteSize = 320
AudioQueueAllocateBuffer(audioQueue!, 320, &(buffers[i]))
SilenceQueueOutputCallback(nil, audioQueue!, buffers[i]!)
}
let startStatus = AudioQueueStart(audioQueue!, nil)
print("Start status for silence \(startStatus)")
}
public func stop() {
guard isStarted else { return }
print("Called stop silence")
if let aq = audioQueue {
AudioQueueStop(aq, true)
audioQueue = nil
}
try! AVAudioSession.sharedInstance().setActive(false)
isStarted = false
}
}
fileprivate func SilenceQueueOutputCallback(_ userData: UnsafeMutableRawPointer?, _ audioQueueRef: AudioQueueRef, _ bufferRef: AudioQueueBufferRef) -> Void {
let pointer = bufferRef.pointee.mAudioData
let length = bufferRef.pointee.mAudioDataByteSize
memset(pointer, 0, Int(length))
if AudioQueueEnqueueBuffer(audioQueueRef, bufferRef, 0, nil) != 0 {
AudioQueueFreeBuffer(audioQueueRef, bufferRef)
}
}
Tested on iOS 10 and Swift 4
I know this is not the answer to your question, but I think it is a solution.
This assumes that your trying to check something or get data from the internet on a regular basis?
Create a service that checks the internet every set interval for whatever it is you want to know, and create a push notification to alert you of it, if the server is down, or whatever it is your trying to monitor has changed state. Just an idea.
Yes you can do something like this. For that you need to set entry in info.plist to tell os that my app will run in background. I have done this while I wanted to pass user's location after particular time stamp to server. For that I have set "Required background modes" set to "App registers for location updates".
You can write a handler of type UIBackgroundTaskIdentifier.
You can already do this in the applicationDidEnterBackground Method