Working on an iPhone app. After I built the app (Product -> archive) I looked at the package content. Under Frameworks folder, there are a few libraries I have not used anywhere in the project:
libswiftCoreAudio.dylib
libswiftCoreGraphics.dylib
libswiftCoreImage.dylib
libswiftDarwin.dylib
Here I have 2 questions to ask:
Where do they come from?
Is there any way to remove them from the final package?
Most likely they are required by some components of the infrastructure used by your app. Do not try to remove them; Apple works hard to minimize footprint via mechanisms such as "app thinning", so most unnecessary object code will be optimized out of the package delivered to the customer.
Related
My iOS app uses the AVFAudio framework to provide spoken feedback to the user while running. I would like this app to also run on Apple Silicon Macs (where the spoken feedback is not really necessary).
However, just importing the framework results in the following warning email after I upload to App Store Connect:
We identified one or more issues with a recent delivery for your app,
"App Name" 7.0 (24). Your delivery was successful, but you may wish to
correct the following issues in your next delivery:
ITMS-90863: Apple silicon Macs support issue - The app links with
libraries that are not present on Mac:
/System/Library/Frameworks/AVFAudio.framework/AVFAudio
I guess that this means the app will not be able to run on Macs.
How should I get this app to use the AVFAudio framework for iOS and still be available to run on macOS (AS Macs) with or without the framework on macOS?
Relevant code is:
import AVFoundation
class Speaker {
var speechSynth: AVSpeechSynthesizer
class func establishAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.interruptSpokenAudioAndMixWithOthers, .duckOthers])
try AVAudioSession.sharedInstance().setMode(.voicePrompt)
try AVAudioSession.sharedInstance().setActive(true, options: [])
UPDATE / CLARIFICATION:
Note that my project does not include multiple targets. With multiple targets, this would be fairly straightforward. I am wondering if there is a way to achieve this by taking advantage of the newer AS Macs’ ability to run apps built for iOS without a separate target.
Is this possible when using this framework?
UPDATE 2:
I have submitted a support request to Apple for this now and their first suggestion was replacing
import AVFoundation
with
import AVFAudio
and then re-uploaded to App Store Connect, but after trying this, I get the same warning email back again. Will post an update (or hopefully an answer) when I hear back from them again.
I suppose your project has multiple targets defines (i.e. one for iOS and one for macOS). In the "General" tag of your target settings you can select which frameworks should be included under "Frameworks, Libraries and Embedded Content". Add your library for iOS, remove it for macOS.
If you share the same code you between apps you can also conditionally exclude some of it for macOS.
#if os(iOS)
// iOS only code
#endif
However, just importing the framework results in the following warning email after I upload to App Store Connect:
The problem probably isn't importing, but (as the message you got indicates), linking to AVFAudio, that's the problem. So solve that, you should select your app target in the Xcode project and go to the Build Phases tab. Look at the Link Binary with Libraries line and hit the disclosure button at the beginning of the line to reveal all the libraries that are linked into your app. Find AVFAudio and change the setting (there's a popup on the right side of the line) from Required to Optional. That'll let your app link to the framework if it's there, but still run if it's not.
But wait, you're not done yet... What do you think will happen if your app tries to actually use a framework that's not linked in (because it doesn't exist on the machine)? You'll get a crash, of course. To avoid that unhappy fate, you'll need to check for the existence of the framework before you use it. For example, you could do something like:
if NSClassFromString("AVAudioPlayer") != nil {
// do your AVFAudio stuff here
}
Further follow up from Apple support suggested the following:
Change back to import AVFoundation
Reduce the deployment targets from the latest and greatest back down to something less recent.
So I did both of these, changing the deployment targets from iOS 14.5 and macOS 11.3 to iOS 14.1 and macOS 11.0.
This has resolved (or worked around!) the issue.
I do want to deploy to the latest and greatest target for this particular app, so I don't consider it to be a complete solution as yet, but it will do as a work around for now. (I actually want to deploy to 15.0 when it's available to make use of the promised improvements to OSLogStore.)
So this sounds like a bug to me, and I have queried Apple for some further information on the issue, in particular, the ability to target more recent OS versions.
I would like to create a dynamic framework and distribute it. It needs to work when running on simulator, an iPhone device, and be able to submit to the App Store.
I am aware of lipo thanks to this guide. It shows how we can combine the simulator architectures with the iphone architectures into a single dynamic framework that can be distributed without distributing the source code. It allows me to use the framework for both simulator and iPhone device. However, it fails when I try to send it to the App Store because of the simulator build.
Carthage provides an interesting solution. It creates a framework that works for both simulator and iPhone. But when it is time to send it to the App Store we need to run a script to rip out the simulator slices.
However, the creators of DoubleClick for Publishers (DFP) have found a way to create a framework that works on both simulator and device; Passes the App Store submission; And doesn't require any script to rip out the simulator slices (AFAIK). All I really have to do is drag the GoogleMobileAds.framework file into Linked Frameworks and Libraries. It is a little surprising that I don't need to add it to Embedded Binaries like most dynamic frameworks)
I created a sample project that uses DFP that demonstrates this.
// clone repository
git clone --recursive https://github.com/wh1pch81n/DoubleClickGoogleExample.git
// Move to correct branch
git submodule foreach 'git checkout master'
git submodule foreach 'git pull origin master'
Now you can choose the Simulation_mate scheme and run the app. The app will show a single advertisement banner at the bottom to show that the ad is actually working.
The dependency graph looks like this.
Simulation_mate.app
-> Mate.framework
-> DFP.framework
-> GoogleMobileAds.framework
Again, I can run Simulation_mate on simulator, iPhone, and can submit it to the App Store.
My question is how in the world did they create GoogleMobileAds.framework like this? What steps need to be followed to create a framework like this?
As suspected, the GoogleMobileAds.framework is a static framework, rather than a dynamic framework.
> file GoogleMobileAds
GoogleMobileAds: Mach-O universal binary with 5 architectures: [arm_v7: current ar archive] [arm_v7s] [i386] [x86_64] [arm64]
GoogleMobileAds (for architecture armv7): current ar archive
GoogleMobileAds (for architecture armv7s): current ar archive
GoogleMobileAds (for architecture i386): current ar archive random library
GoogleMobileAds (for architecture x86_64): current ar archive random library
GoogleMobileAds (for architecture arm64): current ar archive
Whereas, running file on a dynamic framework, you get a different result:
> file AppKit
AppKit: Mach-O universal binary with 2 architectures: [i386: Mach-O dynamically linked shared library i386] [x86_64]
AppKit (for architecture i386): Mach-O dynamically linked shared library i386
AppKit (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64
Unfortunately, the problem you describe is a known limitation of the App Store distribution system. In theory, the correct way to distribute your dynamic framework should be to distribute the full fat binary with all architectures, and let either the build system throw out the the unneeded architectures when building, or let the App Store distribution system do that when slicing the app for specific devices ("slicing"). If I had to guess, the App Store distribution fails with such frameworks due to iOS 8 and below support, since those systems do not support slicing from the App Store.
The reason Google creates static libraries is in order to support iOS 7. A silly reason at this day and age, it has created many issues for developers, since Google also insists on using CocoaPods. Static frameworks, while superficially seem like a silver bullet at first ("no scripts for lipoing out archs; no embedding"), are actually not that great. For instance, if you'd like to include non-compiled resources, a static framework will not do the trick (dynamic frameworks are bundles, while static frameworks are not).
I'm studying IOS/Swift development and I'd like to build an IOS app that in the future will have its own Today Extension. I've read that it is better to put the common logic (for example the logic that accesses Internet resources, performs some job on the result etc) between the App and its extension into a Custom Framework.. Since I think I'll need to change both Framework and App code during the development of the App, I'd like to avoid building the framework in a separate project, than use Show In Finder function to locate the framework bundle and then drag-and-dropping it within the App project.. So I'd like to know if this is the correct way to set up a Workspace with an App, the framework it uses and the Today Extension of the App:
Create a new Project with an IOS App target
Select the project in the Project Navigator and then Editor->Add Target... and specify a Cocoa Touch Framework
Editor->Add Target... and specify Today Extension
Add the framework in Linked Frameworks and Libraries
In this way I've seen that changes to the Framework sources do not need a Framework target recompilation, maybe because the project knows about the need to recompile framework sources even if I only build & run the IOS App target that uses that framework? Am I doing it right? I've seen that the Today Extension created in such way does not lists the framework in the Build Phases -> Target Dependencies while the IOS App does, but both of them correctly use the updated code of the Framework when I update it.
Then, I've noticed the following warning when I compile the project, but I don't know if it is related to the way I've setup the project:
ld: warning: linking against dylib not safe for use in application extensions: /Users/gianni/Library/Developer/Xcode/DerivedData/TestFrameworkApp-dshihhfiuepeqzddbnpgnfwilhem/Build/Products/Debug-iphonesimulator/TestFramework.framework/TestFramework
BTW is it normal that the entries within the Products folder are always red? Is it due to the fact that I build only for IOS Simulator since I don't have a Paid developer account that allows me to build for an IOS Device? Does this prevent me to build a framework that can be exported for some other Project since I can't find the Framework bundle with a right clicking on the framework product and selecting Show In Finder (and so I can't drag-and-drop it to another project)?
I hope I've been clear enough and that you'll point me in the right direction to understand what is the suggested way of starting my new project :)
It sounds to me like you're headed down the exact path I would take.
Regarding the warning you're seeing... I believe that checking the "Allow app extension API only" for your shared framework will supress that and cause build failures when you attempt to use a API that is not allowed in extensions.
Apple iTunes Submission Error : Unable to process application at this time due to following error: bundle 'xyz.app' is missing a bundle executable.
Certificates and Profile : Checked
This is likely happening due to the use of a "fake framework" (either the one from https://github.com/kstenerud/iOS-Universal-Framework or variations of this design). Some libraries and packages had their resources bundled together as a "framework" to make the inclusion of headers, nibs, images and localizable strings easier for host projects. However, those frameworks were actually a mutant bundle and don't meet the definition/structure that Apple is enforcing now that they've added Cocoa Touch Frameworks.
Some solutions:
Split the "fake framework" into a separate .a binary, resource bundle and set of headers. I'd recommend using Cocoapods to package them up so that anyone trying to incorporate the library/bundle/headers doesn't have to deal with manual inclusion at multiple steps. You can still lipo together multiple .a files that are targeting the architectures for devices/simulators to create a universal binary and all will work fine.
Switch to using a Cocoa Touch Framework (though, these appear to only work for deployment target 8.0) and embed the binary into the app. If you go this route, you'll need to also make a separate bundle for your resources because your framework now contains a binary (and thus, adding it as a "resource" package is going to cause you to have multiple binaries in your app when you package it and it'll get rejected from the app store). See here for some info on creating an iOS resource bundle: http://jaym2503.blogspot.ro/2014/03/how-to-create-resource-bundle-in-ios.html . There are some other nuances and gotchas with this approach (such as if you lipo together the simulator/device binaries in the framework and then embed the framework, you'll get rejected from the app store for having a binary embedded that includes architectures for the simulator). Unfortunately, as of now there isn't much documentation from Apple on how to use these frameworks, so it's a lonely road.
I'd personally recommend the Cocoapod route.
Check that you are building for arm64 armv7 armv7s
This was simply an update that didn't change anything to do with the rejection reason. Here is the response from the resolution center:
Reasons
Program License Agreement
PLA 3.3.12
We found your app uses the iOS Advertising Identifier but does not include ad functionality. This does not comply with the terms of the iOS Developer Program License Agreement, as required by the App Store Review Guidelines.
Specifically, section 3.3.12 of the iOS Developer Program License Agreement states:
"You and Your Applications (and any third party with whom you have contracted to serve advertising) may use the Advertising Identifier, and any information obtained through the use of the Advertising Identifier, only for the purpose of serving advertising. If a user resets the Advertising Identifier, then You agree not to combine, correlate, link or otherwise associate, either directly or indirectly, the prior Advertising Identifier and any derived information with the reset Advertising Identifier."
Please check your code - including any third-party libraries - to remove any instances of:
class: ASIdentifierManager
selector: advertisingIdentifier
framework: AdSupport.framework
If you are planning to incorporate ads in a future version, please remove the Advertising Identifier from your app until you have included ad functionality.
To help locate the Advertising Identifier, use the “nm” tool. For information on the “nm” tool, open a terminal window and enter, “man nm.”
If you do not have access to the libraries source, you may be able to search the compiled binary using the "strings" or "otool" command line tools. The "strings" tool lists the methods that the library calls, and "otool -ov" will list the Objective-C class structures and their defined methods. These techniques can help you narrow down where the problematic code resides.
The third party libraries I'm using is the latest version of parse.com sdk, latest version of flurry analytics, and version 2.2 of test flight. Does anybody know which library is the issue? Thanks
In Terminal:
Go to the root folder of your project
Type: grep -r advertisingIdentifier . (the dot at the end is critical)
This will recursively check every file, and give you the name of the offending library.
(in my case, it was Flurry)
Update
Google released Google Analytics Services iOS SDK v3.03c which removes the AdSupport.framework dependency by default.
Full changelog entry for Release Version 3.03c (February 19, 2014): https://developers.google.com/analytics/devguides/collection/ios/changelog
Old Awnser
My problem was with Google Analytics and TestFlight.
For TestFlight I just update the library to the version 2.2.1 (https://www.testflightapp.com/sdk/ios/release_notes/2.2.1/).
But for updating Google Analytics I had to remove the -ObjC flag. But as I use Cocoapods to manage some of third libraries, I couldn't just remove it. So I did the follow:
1 - started updating the libGoogleAnalytics.a to Version 3.03a (February 5, 2014)
2 - then a removed also the AdSupport.framework
3 - added "-force_load\ "${PROJECT_DIR}/Source/Library/GoogleAnalyticsServicesiOS_3.03a"" under "Target --> Build Settings --> Other Linker Flags"
(Note that in my project the GoogleAnalytics is inside /Source/Library/ folder, and you have to configure your own)
But as I coudn't remove -ObjC flag I searched for how to use -force_load flag with Cocoapods, then I found two useful links:
1 - https://github.com/CocoaPods/CocoaPods/issues/712
2 - http://www.deanmao.com/2012/12/31/linker-error-using-cocoapods/
To summarize I changed the -ObjC flag for "-force_load $(TARGET_BUILD_DIR)/libPods.a" also under "Target --> Build Settings --> Other Linker Flags"
But again, when I tried to publish my app I got an error telling me that the compiler didn't find libPods.a, so I went to "Target --> Build Settings --> Other Linker Flags --> Release" and change this string $(TARGET_BUILD_DIR) to ${BUILT_PRODUCTS_DIR}.
So my Other Linker Flags became like the image below.
Hope to help somebody.
Sorry for my english.
=]
For me, Flurry Analytics and TestFlight were both the problem.
For TestFlight, it's as easy as updating it. The 2.2.1 version won't cause any problem (I checked using strings, as Apple suggest)
For Flurry, there is currently no fix apart from removing Flurry, and contrary to Lou Weed suggestion, even if AdSupport.framework is not linked, your app will be rejected.
Here is Flurry support answer :
"Thank you for reaching out to Flurry. We have recently learned that Apple has rejected some apps it believes are using the Identifier for Advertising (IDFA) without including ad functionality. Please note that Flurry does not collect the IDFA unless an app includes the AdSupport.framework for ad functionality. We are seeking clarification with Apple and we will update our affected customers as we learn more over the next several days."
Flurry just released the version 4.3.2 of their SDK, which specifically address that problem.
Turns out the Testflight v2.2.0 was the conflict. They have since fixed it according to their changelog: Consolidate both SDK versions into one which removes all access to ASIdentifierManager
Crashlytics was the problem. We had a few apps rejected because of it. But I already talked with Crashlytics and they released a new update today (version 2.1.6) which fix this issue.
Several libraries have the AdSupport.framework but they do not use it unless specifically called. Crashlytics was calling the framework because it had to check if the app had support for ads. And that change was implemented on version 2.1.5
So if you are using crashlytics, that's the most probable reason. To fix that, just re-archive your app so crashlytics can use the new version with this fix.
Response from Crashlytics Team:
"We just pushed out an update for this- can you build and run your app again with the Mac app open so it can update your SDK? You'll be all good to go after that! Keep me posted after you resubmit :)"
PS: This is starting to happen because Apple seem to have changed their policy for ADSupport usage.
I'd previously written the following in comments, directing people to the "strings" or "otool" terminal commands. However, I really like the suggested answer of using grep. So you can try it first. My suggestion is to prepend "git" to that command, because it's so much faster:
git grep advertisingIdentifier
If that doesn't work, then try (as posted earlier):
grep -r advertisingIdentifier .
What follows is what I'd previously written in a comment above, as a how-to for terminal commands:
You can search your project's files in Xcode or you can try removing the AdSupport framework to see what fails at build/run time.
To use the terminal, click Spotlight (search) and type Terminal. Wait for it to appear as an Application in the search results.
Once in Terminal, type "cd" followed by a space, then drag and drop your Xcode build folder from Finder into Terminal. This should automatically type that folder name in. Hit Enter, and it will change directories (cd) you to that folder.
From there, type strings then a space, then the filename for your library or otool, a space and the filename for library. You should be able to hit TAB to auto-complete filenames.
My app was also rejected with the same error! I found an occurrence of advertisingIdentifier in the latest Facebook SDK (3.12). Maybe you can check your library's for an occurence with the method below:
I opened the FacebookSDK.framework as a library in the terminal and typed the following command
otool -v -s __TEXT __objc_methname FacebookSDK | grep advertisingIdentifier
But I don't know what to do. Was my app rejected because of this reference? If yes what to do if I want to use facebook functionalities in my app?
This is a bit more complicated than it seems on the surface. After some experimentation I found that the AdSupport Framework is linked even if only directly accessing classes that are referenced in the AdSupport Framework. Ironically, [AsIdentifierManager class], which is used in a lot of third-party libraries to check whether or not the AdSupport Framework has been linked, will actually cause the AdSupport Framework to be linked. Obfuscating the class by using NSClassFromString(#"AsIdentfierManager") will not cause the AdSupport framework to be automatically linked. Of course, most of the time this code will be in third-party libraries so you won't have much control over it, however, this is what's going on.
I built an example project on GitHub that illustrates this behavior using Segment.io's framework.
https://github.com/distefam/AdSupportDemo
Flurry Analytics uses this API as well.
Terminal output:
Binary file ./Lib/Flurry/libFlurry_4.3.0.a matches
Flurry says that the selector is not called if AdSupport framework isn't linked.
So I removed the framework and tried submitting again.