Issues in using CNCopyCurrentNetworkInfo - objective-c

I am using the below code to retrieve the SSID of the WiFi network the iPod is connected.
NSArray *ifs = (id)CNCopySupportedInterfaces();
NSLog(#"%s: Supported interfaces: %#", __func__, ifs);
id info = nil;
for (NSString *ifnam in ifs) {
info = (id)CNCopyCurrentNetworkInfo((CFStringRef)ifnam);
NSLog(#"%s: %# => %#", __func__, ifnam, info);
if (info && [info count]) {
break;
}
[info release];
}
Sometimes this code is not returning the proper SSID of the network my device is connected.Any pointers on why the SSID is not retrieved correctly? Does CNCopyCurrentNetworkInfo package dependent on the iOS version of the device?
Thanks.

add SystemConfiguration.framework to project.
import < SystemConfiguration/CaptiveNetwork.h >
CFArrayRef myArray = CNCopySupportedInterfaces();
CFStringRef interfaceName = CFArrayGetValueAtIndex(myArray, 0);
CFDictionaryRef captiveNtwrkDict = CNCopyCurrentNetworkInfo(interfaceName);
NSDictionary *dict = ( NSDictionary*) captiveNtwrkDict;
NSString* ssid = [dict objectForKey:#"SSID"];
NSLog(#"%s ssid : %#",__FUNCTION__, [ssid description]);
For iOS 12 and later, you must enable it from capabilities.
Important
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID. Documentation link

Yes. CNCopyCurrentNetworkInfo is available only in iOS 4.1 and later.
For more info ,please look at the developer.apple SystemConfiguration Reference
you can check the sample code here

Related

can't load Safari contentBlocker. because can't access app group's NSUserDefault

I am making iOS 9 Safari AdBlocker app.
I am using the module of AdBlockPlusSafari.
App works good on simulator.
But when try to run it on device(iPhone6), it fails to reload contentBlocker.
[SFContentBlockerManager
reloadContentBlockerWithIdentifier:self.contentBlockerIdentifier
completionHandler:^(NSError *error) {
if (error) {
NSLog(#"Error in reloadContentBlocker: %#", error);
}
dispatch_async(dispatch_get_main_queue(), ^{
wSelf.reloading = NO;
[wSelf checkActivatedFlag];
if (completion) {
completion(error);
}
});
}];
it gives error
Error Domain=ContentBlockerErrorDomain Code=3 "(null)"
It caused by accessing the values in NSUserDefault (App Group).
- (instancetype)init
{
if (self = [super init])
{
_bundleName = [[[[[NSBundle mainBundle] bundleIdentifier] componentsSeparatedByString:#"."] subarrayWithRange:NSMakeRange(0, 2)] componentsJoinedByString:#"."];
NSString *group = [NSString stringWithFormat:#"group.%#.%#", _bundleName, #"AdBlockerPro"];
NSLog(#"Group name: %#", group);
_adblockProDetails = [[NSUserDefaults alloc] initWithSuiteName:group];
[_adblockProDetails registerDefaults:
#{ AdblockProActivated: #NO,
AdblockProEnabled: #YES
}];
_enabled = [_adblockProDetails boolForKey:AdblockProEnabled];
_activated = [_adblockProDetails boolForKey:AdblockProActivated];
}
return self;
}
The App Group name in host app and safari extension is same.
But in Safari extension, when app accesses the setting in NSUserDefault, it gives me the error.
In Project setting/Capabilities, I did all for App Group. In app id, it involves app group name exactly.
This happens on only device. On simulator, it works good. I can't find the reason of this error.
Please help me if you are experienced this.
Looking forward to your help.
I found the reason myself.
I have put something (NSMutableDictionary) in app group container and did something to write file to extension bundle.
It is prohibited by Apple.
So I deleted all from AdBlockManager (interacts with app group) except flag variables (Boolean type).
And I proceeded the file management using NSFileManager.
http://www.atomicbird.com/blog/sharing-with-app-extensions
Finally, app extension is working for me on device.
Good luck!

Is it possible to "unpair" a Bluetooth device in Cocoa/ObjC?

I've paired an IOBluetoothDevice in my Mac/Cocoa app and would like to "unpair" it programmatically. That is, I would like to remove the device from the left pane of the Bluetooth section of System Preferences.
I've seen [IOBluetoothDevice removeFromFavorites], but that just removes the heart icon next to the "Favorite" attribute of the device -- the device is still listed in the left pane.
Is this possible through Cocoa?
In the above picture, I would like to programmatically remove "Apple Mighty Mouse" from the left pane.
Paired devices are a part of System Preferences.
You can find the file with the bluetooth preferences in /Library/Preferences, its name is com.apple.Bluetooth.plist.
However, you cannot edit the file directly. You should use SCPreferences class from System Configuration framework.
Note the API for accessing/modifying system preferences is pretty low level.
EDIT: The following code works if run in superuser mode. I am not a Mac OS developer myself but it should be possible to init it with an AuthorizationRef and run it with user mode (the user will confirm access to system configuration).
SCPreferencesRef prefs = SCPreferencesCreate(kCFAllocatorDefault,
CFSTR("Test"),
CFSTR("/Library/Preferences/com.apple.Bluetooth.plist"));
const CFStringRef PAIRED_DEVICES_KEY = CFSTR("PairedDevices");
NSArray *pairedDevices = (__bridge NSArray *) SCPreferencesGetValue(prefs, PAIRED_DEVICES_KEY);
NSLog(#"Paired devices: %#", pairedDevices);
NSString *deviceToRemove = #"e4-32-cb-da-ca-2f";
NSMutableArray *newPairedDevices = [pairedDevices mutableCopy];
[newPairedDevices removeObject:deviceToRemove];
Boolean valueSet = SCPreferencesSetValue(prefs, PAIRED_DEVICES_KEY, (__bridge CFPropertyListRef) [NSArray arrayWithArray:newPairedDevices]);
NSLog(#"Value set: %#", (valueSet) ? #"YES" : #"NO");
if (!valueSet) {
NSLog(#"Error: %#", SCCopyLastError());
}
Boolean saved = SCPreferencesCommitChanges(prefs);
if (!saved) {
NSLog(#"Error: %#", SCCopyLastError());
}
NSLog(#"Saved: %#", (saved) ? #"YES" : #"NO");
CFRelease(prefs);

Sandboxing coreWLAN?

I'm writing an OS X application that depends on the ability to determine wireless signal strength, but I can't figure out what entitlements to use to sandbox it.
Whenever I use
NSMutableArray *scanResults;
CWInterface *currentInterface = [CWInterface interface];
NSLog(#"currInterface: %#\n", currentInterface);
NSMutableDictionary *signalsDict = [[NSMutableDictionary alloc] init];
NSError *err = nil;
scanResults = [NSMutableSet setWithSet:[currentInterface scanForNetworksWithSSID:nil error:&err]];
I get the error The operation couldn't be completed. (com.apple.coreWLAN.error error 1.) despite having all entitlements checked in XCode. What entitlement(s) am I missing?
The CoreWLANWirelessManager sample project has the same problem.
CoreWLAN doesn't seem to be available at all to sandboxed apps.
Apple's developer documentation states "With App Sandbox, your app cannot modify the system’s network configuration (whether with the System Configuration framework, the CoreWLAN framework, or other similar APIs)", which seems to imply that reading but not writing settings might be OK, but that doesn't seem to work in practice, and this is confirmed by a post by Apple DTS: https://forums.developer.apple.com/thread/11307
How about the entitlements for the Wifi Diagnostics app that ships with Mac OS X 10.11.1, located at /System/Library/CoreServices/Applications/. Checking entitlements I see that it posseses the following: com.apple.wifi.associate, com.apple.wifi.scan, com.apple.wifi.set_channel, com.apple.wifi.start_autojoin, com.apple.wireless-diagnostics, and com.apple.wireless-diagnostics.basic_report.
Are we mere mortals working in a sandbox not able to get these?
I think you need to check Outgoing Connections (Client).
You might want to use Apple80211 private framework by using dlfcn.h library. An iphone example can be found here:
http://www.csse.uwa.edu.au/~chris/iphone/APlogger/
Download the source file and investigate scanner module.
In summary, you will come up with something like this:
#define IF_NAME "en0"
#include <dlfcn.h>
- (void)performScan
{
int (*open)(void *);
int (*bind)(void *, NSString *);
int (*close)(void *);
int (*scan)(void *, NSArray **, void *);
void *libHandle;
void *airportHandle;
libHandle = dlopen("/System/Library/Frameworks/Preferences.framework/Preferences", RTLD_LAZY);
open = dlsym(libHandle, "Apple80211Open");
bind = dlsym(libHandle, "Apple80211BindToInterface");
scan = dlsym(libHandle, "Apple80211Scan");
close = dlsym(libHandle, "Apple80211Close");
open(&airportHandle);
bind(airportHandle, #IF_NAME);
NSArray *found;
NSDictionary *params = [[NSDictionary alloc] init];
scan(airportHandle, &found, params);
int nnw = [found count];
for(int i=0 ; i < nnw ; i++) {
NSDictionary *nw = [found objectAtIndex:i];
NSString *ssid = [self fixSSID:nw];
// RSSI indicates signal strength
int rssi = [[nw objectForKey:#"RSSI"] intValue];
}
// Cleanup
close(airportHandle);
dlclose(libHandle);
}
-(NSString *)fixSSID:(NSDictionary *)nw
{
if ([[nw objectForKey:#"HIDDEN_NETWORK"] boolValue])
return #"<hidden>";
else
return [nw objectForKey:#"SSID_STR"];
}
Note that if you use private frameworks in your iOS apps, you will not be able to publish them on App Store (Apple will reject your app because there is no public documentation for Apple80211 framework). but since your question is regarding OSX development, this doesn't apply for your case.
Hope it helps.

Retrieving stored passwords from keychain fails outside XCode

I am storing generic passwords in the keychain following Apple's example code in the "Keychain Services Programming Guide".
Everything works fine as long as I am running the App in Debug mode from Xcode. However when I archive and export the app, it will still store passwords (visible in Keychain Access) but is not able to retrieve them.
The keychain constantly returns errSecAuthFailed (-25293). This occurs on Mountain Lion but not on Snow Leopard. My App is code signed and sandboxed. To me it seems that when retrieving the password, keychain does not recognize the App as the same one that stored the password, because when I set the password to be accessible by any application it also works well.
I use the following code:
+ (NSString*) retrievePasswordFromKeychainWithKey: (NSString*) theKey {
SecKeychainUnlock(NULL, 0, NULL, FALSE);
const char* userNameUTF8 = [NSUserName() UTF8String];
uint32_t userNameLength = (uint32_t)strlen(userNameUTF8);
uint32_t serviceNameLength = (uint32_t)strlen([theKey UTF8String]);
uint32_t pwLength = 0;
void* pwBuffer = nil;
SecKeychainItemRef itemRef = nil;
OSStatus status1 = SecKeychainFindGenericPassword (NULL, serviceNameLength, serviceNameUTF8, userNameLength, userNameUTF8, &pwLength, &pwBuffer, &itemRef);
if (status1 == noErr) {
NSData* pwData = [NSData dataWithBytes:pwBuffer length:pwLength];
SecKeychainItemFreeContent (NULL, //No attribute data to release
pwBuffer //Release data buffer allocated by SecKeychainFindGenericPassword
);
return [NSString stringWithCString:[pwData bytes] encoding:NSUTF8StringEncoding];
}
//status1 is always -25293
return nil;
}
OK, I just learnt that this is an open bug in Mac OS 10.8.0. Apps signed with a Developer ID cannot access data from the keychain.
I hope this will be fixed in 10.8.1...
A workaround is not to sign the App with your Developer ID. (I have also read that Apps built under Lion are not affected by this bug, but I could not test this, yet)

AssetsLibrary does not get images saved in the Camera roll when I run the program on the device

I wrote a simple iOS program to get number of photo images which are saved in the camera roll by using 'Assets Library' framework provided in the SDK4.2.
The program worked well as I expected when I ran it on the iPhone simulator.
But, it didn't retrieve any images when I ran on the 'real' iPhone device (iPhone 3GS with iOS 4.2.1).
This problem looks like as same as the problem discussed in the below article:
Assets Library Framework not working correctly on 4.0 and 4.2
So, I added the "dispatch_async(dispatch_get_main_queue()..." function as below, But I couldn't solve the problem.
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray assets = [[NSMutableArray array] retain]; // Prepare array to have retrieved images by Assets Library.
void (^assetEnumerator)(struct ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if(asset != NULL) {
[assets addObject:asset];
dispatch_async(dispatch_get_main_queue(), ^{
// show number of retrieved images saved in the Camera role.
// The [assets count] returns always 0 when I run this program on iPhone device although it worked OK on the simulator.
NSLog(#"%i", [assets count]);
});
}
};
void (^assetGroupEnumerator)(struct ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
}
};
// Create instance of the Assets Library.
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos // Retrieve the images saved in the Camera role.
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failed.");
}];
}
Could you please tell me if you have any ideas to solve it?
I have 1 update:
To get error code, I modified the failureBlock of the enumerateGroupsWithTypes as below, and then reproduced the symptom again.
Then, the app returned the error code -3311 (ALAssetsLibraryAccessUserDeniedError).
However I didn't any operation to deny while my reproducing test.
What's the possible cause of the err#=-3311?
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failed");
resultMsg = [NSString stringWithFormat:#"Failed: code=%d", [error code]]; }];
It is strange that location services should be involved when accessing saved photos. Maybe it has to do with geo-tagging information on the photos. Anyways Apple says that enabling location services is required when using enumerateGroupsWithTypes:usingBlock:failureBlock:
Special Considerations
This method will fail with error ALAssetsLibraryAccessGloballyDeniedError if the user has not enabled Location Services (in Settings > General)."