I've got an app where I want to split its Model code into a framework (.xcframework and .framework for debugging) so that it can be used by more than one app.
The code has dependencies on 3rd party code, which are installed via pods.
During the conversion process I keep running into the same issue which manifests with all the 3rd party code - which is that the majority of its api can be used (something like 80-90%) but for the remainder there is a linker error at runtime showing undefined symbols.
I have this problem with CocoaLumberjack,RealmSwift, PhoneNumberKit and more.
Its very quick and easy to reproduce the issue with a minimal framework and minimal app, below I'll describe how a minimal setup using CocoaLumberjack reproduces the issue:
From scratch, I use Xcode to create a framework project, run pod init, then modify the pod file to be:
platform :ios, '16.0'
workspace 'TheFramework'
project 'TheFramework'
target 'TheFramework' do
use_frameworks!
pod 'CocoaLumberjack/Swift', '3.8.5'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0'
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
end
end
end
Then I add source code:
import Foundation
import CocoaLumberjack
public class AClassInTheFramework {
public class func aMethod() {
let consoleLogger = DDOSLogger.sharedInstance
DDLog.add(consoleLogger, with: .debug)
DDLogDebug("Some logging")
}
}
Within the Xcode project, Build Libraries for Distribution is set to Yes, I also add that line to the pod file in case CocoaLumberjack isn't set similarly.
In the Framework's Xcode General section, Frameworks and Libraries contains Pods_TheFramework.framework set to Do Not Embed.
In the Build Phases section, in the Link Binary with Libraries section, Pods_TheFramework.framework is set to required.
Next I create an Xcode app template, run pod install, and edit the app pod file to be:
platform :ios, '16.0'
workspace 'AppUsingFramework'
project 'AppUsingFramework'
target 'AppUsingFramework' do
use_frameworks!
pod 'CocoaLumberjack/Swift', '3.8.5'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0'
end
end
end
I build the framework, and drag and drop it into the app.
I add the following code to the app's delegate:
import TheFramework
...
AClassInTheFramework.aMethod()
The App's target has the following linkage settings:
When I build and run the app, there is the following error:
If I change the source code in the framework to this:
public class AClassInTheFramework {
public class func aMethod() {
let consoleLogger = DDOSLogger.sharedInstance
DDLog.add(consoleLogger, with: .debug)
// DDLogDebug("Some logging")
}
}
Then there is no error and the code runs successfully. This illustrates the problem I've encountered with all the nested frameworks - in this particular case calls to DDLog.add() don't result in an error but calls to DDlogDebug() do, and that has been mirrored with other nested frameworks (for example with Realm, opening a database, adding, finding,retrieving an item all works without a problem, however attempting to use Realm's Results<> API results in a similar symbol not found error).
Additionally note that the identical CocoaLumberjack code can run fine when used directly from within the app, i.e., if I add the following code to the app:
import CocoaLumberjack
func useCocoaLumberjackDirectlyFromWithinApp() {
let consoleLogger = DDOSLogger.sharedInstance
DDLog.add(consoleLogger, with: .debug)
DDLogDebug("Some logging")
}
useCocoaLumberjackDirectlyFromWithinApp()
Then it runs, i.e. DDLogDebug() can be successfully called from within the app, its only when its called via the framework that the error occurs.
Why might I be encountering these issues? I'd have thought either I'd be able to use 100% of the nested framework's public api, or 0% of it (is something is not configured correct), not ~80% which is what I am encountering.
Any ideas?
TIA
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I've noticed that NSLog() doesn't do anything on iOS 26 when an app is downloaded from TestFlight.
I've got an app uploaded with a liberal sprinkling of NSLog lines in it for debugging purposes - if I download that onto an iOS 18 devices, and use Apple Configurator's console or the Mac's console app, then the logging output is verbose and as expected.
But if I download that same app onto an iOS 26 handset, there's nothing.
Logging is visible in the Xcode console, however sometimes there are situations where you need console logging with a TestFlight build.
What happened? Was this an intentional change? Why. This is a major hindrance in diagnosing issues / bugs etc.
If an iOS application has a notification service extension which gets sent a push, but the user has not been prompted for notification authorization via requestAuthorization() then what is the expected behavior?
Will the push get delivered to the NSE but the resulting notification not displayed? Or will the push not get delivered at all to the NSE?
One of the responses to a call to AgeRangeService.shared.requestAgeRange is AgeRangeService.Error.invalidRequest.
This has no documentation. What on earth is an invalid request - I mean the app just calls the API, there's no parameters supplied or anything, how can the request ever be invalid?
If the app calls AgeRangeService.shared.requestAgeRange and gets this as a response then what is the app supposed to do with that?
Topic:
App & System Services
SubTopic:
General
If I have two iOS apps, with bundle ids com.mycompany.A and com.mycompany.B, and they both have the shared group id of group.com.mycompany.B, then they can read/write data to same file (in shared defaults or, for example, a Realm database saved to the same shared group location).
What I have noticed is that if both apps get installed and some data X written to shared defaults (but data which isn't accessed by both apps i.e. if only app A uses this data), then if app A is deleted and reinstalled then X is not deleted in this situation (unless both apps A and B are deleted).
I guess that is to be expected as they both use the same group id and the OS won't clear that area out unless both apps are deleted.
However I think I am seeing a situation where if app A has group.com.mycompany.A as its group and app B has group.com.mycompany.B as its group, then this is still the same situation - data written by app B for doesn't get deleted if only app B is deleted/reinstalled on the phone. It won't get deleted unless both app B and A are deleted from the phone.
This is not what I was expecting.
I need to perform some more checks (its more complex to verify as app A is developed by one company and app B by another), but if this were to be the case would this be unexpected behavior by the OS? Presumably it would be unexpected as the container should be identified by the full group id and not a partial portion of the group id right?
I wrote a notification content extension which with 10.1 was working perfectly and reliably - it was getting called 100% of the time, as it should, and eveything was great and working as it should.But now with 10.2 I just cannot get the bugger to work at all - the extension just isn't being called by the OS (actually it was called once out of about 30 attempts and on that one occasion the UNNotificationExtensionDefaultContentHidden YES flag was ignored, which it didn't use to be with 10.1)Anybody else having problem with content extensions with 10.2, but if not is there some trick or tweak or change you applied to get them to work?
It an app receives a Voip push, but has to terminate the call, then if this happens a few times the OS is terminating the app (Message from debugger: Terminated due to signal 9) and then it stops delivering future pushes to the app.i.e. with code let endCallAction = CXEndCallAction(call: callUUID) let transation = CXTransaction(action: endCallAction) self.callController.request(transation) { (error) in } provider.reportNewIncomingCall(with: callUUID, update: callUpdate) { error in Model.priorityLog("GRUNT reportNewIncomingCall completion error \(String(describing: error))") completion() }
We have an app which gets sent APNSs by the server and use a Notification Service Extension to intercept them.
In my observations I've never seen throttling applied on the device to these pushes (unlike app directed push where its easy to observe happening if you let the batter run low for example).
The Apple documentation says it may perform server side throttling, but when, how often, under what circumstances?
If the app has 30 million users and the server sends them 10m or 100 pushes per day will it get throttled?
Does anybody have any have any documented or observed info on this.
I'm running an app with the com.apple.developer.usernotifications.filtering entitlement and the filtering works fine with iOS 15.
However I tried running it on an older phone / OS (iPhone 8 / iOS 13.1.2) and it does not work.
Is this entitlement not supported with iOS 13.1.2? If so, what is the earliest iOS version that supports it?
The handset logged this:
The OS displayed:
default 07:55:58.800825-0800 NotificationServiceExtension
Mutated notification request will not visibly alert the user, will deliver original content
If the app is run on iOS 15, it says :
default 07:20:27.251600-0800 SpringBoard Mutated notification request will not visibly alert the user, will supress original content
I've got a notification service extension which is working as expected unless the user
a) turns the passcode for the iPhone on and
b) turns on Auto-lock and the iPhone is locked when a push arrives
If auto-lock is turned on and the phone is locked but passcode is not turned on, then the notification is displayed as expected.
The iPhone displays a notification with the top line being the name of the app and the second line saying "Notification", which is neither the content within the push payload, not what is being set as the content within the extension.
There's no difference in behaviour of the extension when passcode is enabled. Adding logging to the extension I can confirm the content is being replaced and posted as expected:
NSLog("Going to post notification title: \(notificationContent.title) subtitle: \(notificationContent.subtitle) body: \(notificationContent.body)")
contentHandler(notificationContent)
The notificationContent is as expected and is identical when passcode is on off.
Why is the iPhone displaying the notification incorrectly if passcode is turned on and the phone locked?
If a unwanted communication extension target is created,then the auto-generated template code doesn't work. This can very quickly be demonstrated by:
In Xcode create a new iOS project
In that project create an unwanted communication extension target (I called it cheese so its easy to see in the console)
Add some logging lines to the UnwantedCommunicationReportingExtension code that was created.
Install app to iPhone, and select it in the settings app
Swipe on a call in the call history.
The following lines can be seen in the iPhone console:
Unknown class _TtC6cheese13MainInterface in Interface Builder file.
View controller is not of class ILClassificationUIExtensionViewController
None of the logging lines added to the code are displayed, also the Done button in the presented UI is disabled - but in the code that is generated by the target templete is:
self.extensionContext.isReadyForClassificationResponse = true
Which is supposed to enable the Done button.
In the storyboard that gets created the custom class is set to MainInterface:
How are you supposed to get this stuff working if it doesn't work out of the box? MainInterface isn't being recognized as the view controller class, and in the drop down UnwantedCommunicationReportingExtension doesn't appear.
class UnwantedCommunicationReportingExtension: ILClassificationUIExtensionViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NSLog("viewDidAppear")
// Notify the system when you have completed gathering information
// from the user and you are ready with a classification response
self.extensionContext.isReadyForClassificationResponse = true
}
// Customize UI based on the classification request before the view is loaded
override func prepare(for classificationRequest: ILClassificationRequest) {
NSLog("prepare")
// Configure your views for the classification request
}
// Provide a classification response for the classification request
override func classificationResponse(for request:ILClassificationRequest) -> ILClassificationResponse {
return ILClassificationResponse(action: .none)
}
}
I'm experimenting/testing the new background download extension. The documentation for it says that the applicationDidInstall() and applicationDidUpdate() methods are only called if the app is installed from the App Store or from Testflight (I tried installing via Apple Configurator 2 but they are not called then).
Hence in order to check these out I have to first upload the app to Testflight, but there's an error about the info.plist - however the info.plist is created/populated using Xcode's target template for the extension, so if Xcode itself is incorrectly creating the info.plist how is one supposed to know what's wrong with it and how to fix it?
This is the content of the info.plist that was generated:
<key>EXAppExtensionAttributes</key>
<dict>
<key>EXExtensionPointIdentifier</key>
<string>com.apple.background-asset-downloader-extension</string>
<key>EXPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).BackgroundDownloadHandler</string>
</dict>
And the error uploading to Testflight
P.S. There's no apparent relevant tag for this extension
In Xcode 14 Beta 4, there was a target type for the new background download extension. With Beta 5 this target type has been removed.
So with Beta 5 how is one supposed to create a target of this type?
I want to use features described in the background assets WWDC22 session, however with Xcode 14 Beta 5 there is no application extension of this type.
There was an application extension type with beta 4, but there are two problems with this
firstly Testflight doesn't recognize it so its impossible to upload and therefore use (because some of the features are documented as only working when the app is installed from Testflight/App Store).
secondly, if one creates an extension using Xcode beta 4, then it complies with beta 4, but it won't compile with beta 5.
Therefore it is currently impossible to even begin to start working with the background assets extension framework.
My notification service extension has the com.apple.developer.usernotifications.filtering entitlement and it would appear that this is preventing a contact with an image from either being added or updated, any attempt to do so results in calls to CNContactStore.execute() throwing an CNInvalidRecords error.
If the filtering entitlement is removed then there are no problems and the contact can be added/updated without problem.
If the entitlement is present but the contact is added/updated without an image then it is also problem free.
It is specifically adding a contact with an image that cannot be accomplished if the entitlement is present.
I've encountered problems with this entitlement before - for example there are problems attempting to use a CXCallDirectoryManager from within a notification service extension with this extension. Quote from Apple on the call extension issue:
"Because that entitlement can "hide" notifications, its sandbox profile is actually more restrictive than the standard NSE sandbox. That's what's preventing access to CXCallDirectoryManager, not the App ID properties.
-Kevin Elliott DTS Engineer, CoreOS/Hardware"
I'd be appreciative if somebody from Apple could comment and confirm if the problems with updating contacts with an image with this entitlement is the same fundamental sandboxing issue?
If so, I don't understand why a contact can be added with this entitlement, its only an issue if the contact has an image, does that imply a bug?