I am trying to advertise a service with Multipeer Connectivity in macOS 10.13 as such:
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: serviceID);
super.init();
self.serviceAdvertiser.delegate = self;
self.serviceAdvertiser.startAdvertisingPeer();
}
where
private let serviceID = "sample-test";
private let peerID = MCPeerID(displayName: Host.current().localizedName!);
Instead of getting the proper delegate callback methods getting called the advertiser immediately fails and this is what I get in the console:
2017-10-16 11:22:35.568607-0700 macApp[3060:288948] [] tcp_listener_socket_create bind(fd 3) failed: [1] Operation not permitted
2017-10-16 11:22:35.569223-0700 macApp[3060:288940] [MCNearbyServiceAdvertiser] Server did not publish: errorDict [{
NSNetServicesErrorCode = 1;
NSNetServicesErrorDomain = 1;
}].
Any idea how to solve this?
UPDATE:
Running the exact same code in an iOS Simulator works fine so I'm guessing it has something to do with some permissions on the Mac machine.
Seeing that the log says that this is a permission issue I went ahead and enabled the root user on the Mac and tried running the same code to no avail.
I am thinking of turning off System Integrity Protection but I have a hard time coming to terms with the fact that Apple would publish this framework if all these security compromises are required in order to use it. Will keep investigating.
After days of struggle the solution is very simple. Make sure you enable the networking entitlements for your target. See attached snapshot:
Related
I am working on an "uninstaller" for an macOS app we've had for several years now. The purpose for the uninstaller is to allow us to put a given system into a nascent state as if the original app had never been installed so that we can more reliably test the install process.
The original app has an extensive array of preferences stored in UserDefaults. In the original app there is a resetToDefaults() method which works just fine resetting all the defaults however for the uninstaller we'd wanted to remove the values completely. It looks to be straight-forward and this is what I came up with...
func flushPreferences() {
let defaults = getDefaultPreferences()
for preferenceName in defaults.keys.sorted() {
UserDefaults.standard.removeObject(forKey: preferenceName)
}
UserDefaults.standard.synchronize()
}
... which does not work at all.
I read in the documentation
Removing a default has no effect on the value returned by the objectForKey: method if the same key exists in a domain that precedes the standard application domain in the search list.
and I don't really understand what "domain" relates to and thought it might be app so tried the code as a test in the original app and that does nothing either.
Someone else suggested this, which also does nothing
let appDomain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: appDomain)
I also found some test code which works absolutely fine... which looks to be nearly identical to what I'm doing. I even tried using it with hard-coding one of our pref keys and that fails as well.
func testRemoveObject() {
let myKey:String = "myKey"
UserDefaults.standard.set(true, forKey: myKey)
let beforeVal = UserDefaults.standard.value(forKey: myKey)
print("before: \(beforeVal ?? "nil")")
UserDefaults.standard.removeObject(forKey: myKey) // Note: This is the only line needed, others are debugging
let afterVal = UserDefaults.standard.value(forKey: myKey)
print("after: \(afterVal ?? "nil")")
}
What am I missing? It looks like this one (based on what I've been able to find on the web) can be somewhat mysterious but I'm thinking it must be something obvious that I'm not seeing.
Well, thanks to red_menace's suggestion I found one article that led to another that suggested that the following command will reset the user preferences cache:
killall -u #USER cfprefsd
which seemed to work (yay) but upon further investigation it appears that simply closing the app is what updates the actual preference in the .plist and that changing it in the app will not show up until you exit.
This makes sense as it explains why you can create a preference on the fly save it, confirm it saved, delete it and confirm it deleted but cannot delete a previously saved preference — as similar to the persistent prefs perhaps the new preference is not added to the cache until the application exits.
This could also explain the various odd behaviors that other posters were finding (only worked every other time, had to do it asynchronously, etc.). As for NSUserDefaults.synchronize() has been depreciated and developer.apple.com indicates that it is unneeded and does nothing.
So one problem solved...
As it turns out my initial instinct was accurate as well and you cannot access prefs from one app in another using the removeObject(forKey: preferenceName)
// Will not work cross-application, though will work locally (inter-app)
UserDefaults.standard.removeObject(forKey: preferenceName)
To get it to work cross applications you have to use CFPreferencesSetAppValue(_ key:, value:, applicationID:) which is part of the "Preferences Utilities" section of the Core Library which requires that you know the appDomain of the initial app. So, the final solution is:
In the source app:
let appDomain = Bundle.main.bundleIdentifier! // Note, needed by uninstaller
will give you the domain for the stored preference in the source app.
And in the app doing the changing — the final working code:
func flushPreferences() {
let defaults = getDefaultPreferences()
let sourceAppDomain = "{THE_BUNDLE_ID_FROM_SOURCE_APP}"
for preferenceName in defaults.keys {
print("Preference name: \(preferenceName)")
CFPreferencesSetAppValue(preferenceName as CFString,
nil,
sourceAppDomain as CFString)
}
}
Hope this helps someone save some time at some point - thanks to everyone who contributed. This one was a BEAR!
I would like to be able to somehow detect if my app is running on a beta version of macOS 11, as there are some known bugs I want to inform users about. I only want to show such an alert to macOS 11 beta users, meaning not macOS 10.15 users nor users on the final version of macOS 11. I could of course just submit an app update to remove the alert when macOS 11 is close to being done, but it would be nice to have something reusable I could use in multiple apps and for future macOS beta versions.
Constraints:
The app is sandboxed.
The app is in the App Store, so no private APIs.
The app doesn't have a network entitlement, so the detection needs to be offline.
I don't want to bundle a list of known macOS build numbers and compare that.
My thinking is that maybe it's possible to use some kind of sniffing. Maybe there are some APIs that return different results when the macOS version is a beta version.
I believe you're out of luck. About This Mac uses PrivateFrameworks/Seeding.framework, here the important disassembly:
/* #class SDBuildInfo */
+(char)currentBuildIsSeed {
return 0x0;
}
So it seems this is a build time compiler flag. The plists in the framework don't contain this flag unfortunately.
Sample private API usage: kaloprominat/currentBuildIsSeed.py
For the crazy ones: It would be possible to read the binary and compare the assembly for the function. I'd start with the class-dump code, which gets you different fat binaries and the function offset.
This is far from perfect but macOS BigSur release notes mention:
Known Issues
In Swift, the authorizationStatus() property of CLLocationManager is incorrectly exposed as a method instead of a property. (62853845)
This^ is new API introduced in MacOS11 / iOS14
So one could detect this for this particular beta.
import CoreLocation
func isMacOS11Beta() -> Bool {
var propertiesCount = UInt32()
let classToInspect = CLLocationManager.self
var isMacOS11OrHigher = false
var isMacOS11Beta = false
let propertiesInAClass = class_copyPropertyList(CLLocationManager.self, &propertiesCount)
if classToInspect.responds(to: NSSelectorFromString("authorizationStatus")) {
isMacOS11OrHigher = true
isMacOS11Beta = true
for i in 0 ..< Int(propertiesCount) {
if let property = propertiesInAClass?[i], let propertyName = NSString(utf8String: property_getName(property)) as String? {
if propertyName == "authorizationStatus" {
isMacOS11Beta = false
}
}
}
free(propertiesInAClass)
}
return isMacOS11OrHigher && isMacOS11Beta
}
I am building a Swift 5 application with XCode 10.3. For this, I have a framework which contains a implementation for a logsystem (for debugging purposes). The default implementation for this logsystem is based on OSLog/os_log. When using the system in the consuming app, then none of the logs appear in the Console app. However, when placing breakpoints, I can see that the os_log statement (see code example below) is reached and that the correct parameters are passed on to the os_log function. However, when I use os_log or NSLog in the host application, then they do show up.
I have verified that it is not an issue with the LogSystem / DefaultLogImplementation types as all of the breakpoints that need to be hit, are hit, and all unit tests are green. In addition, the os_log statement is reached and executed, but the logs do not appear. I have verified that all messages are shown in the console app, I have verified that I have selected the correct device, I have tried multiple filters (and even dug through all the logs without filters enabled)...
Can anyone help / give a pointer at what the issue may be? I am currently suspecting that there is a bug in the implementation of OSLog/os_log.
Code sample
App-side code, which consumes code similar to the examples provided below
class SomeClass {
private let logSystem = LogSystem()
func doSomethingImportant() {
// Do important stuff and log the result
logSystem.debug("Finished process with result \(result)")
}
}
Framework-side code, which is consumed by the app
public class LogSystem {
private let logImplementation: LogImplementation
init(_ logImplementation: LogImplementation = DefaultLogImplementation()) {
self.logImplementation = logImplementation
}
public func debug(_ message: String) {
logImplementation.log(message, type: .debug) // And some other parameters...
}
public func error(_ message: String) {
// Similar to debug(:)...
}
}
public protocol LogImplementation {
func log(_ message: String, type: LogType, ...other parameters...)
}
public class DefaultLogImplementation: LogImplementation {
func log(_ message: String, type: LogType, ...other parameters...) {
let parsedType = parseLogType(type) // Function that parses LogType to OSLogType
let log = OSLog(subsystem: "my.subsystem.domain", category: "myCategory")
os_log("%{private}#", log: log, type: parsedType, message) // Breakpoint here is reached, but log does not appear in console app (even with debugger attached, which should remove the effect of "%{private}%". Either way, at the very least censored logs should still appear in console app.
}
}
Additional info
Swift version: 5.0
XCode version: 10.3
Target device: iOS 12.2 iPhone X simulator
NSLog appears in console app: Yes
Selected correct device in console app: Yes
Correct filters: Yes
Update 2019-08-16 13:00 (Amsterdam time)
It appears that only .debug level messages are not appearing in the Console app. This bug occurs when using any simulator device in combination with OSLog. I have tried several commands to fix this:
sudo log config --mode level:debug,persist:debug
sudo log config --subsystem my.reverse.domain.name --mode level:debug,persist:debug
Neither of them fixed the issue. In fact, not a single debug-level message of the simulator is showing up in the console app (not even from iOS itself). Yes, the option to show .info and .debug level messages is enabled.
I tried setting the log-level for the simulator specifically through the following command:
xcrun simctl spawn booted log config --mode level:debug
But this result in an error:
log: Simulator unable to set system mode
In the console app, there are two options to include / hide debug and info messages:
I've spoken with an Apple DTS Engineer and it's a know issues not being able to see debug messages using a simulator (in Xcode 11 too): open a feedback
I am trying to use some old IOKit functionality in a new Swift 4.0 Mac app (not iOS). I have created a bridging header to use an existing Objective C third party framework, DDHidLib, and I am current working in Xcode 9.
The code that attempts to create a plug in interface for a usb gamepad falls over on IOCreatePlugInInterfaceForService, returning a non-zero error.
The truly bizarre thing is I have an older app created in a previous version of Xcode that uses the same framework and works correctly after opening in the new Xcode 9. This previous project is still Swift using a bridging header for the same Obj-C framework. I have checked the build settings and tried to make everything match, but I get the same result; the old app works but any new apps do not.
Is there a way to either: find out the exact differences in build settings/compilers to see what the elusive difference may be, OR to step into the IOCreatePlugInInterfaceForService IOKit method to see what may be causing the error to be returned in one project but not another?
EDIT: Here is the method that is failing:
- (BOOL) createDeviceInterfaceWithError: (NSError **) error_; {
io_name_t className;
IOCFPlugInInterface ** plugInInterface = NULL;
SInt32 score = 0;
NSError * error = nil;
BOOL result = NO;
mDeviceInterface = NULL;
NSXReturnError(IOObjectGetClass(mHidDevice, className));
if (error)
goto done;
NSXReturnError(IOCreatePlugInInterfaceForService(mHidDevice, kIOHIDDeviceUserClientTypeID,kIOCFPlugInInterfaceID,&plugInInterface,&score));
if (error)
goto done;
//Call a method of the intermediate plug-in to create the device interface
NSXReturnError((*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &mDeviceInterface));
if (error)
goto done;
result = YES;
done:
if (plugInInterface != NULL)
{
(*plugInInterface)->Release(plugInInterface);
}
if (error_)
*error_ = error;
return result;
}
In the old version that works, IOCreatePlugInInterfaceForService always returns a value of 0. In all the versions that don't work, the return value appears to always be -536870210. The mHidDevice in this function is the io_object_t handle for the device.
EDIT2: Here is the IORegistryExplorer page for the device
Finally managed to resolve this after weeks of head scratching. The new Xcode 9 uses app sandboxing to basically prevent access to USB, bluetooth, camera and microphone etc. by default in a new app. Once I switched this off it reverted to it's expected behaviour.
Glad it was such a simple answer in the end but disappointed Xcode does not provide more descriptive error messages or responses to let a user know they are essentially preventing themselves from accessing the parts of the system they need.
Looks like kIOReturnNoResources is returned if the loop at the end of IOCreatePlugInInterfaceForService completes with haveOne == false for whatever reason. Perhaps Start() is returning false because another process or driver already has exclusive access? I'd check what clients the device has in IORegistryExplorer.
This error also happens when an application is trying to access the camera or bluetooth on MacOS 10.14 and higher. Permission shall be granted either explicitly by user (pop-up window), or through the Security & Privacy. The application should check for permission as shown here.
Here is an example of what I want to do:
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError)
{
let nm = NetworkModel()
nm.sendlog("file name :AppDelegate , line number : 288", info: " Failed to register: \(error)")
}
current scenario i done that hard coded value line number and file name . but is it possible to programatically pick line number and file name .
Literal Type Value
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
#dsohandle UnsafeMutablePointer The dso handle.
Example
print("Function: \(#function), line: \(#line)")
With default values in parameters you can also create a function
public func track(_ message: String, file: String = #file, function: String = #function, line: Int = #line ) {
print("\(message) called from \(function) \(file):\(line)")
}
which can be used like this
track("enters app")
In Swift 2.1
Literal Type Value
__FILE__ String The name of the file in which it appears.
__LINE__ Int The line number on which it appears.
__COLUMN__ Int The column number in which it begins.
__FUNCTION__ String The name of the declaration in which it appears.
for more info see the documentation
You can use #function, #file, #line
Here is the implementation of log method in swift : https://github.com/InderKumarRathore/SwiftLog
Below is the snippet
public func debugLog(object: Any, functionName: String = #function, fileName: String = #file, lineNumber: Int = #line) {
#if DEBUG
let className = (fileName as NSString).lastPathComponent
print("<\(className)> \(functionName) [#\(lineNumber)]| \(object)\n")
#endif
}
For swift 4 and swift 5:
func printLog(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
#if DEVELOPMENT
let className = file.components(separatedBy: "/").last
print(" ❌ Error ----> File: \(className ?? ""), Function: \(function), Line: \(line), Message: \(message)")
#endif
}
// "❌ Error ----> File: classNameViewController.swift, function: functionName(), Line: 123, Message: messageError"
Example Logging Code (TL;DR)
import os.log
#available(OSX 11.0, iOS 14.0, *)
extension Logger {
private static var subsystem = Bundle.main.bundleIdentifier!
/// Logs the payment flows like Apple Pay.
#available(OSX 11.0, iOS 14.0, *)
static let payments = Logger(subsystem: subsystem, category: "payments")
}
static func DLog(message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, column: UInt = #column, category: String, type: OSLogType = .info, bundle: Bundle = .main) {
// This method is only for iOS 14+
if #available(OSX 11.0, iOS 14.0, *) {
Logger.payments.debug("\(file) : \(function) : \(line) : \(column) - \(message, privacy: .private)")
// This makes the message unreadable without a debugger attached.
} else {
// Fallback on earlier versions
let customLog = OSLog(subsystem: bundle.bundleIdentifier!,
category: category)
// IMPORTANT: I have assumed here that you only print out non-sensitive data! Using %{private}# or %{public}# in an interpolated string is not portable to `Logger`!
os_log(message, log: customLog, type: type)
// Unfortunately this legacy API doesn't support non-StaticString logs. :(
}
}
Note that you may need to rework this code for more flexibility over private/public access levels - os_log and Logger don’t handle privacy levels in the same way. This is just to show how to use the API if you don’t add os_log privacy levels in message.
Apple recommend using OS Logging which is why I have used this approach rather than print statements. I added this answer because of the new Logger API in iOS 14 that also enables string interpolation.
Motivation
I think the answers here solve the question for < iOS 14, but they can be improved further for memory efficiency since this Debug Logging function will likely be used all across a codebase in iOS 14+ (new Logger API). Apple recommend using OS Logging which is why I have used this approach rather than print statements (although these will still work).
These are minor improvements but I think they can help because even minor improvements add up. In fact, the Swift Standard Library uses these optimizations everywhere they can)! These optimisations are contributing factors to the incredible memory efficiency of Swift despite being a high-level language (and part of great API Design!). (-:
Because this function would probably fit well as part of a generalized (OS) Logging service, I have also included considerations I make when logging within apps. I think these could help you when logging for debugging purposes (I answered this question because it was probably for debug logging!).
Improvements
I prefer to use UInt in certain situations if I know the Int is going to be positive, and any edge case crashes are unlikely. Note, I don't use UInt when interfacing with Foundation classes that use Int types for this reason. This allocates less memory at runtime and I find being more specific also helps me understand the code better.
I prefer to use StaticString where I know the string is known at compile-time. From the docs, StaticString only provides low-level access to String contents and is immutable. Thus only use it where appropriate. Even concatenated String literals cannot be used to initialize a StaticString. Because its functionality is more restricted than String this means that it is lightweight - only requiring an address pointer and length under the hood. Using StaticString also provides a small performance boost with OS-level memory management because it is neither allocated nor deallocated (no need for reference counting).
Using Apple's recommended Logger API for logging is better than print statements for multiple reasons: performance, privacy and unified management. It can be better for debugging issues in released products (OS Logs provide more context).
I would recommend using StaticString where you can, for example with the function and file names, and UInt for the line and column numbers. This is only now possible with the Logger iOS 14+ API.
(Bonus) Logging Considerations
Should I do LOTS of logging?
I think logging is a balancing act. Lots of logging can be very helpful to debug issues when viewing crash reports if you have access to these. However, you need to balance Privacy, Size of the Compiled Binary and Overwhelming the Logging System.
1. Privacy: Redact sensitive info using "\(message, privacy: .private)" and "\(message, privacy: .public)" with Logger, and DON'T print sensitive information when using NSLog or print statements. I would recommend replacing print statements (for production) with OS Logs when they are for debugging purposes. Or use a preprocessor directive (#if DEBUG) inside the logging service to print only on a Debug scheme..
2. Size of the Compiled Binary: Logging statements are essentially more lines of code. The more code you add, the bigger your binary. This is usually not an issue, as long as you don't log everything under the sun since only a large increase in this metric would affect the binary size.
3. Overwhelming the logging system: If you log excessively, the device may have to discard some logs in order to keep running (limited RAM or swap space at runtime due to OS multitasking - worse for older devices). Apple is usually reliable in handling lots of user logging, and it's hard to achieve this in practice. Nevertheless, if you do encounter this, some more useful log events could be pushed out of the window.
OS Logs
The most important thing to get right is the log level. By default on iOS log entries at .info and below will be suppressed at the point of log generation, so the only real negative with those is the binary size. If you log at .log (.default for os_log) or higher, you need to be careful about not logging too much. Apple's general advice is that you look at each of these higher-level log entries to make sure they contain info that might be useful while debugging.
Finally, make sure to set the subsystem and category. The unified logging system processes a lot of log entries - the subsystem with category makes it much easier for you to focus on specific problems.
How to choose the category and subsystem?
This is a pretty opinionated topic, so I will focus on what I would think about when deciding. YMMV: there may be better options and if so, please let me know.
1. By feature (product-specific): For example, if you are a shopping app, maybe organise the subsystem by the payment, login or other app flows. If you aren't already using a modular codebase for organisation into features (or frameworks for shared code) then I can recommend this tutorial series.
2. By technology: What I mean by this is the domain of the code, is it for Push Notifications, Deep Links, User Defaults (or persistence) logic. This could be helpful for the category parameter.
3. By target: I like to also use the bundleIdentifier of the current Bundle if I can for the subsystem. This makes even more sense if you have Extension Targets (Push Notifications), multiple Targets, or different Platforms (like WatchOS, SiriKit extensions and similar). You can get the current bundle, using this code:
let myBundle = Bundle(for: MyClass.self)
Debugging user issues using OS device logs
See here for a detailed walkthrough (Access Device Console Logs). The main idea is to connect the device to your Mac (or use the Mac itself for Mac apps ;)), open Console.app, run the app and try to reproduce the crash. If you manage to get that far in the first place, then you can correlate the time of the crash with the logs for more context.
Any other reasons for logging less?
If you are an Apple Early-Adopter then you know how buggy some of the iOS Betas can be ;). When posting bug reports to Apple, the OS Logs get included in sysdiagnose files, so you may get a faster turnaround time because your bug reports have less noise.
Should I use a third-party logging service?
Depends if you can afford it and whether you really need it. I prefer to minimise the number of third-party dependencies I import in my code (if I can control this) and the native (Apple) Logging APIs worked fine for most of my use cases. These services charge extra per month, but the advantage is the user cannot view the logs (even if he wants to) by hooking his device up to Console.app on his Mac. Note that this is NOT an issue if you apply the .private log levels, so I don't usually use third-party services (YMMV). A potential reason to go for web-logging is more security against reverse-engineering though, as long as you trust the third-party vendor against data breaches (BUT also it's not as eco-friendly ;)).
static func DLog(message: String, file: String = #file, function: String = #function, line: Int = #line, column: Int = #column) {
print("\(file) : \(function) : \(line) : \(column) - \(message)")
}
add where ever you want in your code (inside a struct/var/closure/func...) the next line:
let _ = print("**** \(#file)" + String(describing: type(of: self)) + ".\(#function)[\(#line)]")
and it will print the next line:
**** /path/to/my/file.swift MyClass.someFunc()[17]