Delve into the world of built-in app and system services available to developers. Discuss leveraging these services to enhance your app's functionality and user experience.

Post

Replies

Boosts

Views

Activity

CNContact identifiers
Hi,I saw the WWDC talk on the new Contacts framework, and I had a couple of questions about the identifier property:When users upgrade from iOS8 to iOS9, will their old AddressBook recordIDs still work if we are using the AddressBook framework in the app (i.e. haven't upgraded to using Contacts framework)?Are the new CNContact identifiers consistent across iOS and Mac devices? i.e. if we have the identifier on one device, we can lookup the contact on another device with the same identifier?Can these identifiers change in the lifetime of that contact? This was a problem with AddressBook recordIDs ... if you removed the account and then synced it back again, all the contacts would get a different identifier. Same with restoring the device. Will the CNGroup and CNContainer identifiers also be consistent across devices?Hope someone in the Apple Contacts frameworks team can answer. Thanks.
6
0
4.1k
Jun ’15
Testing Background Session Code
Debugging code that runs in the background on iOS is tricky because the act of running your app in the debugger affects how it behaves. This is especially true with URLSession background session code. I regularly see folks confused about how to test and debug such code, and this is my attempt to help with that. If you have questions or comments about this, start a new thread and tag it with Foundation and CFNetwork so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Testing Background Session Code Testing an app that uses a URLSession background session is tricky because of the differences between the test environment and the production environment. This post explains those differences and describes how you can effectively test your background session code. Confusing Behaviours When testing background session code you might encounter four common sources of confusion: When you run your app from Xcode, Xcode installs the app in a new container, meaning that the path to your app changes. Background sessions should deal with this path change correctly, but there have been times in the past where that’s not been the case. Note If you encounter a problem with a background session mishandling a container path change, please report that as a bug. Xcode’s debugger prevents the system from suspending your app. If you run your app from Xcode, or you attach to the process after launch, and then move your app into the background, your app will continue executing in situations where the system would otherwise have suspended it. Simulator may not accurately simulate app suspend and resume. Note This is supposed to work but there have been problems with it in the past (r. 16532261). If you see this problem on modern versions of Xcode, please report it as a bug. When you remove an app from the multitasking UI (aka force quit it), the system interprets that as a strong indication that the app should do no more work in the background, and that includes URLSession background tasks. Testing Recommendations Given the above, my recommendation is that you test your app as follows: Test on a real device, not in Simulator. Run your app from the Home screen rather than running it from Xcode. Don’t use force quit to test the ‘relaunch in the background case’. Doing this avoids all of the issues described above, resulting in a run-time environment that’s much closer to what your users will see. Debugging Background Session Code If you encounter problems that you need to debug, you have two options. The first is to use logging. Your app needs good logging support anyway; without that, it’ll be impossible to debug problems that only show up in the field. Once you’ve taken the time to create this logging, use it to debug problems during development. I recommend that you log to the system log. For more on that, see Your Friend the System Log. If you have a specific problem that you must investigate with the debugger, run your app from the Home screen and then attach to the running process by choosing Debug > Attach to Process in Xcode. Alternatively, use “Wait for executable to be launched” in the Info tab of the Xcode’s scheme editor. IMPORTANT As mentioned above, the debugger prevents your app from suspending. If that’s a problem, you can always detach and then reattach later on. About Force Quit The behaviour of background sessions after a force quit has changed over time: In iOS 7 the background session and all its tasks just ‘disappear’. In iOS 8 and later the background session persists but all of the tasks are cancelled. Note You can use the NSURLErrorBackgroundTaskCancelledReasonKey property of the error to find out why a task was cancelled. Test that your app handles the force quit behaviour of the platforms you support. However, don’t use force quit to test how your app deals with the ‘relaunch in the background case’. The best way to test the ‘relaunch in the background case’ is to have your app terminate itself by calling exit. The system does not interpret this as a force quit, and thus will relaunch your app in the background when your background session needs attention. There two ways to go about this: Simply add a Quit button that calls exit. Add some setting that causes your app to call exit shortly after moving to the background. You’ll need to use a UIApplication background task to prevent your app from being suspended before it gets a chance to quit. IMPORTANT The suggestions above are for testing only. Do not include a Quit button in your final app! This is specifically proscribed by QA1561. Starting From Scratch While bringing up a feature that uses background sessions, it’s often useful to start from a clean slate on launch; this prevents tasks from a previous debugging session confusing the current debugging session. You have a number of options here: If you’re testing in Simulator, Device > Erase All Content and Settings is an excellent way to wipe the slate completely clean. On both Simulator and a real device, you can delete your app. This removes the app, everything in the app’s container, and everything created by the app, including any background sessions. You can also take advantage of the invalidateAndCancel() method, which will cancel any running tasks and then invalidate the session. There are a few ways to use invalidateAndCancel(): During active development it might make sense for your app to call this on launch, guaranteeing that you start with a clean slate every time. Alternatively, you could keep track of your app’s install path and call it on launch if the install path has changed. You might add a ‘hidden’ UI that invalidates the session. This is helpful when you encounter a problem and want to know whether it’s caused by some problem with the background session’s persistent state. Revision History 2023-07-12 Fixed the formatting. Made significant editorial changes. 2018-05-03 Added a discussion of force quit. Also made major editorial changes, restructuring the document to be easy to read. 2015-08-18 First posted. (r. 22323379)
0
0
28k
Aug ’15
Universal Deep Linking with redirects
I was wondering if it was possible to get Universal Deep Linking working with redirects that open up Safari first. I've successfully gotten deep linking to work when doing a normal link to the site, but when it is behind a link that is created by a marketing company, that link opens up Safari first, then opens the mobile web browser and not the app itself.Is it possible to go from a redirect in Safari to the app?
14
0
29k
Nov ’15
Background Transfer Service and Client Certificates
I can't get NSURLSession background tasks to use client certificates.I have a simple app that creates an NSURLSessionDownloadTask from an NSURLSession that uses NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(...).I have a custom delegate that implements:"func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)". For NSURLAuthenticationMethodServerTrust, I accept any server. For NSURLAuthenticationMethodClientCertificate, I have a hardcoded PKCS12 bundle with a single certificate and key from which I successfully create an NSURLCredential (using SecPKCS12Import), then pass that credential to the completion handler with .UseCredential.At runtime, I get the didReceiveChallenge callback for ClientCertificate, then another for NSURLAuthenticationMethodServerTrust, then URLSessionDidFinishEventsForBackgroundURLSession right away without completing the TLS handshake.If I change the NSURLSessionConfiguration to use NSURLSessionConfiguration.defaultSessionConfiguration(), the client cert is presented correctly and the download proceeds.Both tests are done with the app in the foreground.
16
0
7.5k
Dec ’15
Text Kit not word-wrapping first/last lines in UITextView
When using an exclusion path with an UITextView, the text view is not properly wrapping the words for the first and last line in the example shown (lower text). It does render properly using word-wrap with Core Text (top example).(Hmm - the image link I provided in the HTML here works with the preview, but not the post. The example image is at: http i.stack.imgur.com/Z91ge.pngHere is the code for the UITextView (both this and the Core Text example use the same size bezier path, and both use the appropriate corresponding font and paragraph settings; wrap by word and centered):NSString *testText = @"Text Kit exclusion paths ..."; / UIBezierPath *viewPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 280, 280)]; UIBezierPath *shapePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 265, 265)]; viewPath.usesEvenOddFillRule = true; shapePath.usesEvenOddFillRule = true; [shapePath appendPath:viewPath]; NSMutableAttributedString *title = [[NSMutableAttributedString alloc]initWithString:testText]; UIFont *font = [UIFont fontWithName:@"BradleyHandITCTT-Bold" size:14]; [title addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, title.length)]; [title addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, title.length)]; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setAlignment:NSTextAlignmentCenter]; [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping]; [title addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, title.length)]; UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 370.0, 280, 280)]; textView.textContainerInset = UIEdgeInsetsMake(0,0,0,0); textView.textContainer.exclusionPaths = @[shapePath]; [textView.textContainer setLineBreakMode:NSLineBreakByWordWrapping]; textView.contentInset = UIEdgeInsetsMake(0,0,0,0); textView.attributedText = title; textView.backgroundColor = [UIColor clearColor];It is worth noting that the Text Kit is respecting the word wrapping rules except for the first and (possibly last) line where the text does not actually fit. I need this to work with a UITextView because text entry is required, or I would be done with my Core Text example.I have tried everything I can think of to reliably work-around the issue. I don't fully understand how UITextView is working with Core Text (or I would expect to get the same results which I don’t), so any suggestions would be greatly appreciated.
3
0
1.9k
Feb ’16
Messages App Extension won't hit breakpoints
Greetings,I've been struggling some time with this issue. I have this App which I'm developing a Messages App Extension. App works fine but I can't debug it. I can add NSLog sentences to check for workflow but when I try to add a breakpoint it won't stop there, even if the NSLog nearby is triggered (i.e. It doesn't stop on a breakpoint in viewDidLoad from my MSMessagesAppViewController subclass). I've tried to create a project from scratch and it works fine. It's just with this project (created from XCode 7) that I'm not able to hit any breakpoint. I've tried deleting the extension and recreating a new one with no success. When I try to debug the App target It works just fine, it's this extension target I cannot reach any breakpointAny ideas?
6
0
3k
Aug ’16
iOS Universal Links with Wildcards not working
https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html as per this link Universal links with wildcards working fine, followed steps in this link to apply deep linking to my application but it doesn't work. When clicking on a link in received email, it should navigate to my application but it is opening in safari browser. I have mentioned "*.test.com" as applinks in Associated domains under Capabilities in Xcode and in our web side we have deployed "apple-app-site-association" file in root folder. My server host name like "abc.test.com" and I have mentioned applinks like "*.test.com" this universal links with wildcard feature is not working.I have Enabled ‘Associated Domains’ in my app identifier on developers.apple.com, If I have mentioned applinks like "abc.test.com" then it is working fine. My application Developement target is 9.3.
3
0
3.5k
Sep ’16
Maximum number of devices
Hi,I have multiple Hue bridges set up in Homekit. Nearly 200 devices in all.When I get over about 170 devices Homekit stops syncing between my iPad and iPhone.I can see no documentation around what the maximum number of devices allowed in Homekit is, is anyone able to point me in the right direction?Many thanks!
5
0
7.3k
Oct ’16
App rejection - UIRequiredDeviceCapabilities
Anyone see something like this on app submission?We are developing an iMessage app with SpriteKit. The message we received from App Review was:"We were unable to install the app on iPhone. The UIRequiredDeviceCapabilities key in the Info.plist is set in such a way that the app will not install on an iPhone. Specifically, the app fails to appear in Messages."We are not having any issues on test devices on our end.Our app plist contains only one UIRequiredDeviceCapabilities key: "armv7"The extension does not include any UIRequiredDeviceCapabilities.
11
1
8k
Dec ’16
Networking in a Short-Lived Extension
There are many cases where you want to do long-running network operations in a short-lived app extension. For example, you might be creating a share extension that needs to upload a large image to an Internet server. Doing this correctly is quite tricky. The rest of this post describes some of these pitfalls and explains how to get around them. Just by way of context: Most of the following was written during the iOS 8.1 timeframe, although AFAIK there’s been no specific changes in this space since then. The specific focus here is a share extension, although the behaviour is likely to be similar for other short-lived extensions. I wasn’t using SLComposeServiceViewController, although I have no reason to believe that makes a difference. I was testing on a device, not the simulator. In my experience the simulator is much less likely to terminate a share extension, which affects how things behave as I’ll explain later. My app and its share extension have an app group in common. I started by verifying that this app group was working as expected (using UserDefaults). The URLSession must be in that app group; set this via the sharedContainerIdentifier property of the URLSessionConfiguration object you use to create the session. The app and the share extension must use the same URLSession background session identifier (the value you pass in to background(withIdentifier:) when creating the configuration that you use to create the session). When an URLSession background session is shared like this, it’s critical to understand that the session only allows one process to ‘connect’ to it at a time. If a process is connected to the session and another tries to connect, the second process has its session immediately invalidated with NSURLErrorBackgroundSessionInUseByAnotherProcess. The connected session is the one that receives the session’s delegate callbacks. IMPORTANT If callbacks are generated when no process is connected, the background session resumes (or relaunches) the app rather than the extension. If a process is connected to a session and is then suspended or terminates, the session disconnects internally. If the process was terminated, the reconnection happens when your code creates its URLSession object on next launch. If the process was suspended, the reconnect happens when the app is resumed with the application(_:handleEventsForBackgroundURLSession:completionHandler:) delegate callback (and remember that this is always the app, not the extension, per the previous paragraph). The only way to programmatically disconnect from a session is to invalidate it. The expected behaviour here is that the extension will start an URLSession task and then immediately quit (by calling completeRequest(returningItems:completionHandler:)). The system will then resume (or relaunch) the container app to handle any delegate callbacks. When the system resumes or relaunches the container app to handle background session events, it calls application(_:handleEventsForBackgroundURLSession:completionHandler:). The container app is expected to: Save away the completion handler. Reconnect to the session, if necessary. This involves creating the URLSession object if it doesn’t currently exist. Handle delegate events from that session. Invalidate the session when those events are all done. The app knows this because the session calls the urlSessionDidFinishEvents(forBackgroundURLSession:) delegate callback. Call the completion handler that was saved in step 1. This leaves the app disconnected from the session, so future invocations of the extension don’t have to worry about the NSURLErrorBackgroundSessionInUseByAnotherProcess problem I mentioned earlier. This design works best if each extension hosted by the app has its own shared session. If the app hosts multiple extensions, and they all used the same shared session, they could end up stomping on each other. In my tests I’ve noticed that some annoying behaviour falls out of this design: If you start a task from an extension, it’s non-deterministic as to whether the app or extension gets the ‘did complete’ callback. If the task runs super quickly, the extension typically gets the callback. If the task takes longer, the system has time to terminate the extension and the app is resumed to handle it. There’s really no way around this. The workaround is to put the code that handles request completion in both your app and your extension (possibly sharing the code via a framework). It would be nice if the extension could disconnect from the session immediately upon starting its request. Alas, that’s not currently possible (r. 18748008). The only way to programmatically disconnect from the session is to invalidate it, and that either cancels all the running tasks (invalidateAndCancel()) or waits for them to complete (finishTasksAndInvalidate()), neither of which is appropriate. One interesting edge case occurs when the app is in the foreground while the share extension comes up. For example, the app might have a share button, from which the user can invoke the share extension. If the share extension starts an URLSession task in that case, it can result in the app’s application(_:handleEventsForBackgroundURLSession:completionHandler:) callback being called while the app is in the foreground. The app doesn’t need to behave differently in this case, but it’s a little unusual. Xcode’s debugger prevents the system from suspending the process being debugged. So, if you run your extension from Xcode, or you attach to its process some time after launch, the process will continue to execute in situations where the system would otherwise have suspended it. This makes it tricky to investigate problems with the ‘extension was suspended before the network request finished’ case. The best way to investigate any issues you encounter is via logging. Note For more information about debugging problems with background networking, see Testing Background Session Code. Keep in mind that not all networking done by your extension has to use a background session. You should use a background session for long-running requests, but if you have short-running requests then it’s best to run them in a standard session. For example, imagine you have a share extension that needs to make two requests: The first request simply gets an upload authorisation token from the server. This request is expected to finish very quickly. The second request actually uploads the file (including the upload authorisation token from the previous request), and is expected to take a long time. It makes sense to only use a background session for the second request. The first request, being short-running, can be done in a standard session, and that will definitely simplify your code. When doing this you have to prevent your extension from suspending while the short-lived request is in flight. You can use ProcessInfo.performExpiringActivity(withReason:using:) for this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History 2023-08-30 Fixed the formatting. Adopted Swift terminology throughout. Made other minor editorial changes. 2017-04-26 Moved to the new DevForums. Made many editorial changes. Added the section on Xcode’s debugger. Added a section on short-running network requests. 2014-12-05 First posted on the old DevForums.
0
0
3.3k
Apr ’17
NSURLSessionDownloadDelegate Methods on iPhone iOS 10.3.1
I've recently been integrating Background Transfer Service into an application so that the user is able to download files in the background.I did most of my building/testing on an old iPhone 6 device that was running iOS 9.3.5 (I try to keep my previous device back a version of iOS). Background Transfer Service works great on this device. I am able to put 100+ download tasks into the queue and they all finish and report progress as expected after sending the application into the background and then re-opening the application.This also appears to work great on a new 5th generation iPad running iOS 10.3.Where things get weird is on my iPhone 7 Plus which is running iOS 10.3.1. The downloads kick off fine, progress is reported as expected in the didWriteData method, however if I background the application and wait ~10 seconds and re-open the application the progress never catches back up to what progress would have been done in the background and the progress never increments at all (it stays where it was when it was backgrounded). The didFinishDownloading method does end up getting called however it appears to wait until the very end of all the download tasks completions for that method to fire for all of the remaining tasks. So the didWriteData, didFinishDownloading, didCompleteWithError, etc. all happen right at the end in one burst. I have however had a few instances where my iPhone 7 Plus device running iOS 10.3.1 did show progress after bringing the app back into the foreground however those instances I could could on one hand. More often than not (9 times out of 10) the progress is never reported on this device after re-opening the application.I am at a bit of a loss and am wondering if there is a known bug we should be looking out for, and if so when we could expect a fix? Or if there are currently any known work arounds. From my testing/debugging I cannot get progress to work in my application on the iPhone 7 Plus running 10.3.1. I also had a co-worker test this as well and they also experienced this.In fact they went a step further and opened the Netflix application on their 10.3.1 iPhone 7 Plus device and began downloading a movie. Progress incremented as expected, they then backgrounded the application and waited ~20 seconds and re-opened the application, only to find the progress get stuck where it was when the app was initially backgrounded and it never moves. After a given amount of time of the progress not moving the download does eventually finish (the progress indicator disappears) and the Netflix movie is downloaded and able to be played. This is the same behavior I see above in my application.Any help with this would be greatly appreciated.Thanks in advance!Adam
29
0
11k
May ’17
Can Apple Watch side SiriKit extension and iPhone app communicate via Watch Connectivity Framework?
Using SiriKit's Car Command Intents (INGetCarLockStatusIntent, INSetCarLockStatusIntent),we are developing SiriKit extension which responds to open and close car door commands from Apple Watch.However, we do not know how to implement the communication between car and Apple Watch. In particular,We do not know whether inter-device communication is possible via "Watch Connectivity Framework".We knows that connection via BLE is not supported from Apple's documents.NG : [Watch SiriKit Extension] --- (BLE) --- [Car]We knows that connection via WIFI is supported from those documents.OK : [Watch SiriKit Extension] --- (HTTP Connection) --- [Car]However, we do not know whether the extension is possible to connect iPhone companion application via "Watch Connectivity Framework" .??? : [Watch SiriKit Extension] --- (Watch Connectivity Framework) --- [iPhone] --- (BLE) --- [Car]We would like to know if there is only a way to connect via WIFI, or even via "Watch Connectivity Framework".
2
0
1.1k
May ’17
EAAccessoryManager notifyObserversThatAccessoryDisconnectedWithUserInfo crashes iOS 11.1.2
App is crashing while a bluetooth device is disconnected. I've seen few related threads and they say "A notification delegate was not reset to nil. A fix has been applied at version 10.3.79."Following is crash log:Triggered by Thread: 0 Thread 0 name: Thread 0 Crashed: 0 libobjc.A.dylib 0x000000018544c430 objc_msgSend + 16 1 ExternalAccessory 0x000000019c07c4dc -[EAAccessoryManager _notifyObserversThatAccessoryDisconnectedWithUserInfo:] + 104 (EAAccessoryManager.m:909) 2 ExternalAccessory 0x000000019c07eddc -[EAAccessoryManager _externalAccessoryDisconnected:] + 928 (EAAccessoryManager.m:1537) 3 CoreAccessories 0x00000001a7892ccc __54-[ACCExternalAccessoryProvider ExternalAccessoryLeft:]_block_invoke + 316 (ACCExternalAccessoryProvider.m:453) 4 libdispatch.dylib 0x0000000185b6d088 _dispatch_call_block_and_release + 24 (init.c:994) 5 libdispatch.dylib 0x0000000185b6d048 _dispatch_client_callout + 16 (object.m:502) 6 libdispatch.dylib 0x0000000185b79b74 _dispatch_main_queue_callback_4CF$VARIANT$mp + 1016 (inline_internal.h:2500) 7 CoreFoundation 0x0000000186191eb0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 (CFRunLoop.c:1815) 8 CoreFoundation 0x000000018618fa8c __CFRunLoopRun + 2012 (CFRunLoop.c:3111) 9 CoreFoundation 0x00000001860affb8 CFRunLoopRunSpecific + 436 (CFRunLoop.c:3245) 10 GraphicsServices 0x0000000187f47f84 GSEventRunModal + 100 (GSEvent.c:2245) 11 UIKit 0x000000018f6842e8 UIApplicationMain + 208 (UIApplication.m:3949) 12 MYAPP 0x0000000100ab8d58 main + 172 (main.m:16) 13 libdyld.dylib 0x0000000185bd256c start + 4
7
1
2.3k
Dec ’17
SiriKit for Car Rental
Hello,I notice this "Book rides and report their status." under SiriKit. Is it possible for car rentals as well? Does this fall under the same category? I.e. implementing this in a car rental app similar to Enterprise instead of apps like Lyft or Uber.
1
1
725
Feb ’18