I am going through a few Swift tutorials on how to build simple apps, as I am just starting to code. I want to make my app iOS 9 compatible, as I have an iPad 3. However, all the os.log statements generate an error in Xcode which tells me to add an if #avaliable statement before any of the os.log statements. What does os.log do, and if I need it, is there an issue using an if #avaliable statement for iOS 9 compatibility? If not, what is the equivalent code for iOS 9 to go in the else statement after the if #avaliable statement? Thanks.
From Apple's documentation:
Unified logging is available in iOS 10.0 and later, macOS 10.12 and
later, tvOS 10.0 and later, and watchOS 3.0 and later, and supersedes
ASL (Apple System Logger) and the Syslog APIs. Historically, log
messages were written to specific locations on disk, such as
/etc/system.log. The unified logging system stores messages in memory
and in a data store, rather than writing to text-based log files.
There is no iOS9 equivalent. You could use a third party logging tool like CocoaLumberjack, which is very popular.
As a concrete example of how to use this logging:
if #available(iOS 10.0, *) {
let bundleID:String = Bundle.main.bundleIdentifier ?? "unknown"
let oslog = OSLog(subsystem: bundleID, category: "Model")
os_log("%#", log: oslog, type: .info, message)
}
Related
In iOS 15.6 beta5 and iOS 16.0 beta:
When an UISceneDidDisconnectNotification was posted, any active SKStoreProductViewController instance would crash for the unrecognized selector named sceneDisconnected.
-[SKStoreProductViewController sceneDisconnected:]: unrecognized selector sent to instance 0x115161a00
This crash only happened in the latest iOS15.6 and iOS16 beta version. Yet I can't find the selector name in any official documents……
Any solutions? Or Is there anything I haven't done right?
Looks like this was fixed in the iOS 15.6 release candidate that was realeased today.
Sept 14 2022 Update: Apple re-introduced this crash in the official 15.7 release. It only seems to happen when the app is force quit so it should not affect users. I've filed a ticket to Apple on feedbackassistant.apple.com and would encourage others to do the same.
Not a solution, but a clear indication that this is Apple's bug to fix. Starting with a clean sample project, all you need to do is present a SKStoreProductViewController and then force quit your application:
import StoreKit
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let storeKitViewController = SKStoreProductViewController()
storeKitViewController.loadProduct(withParameters: [
SKStoreProductParameterITunesItemIdentifier: NSNumber(integerLiteral: 364709193)
])
present(storeKitViewController, animated: true)
// Force quit after presentation to trigger crash
// -[SKStoreProductViewController sceneDisconnected:]: unrecognized selector sent to instance
}
}
I've filed this with Apple via Feedback Assistant.
The crash has been fixed in iOS16
below is an explanation by Apple Framework Engineer
This crash happens in the public release of iOS/iPadOS 15.7, and seed releases of iOS/iPadOS 16 prior to seed 4 [1]. It does not occur in the public release of iOS 16.
The crash primarily happens when the app is in the background and is about to be terminated by the operating system. As a result, these crashes are not expected to be visible to the majority of end users. (One exception is on iPad with an app that supports multiple scenes, and a user manually terminates a scene.)
Your analytics will show an increased in crash rate, however your customers should not be affected by this issue.
We are actively working to address the crash.
[1] In which case you should update to the most recent release.
source: https://developer.apple.com/forums/thread/714464?answerId=729646022#729646022
Able to reproduce a crash with the same stack symbols locally and the code below stops the crash from occurring. The code is creating empty functions to handle the unrecognized selector message and restricting this extension to iOS 15.7:
#available(iOS, introduced: 15.7, obsoleted: 16.0)
#objc extension SKStoreProductViewController {
func sceneDisconnected(_ arg: AnyObject) {}
func appWillTerminate() {}
}
Add this code in application(_:didFinishLaunchingWithOptions:) (at least before the app is terminated). It adds the methods at runtime if they don’t exist.
if #available(iOS 15.7, *) {
if #unavailable(iOS 16.0) {
class_addMethod(
SKStoreProductViewController.self,
Selector(("appWillTerminate")),
unsafeBitCast({ _, _ in } as #convention(c) (SKStoreProductViewController, Selector) -> Void, to: IMP.self),
"v#:"
)
class_addMethod(
SKStoreProductViewController.self,
Selector(("sceneDisconnected:")),
unsafeBitCast({ _, _, _ in } as #convention(c) (SKStoreProductViewController, Selector, NSNotification) -> Void, to: IMP.self),
"v#:#"
)
}
}
It seems they removed appWillTerminate and sceneDisconnected(_:) methods in iOS 15.7, but forgot to remove the code that adds UIApplication.willTerminateNotification and UIScene.didDisconnectNotification observers to the NotificationCenter.
Edit: They seem to have re-added appWillTerminate and sceneDisconnected(_:) methods in iOS 15.7.1. So I’ve updated the code to add the methods only in iOS 15.7.
Edit: They removed the methods AGAIN in iOS 15.7.2 and the crash recurred. I’ve reverted the code.
This is actually an inaccurate reporting because it doesn't actually cause the crash.
I reproduced the reported scene: click on the app-download advertisement in your app, pop up the in-app App Store download page, and then back to the background, the crash message is generated in this time -- but no crash actually occurred, and nothing changed when I returned to the foreground. Everything works fine. The next time the app starts, the crash information will be reported.
So no need to do anything now, just wait for Apple to fix it.
Since version 15.7.1 has been released, we see a decrease in crashes in Firebase Crashlytics. Also when filtering the crashes on iOS version, version 15.7.1 is not present in the list.
This seems a good indication that Apple has finally resolved this issue in 15.7.1.
Please share your observations from your own crash reports to verify that my assumption is correct.
I have an app which deployment target is iOS 12.1, with many protocols defining functions with completion handlers, i.e.
protocol P {
func f(_ completion: #escaping: (String) -> Void)
}
I would like to replace all these with the new async/await iOS 15 syntax, for a better code readability:
protocol P {
func f() async -> String
}
But when doing so, I get the error:
Concurrency is only available in iOS 15.0.0 or newer
What is a good solution for this, considering that I just cannot switch the deployment target from 12.1 to 15.0?
Thank you for your help
For other people who are looking facing this issue on 15.0 target.
This is likely related to your XCode version, you need to be have minimum XCode 13.2 or later to compile code which uses Swift Concurrency on versions older than iOS 15.
The short answer is "there is currently no solution." If you want your apps to run on iOS 12 and earlier, you can't use the async/await calls, unless you want to write 2 versions of all your async code, one that runs on iOS < 15, and the other that runs on iOS ≥ 15.
As George mentions in his comment, Apple is trying to figure out how to "back-depoloy" async/await support. If they are able to do that, you will be able to use the modern approach with older versions, but I would bet Apple will not go back as far as iOS 12.
Edit:
See Bradley's comment below. The best you will get is async/await support in iOS 13, if Apple is able to pull that off. From the link Bradley posted, iOS 12 definitely won't be supported.
How can we check if the OS running on iPhone is the latest one. Is there any API for that?
For example, user is using iOS 14.6, so I want to know if its the latest version that he is using
I know this code is for checking, what OS user is having. This is not the answer at all. Please read the question before closing
if (#available(iOS 14.6, *)) {
// Use iOS 11 APIs.
} else {
// Alternative code for earlier versions of iOS.
}
No there is no way except available only if you created a personal Api to provide this info for your app automatically when opened
I want to disable OSLog for my iOS app (release build). I am not using OSLog in my app but I still see some logs in the console app logged by the apple frameworks (like libnetwork, Corefoundation, SystemConfiguration etc.,). Is there is a way to completely turn off all the logs for the app?
I added the below values to the environment variable but still, it disables the logs only when I ran the application from XCode however, I still see OSLog for my app on the console app when I launch it by clicking the App Icon.
OS_ACTIVITY_MODE=disable
Note: My app uses both Objective C and Swift programming language and disabled NSLog(Objective C) and print(Swift) and I do not have an issue with it. I want to disable all the logs including logs from the Apple framework for my release build.
just a quick solution but not the most comfortable..
#ifndef DEBUG
#define OSLog(FORMAT, ...) {}
#endif
if DEBUG mode is not used, which should be RELEASE then, OSLog() will be exchanged with void function content at compile time. Which will apply to code you wrote, not turning off Logs in general outside your sources scope.
To Disable OSLog on Release Builds
You could create a global function like:
func customLogger(_ category: String) -> OSLog {
#if DEBUG
return OSLog(subsystem: Bundle.main.bundleIdentifier!, category: category)
#else
return OSLog.disabled
#endif
}
For more detailed and advanced scenarios please check Apple's official documentation.
I'm using the built in string method uppercaseString, like this:
let capitalLetters = myString.uppercaseString
The documentation tells this for availability:
iOS (8.3 and later)
However, Xcode is not giving a compiler error, with the if #available recommendation, i.e:
if #available(iOS 8.3, *) {
} else {
}
My question is simple. Can I use this method in an app targeting 8.0? I cannot test it on a device with this version because I don't have one. And the simulator I have is 8.4.
From Apple's documentation:
var uppercaseString: String { get }
Available in iOS 2.0 and later.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/occ/instm/NSString/uppercaseString
Sometimes xcode shows you wrong availability versions (since swift came out). If sometimes you aren't sure about the availability - check it online.
PS: You can download simulators for which version you want