Setting media playback info in MacOs not working - objective-c

I'm a newbie in ObjC and MacOs development.
My final goal is understand how setting playback info works from ObjC to try to implement that later on Rust (using generated ObjC runtime bindings).
So right now I'm trying to write small piece of code on ObjC that would set playback info (without actually playing anything).
I have found https://github.com/MarshallOfSound/electron-media-service/blob/master/src/darwin/service.mm and used it as a base.
Here's code I have right now:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <objc/runtime.h>
#include "MediaPlayer/MPNowPlayingInfoCenter.h"
#include "MediaPlayer/MPRemoteCommandCenter.h"
#include "MediaPlayer/MPRemoteCommand.h"
#include "MediaPlayer/MPMediaItem.h"
#include "MediaPlayer/MPRemoteCommandEvent.h"
#interface NativeMediaController : NSObject { }
#end
#implementation NativeMediaController
- (MPRemoteCommandHandlerStatus)remotePlay {
NSLog(#"Play");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePause {
NSLog(#"Pause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteTogglePlayPause {
NSLog(#"PlayPause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteNext {
NSLog(#"Next");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePrev {
NSLog(#"Previous");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteChangePlaybackPosition:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"ChangePlaybackPosition");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)move:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"Move");
return MPRemoteCommandHandlerStatusSuccess;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
NativeMediaController* controller = [[NativeMediaController alloc] init];
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[remoteCommandCenter playCommand].enabled = true;
[remoteCommandCenter pauseCommand].enabled = true;
[remoteCommandCenter togglePlayPauseCommand].enabled = true;
[remoteCommandCenter changePlaybackPositionCommand].enabled = true;
[remoteCommandCenter nextTrackCommand].enabled = true;
[remoteCommandCenter previousTrackCommand].enabled = true;
[[remoteCommandCenter playCommand] addTarget:controller action:#selector(remotePlay)];
[[remoteCommandCenter pauseCommand] addTarget:controller action:#selector(remotePause)];
[[remoteCommandCenter togglePlayPauseCommand] addTarget:controller action:#selector(remoteTogglePlayPause)];
[[remoteCommandCenter changePlaybackPositionCommand] addTarget:controller action:#selector(remoteChangePlaybackPosition:)];
[[remoteCommandCenter nextTrackCommand] addTarget:controller action:#selector(remoteNext)];
[[remoteCommandCenter previousTrackCommand] addTarget:controller action:#selector(remotePrev)];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:[NSString stringWithUTF8String:"Test title"] forKey:MPMediaItemPropertyTitle];
[songInfo setObject:[NSString stringWithUTF8String:"Test artist"] forKey:MPMediaItemPropertyArtist];
[songInfo setObject:[NSString stringWithUTF8String:"Test albumtitle"] forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:[NSNumber numberWithFloat:60.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[songInfo setObject:[NSNumber numberWithFloat:360.0] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithFloat:112233] forKey:MPMediaItemPropertyPersistentID];
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePlaying;
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
NSLog(#"End!");
char input[50] = {0};
printf("Enter anything to quit: ");
scanf("%s", input);
}
return 0;
}
When this code is executed I see no effect in MacOs playing info widget.
Unfortunately right now I have no idea how to debug this or where can I find better ObjC example.

Finally, I found the reason that caused my problem playing with Apple Swift example.
There're two requirements to show playback widget (i didn't find them documented anywhere) that have been missing:
You should register a callback for at least one action (play, pause)
You should build your application as an App, not as Console. If your program is not shown in the dock as a running app - the playback widget wouldn't work.

Related

How to change mouse settings programmatically in macOS using IOKit

The functions IOHIDGetAccelerationWithKey and IOHIDSetAccelerationWithKey are deprecated since macOS 10.12, therefore I am trying to implement the same using other IO*-methods.
I have never worked with IOKit, thus, all I can do is google for functions and try to get it to work.
Now I found this: Can't edit IORegistryEntry which has an example of how to change TrackpadThreeFingerSwipe property, however it is using a function which is not defined for me: getEVSHandle. Googling for it reveals only that it should be Found in the MachineSettings framework, however I can't seem to add any "MachineSettings" framework in Xcode 11.
What should I do? Current code is like:
#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDLib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSInteger value = -65536;
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &value);
CFMutableDictionaryRef propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, NULL, NULL);
CFDictionarySetValue(propertyDict, #"HIDMouseAcceleration", number);
io_connect_t connect = getEVSHandle(); // ???
if (!connect)
{
NSLog(#"Unable to get EVS handle");
}
res = IOConnectSetCFProperties(connect, propertyDict);
if (res != KERN_SUCCESS)
{
NSLog(#"Failed to set mouse acceleration (%d)", res);
}
IOObjectRelease(service);
CFRelease(propertyDict);
}
return 0;
}
The following works (tested with Xcode 11.2 / macOS 10.15)
#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDLib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
io_service_t service = IORegistryEntryFromPath(kIOMasterPortDefault,
kIOServicePlane ":/IOResources/IOHIDSystem");
NSDictionary *parameters = (__bridge NSDictionary *)IORegistryEntryCreateCFProperty(service,
CFSTR(kIOHIDParametersKey), kCFAllocatorDefault, kNilOptions);
NSLog(#"%#", parameters);
NSMutableDictionary *newParameters = [parameters mutableCopy];
newParameters[#"HIDMouseAcceleration"] = #(12345);
kern_return_t result = IORegistryEntrySetCFProperty(service,
CFSTR(kIOHIDParametersKey), (__bridge CFDictionaryRef)newParameters);
NSLog(kIOReturnSuccess == result ? #"Updated" : #"Failed");
IOObjectRelease(service);
}
return 0;
}

Reading Serial Port iOS

I have the following code to read and write to serial ports on iOS 10.3.3 Jailbroken iPhone 6S (I used h3lix to jailbreak):
Serial.h:
//
// Serial.h
// iOUSB
//
// Created by Brandon on 2018-05-21.
// Copyright © 2018 XIO. All rights reserved.
//
#if !defined(__cplusplus) //-fmodules -fcxx-modules
#import Foundation;
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wauto-import"
#import <Foundation/Foundation.h>
#pragma clang diagnostic pop
#endif
#define BAUD_RATE 9600
#interface Serial : NSObject
+ (instancetype)shared;
- (bool)setup:(NSString *)filePath;
- (bool)disconnect;
- (size_t)available;
- (size_t)read:(uint8_t *)bytes length:(int32_t)length;
- (size_t)write:(uint8_t *)bytes length:(int32_t)length;
- (void)flushInputStream;
- (void)flushOutputStream;
- (void)flush;
- (NSString *)lastError;
- (void)eraseLastError;
#end
Serial.mm:
//
// Serial.mm
// iOUSB
//
// Created by Brandon on 2018-05-21.
// Copyright © 2018 XIO. All rights reserved.
//
#import "Serial.h"
#include <fstream>
#include <iostream>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#define DEFAULT_BAUD_RATE 9600
#interface Serial()
#property (nonatomic, assign) int file_descriptor;
#property (nonatomic, strong) NSString *lastError;
#end
#implementation Serial
+ (instancetype)shared {
static Serial *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[Serial alloc] init];
});
return instance;
}
- (instancetype)init {
if ((self = [super init])) {
self.file_descriptor = -1;
self.lastError = nil;
}
return self;
}
- (void)dealloc {
[self disconnect];
}
- (bool)setup:(NSString *)filePath {
if (self.file_descriptor != -1)
{
return true;
}
self.file_descriptor = open(filePath.UTF8String, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if (self.file_descriptor == -1)
{
const char* error = strerror(errno);
self.lastError = [[NSString alloc] initWithUTF8String:error];
perror(error);
return false;
}
struct termios options;
struct termios oldoptions;
tcgetattr(self.file_descriptor, &oldoptions);
options = oldoptions;
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
int baud_rates[] = {B0, B50, B75, B110, B134, B150, B200, B300, B1200, B1800, B2400, B4800, B9600, B19200, B38400, B7200, B14400, B28800, B57600, B76800, B115200, B230400
};
#else
int baud_rates[] = {B0, B50, B75, B110, B134, B150, B200, B300, B1200, B1800, B2400, B4800, B9600, B19200, B38400};
#endif
auto it = std::find(std::begin(baud_rates), std::end(baud_rates), BAUD_RATE);
if (it != std::end(baud_rates))
{
cfsetispeed(&options, *it);
cfsetospeed(&options, *it);
std::cout<<"BAUD_RATE Set Successfully!\n";
}
else
{
cfsetispeed(&options, DEFAULT_BAUD_RATE);
cfsetospeed(&options, DEFAULT_BAUD_RATE);
std::cerr<<"Invalid BAUD_RATE.. Setting to default: "<<DEFAULT_BAUD_RATE<<"\n";
}
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag |= CS8;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
tcsetattr(self.file_descriptor, TCSANOW, &options);
return true;
}
- (bool)disconnect {
if (self.file_descriptor != -1)
{
close(self.file_descriptor);
self.file_descriptor = -1;
self.lastError = nil;
}
return self.file_descriptor == -1;
}
- (size_t)available {
if (self.file_descriptor == -1)
{
return -1;
}
int available = -1;
ioctl(self.file_descriptor, FIONREAD, &available);
return available;
}
- (size_t)read:(uint8_t *)bytes length:(int32_t)length {
if (self.file_descriptor == -1)
{
return -1;
}
ssize_t bytesRead = read(self.file_descriptor, bytes, length);
if (bytesRead < 0)
{
const char* error = strerror(errno);
self.lastError = [[NSString alloc] initWithUTF8String:error];
perror(error);
}
return bytesRead;
}
- (size_t)write:(uint8_t *)bytes length:(int32_t)length {
if (self.file_descriptor == -1)
{
return -1;
}
ssize_t bytesWritten = write(self.file_descriptor, bytes, length);
if (bytesWritten <= 0)
{
const char* error = strerror(errno);
self.lastError = [[NSString alloc] initWithUTF8String:error];
perror(error);
}
return bytesWritten;
}
- (void)flushInputStream {
if (self.file_descriptor == -1)
{
return;
}
tcflush(self.file_descriptor, TCIFLUSH);
}
- (void)flushOutputStream {
if (self.file_descriptor == -1)
{
return;
}
tcflush(self.file_descriptor, TCOFLUSH);
}
- (void)flush {
if (self.file_descriptor == -1)
{
return;
}
tcflush(self.file_descriptor, TCIOFLUSH);
}
- (NSString *)lastError {
return _lastError ?: #"";
}
- (void)eraseLastError {
_lastError = nil;
}
#end
I purchased the OTG adapter: "Lightning to USB 3 Camera Adapter" shown here: https://www.apple.com/ca/shop/product/MK0W2AM/A/lightning-to-usb-3-camera-adapter
I have plugged in the device and it shows up as unsupported device which is fine, since it's not MFI-Certified.
However, when I try to open /dev/tty.iap, I keep getting Error: Resource Busy.
I am able to open /dev/tty.wlan for example.. Only some ports give me trouble like the iap one.
What am I doing wrong that I cannot read tty.iap? I have tried to run the application as root by moving it to /Applications from /var/containers/Bundle/Applications.. I've tried to chown root:wheel the app and chmod 0777 the app. Still I get resource busy..
I've read online that you get this error when you are NOT root.
How can I run my app as root?
How can I fix this so I can read the lightning port?
I tried https://stackoverflow.com/a/15970080/1462718 but it doesn't seem to work. The app would launch and then immediately close no matter how many times I try.
By default the serial port is in use as an out-of-band management interface (allowing you to connect to the system via a serial connection if the network is down).
You will need to disable this before you can reuse the serial port for any other purpose.
The specifics of how to do this vary by OS version.

Objective-C, Authorization fails to perform function

I'm trying to create an Authorization to copy a file using SMJobBless, although I can't get it to work. The helper app is successfully authorized and the Job is available! message appears before the [self copyFile] method, but the copyFile always fails. If someone could shed some light on what I'm doing wrong or provide and example of how to make this work that would be great.
appDelegate.h
#import <Cocoa/Cocoa.h>
#interface SMJobBlessAppController : NSObject {
IBOutlet NSTextField *_textField;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
- (void)copyFile;
#end
appDelegate.m
#import <ServiceManagement/ServiceManagement.h>
#import <Security/Authorization.h>
#import "appDelegate.h"
#implementation SMJobBlessAppController
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSError *error = nil;
if (![self blessHelperWithLabel:#"com.apple.bsd.SMJobBlessHelper" error:&error]) {
NSLog(#"Something went wrong!");
} else {
/* At this point, the job is available. However, this is a very
* simple sample, and there is no IPC infrastructure set up to
* make it launch-on-demand. You would normally achieve this by
* using a Sockets or MachServices dictionary in your launchd.plist.
*/
NSLog(#"Job is available!");
[self->_textField setHidden:false];
[self copyFile];
}
}
- (void)copyFile {
NSError *error = nil;
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *sourceFile = #"~/path/to/file.txt";
NSString *destFile = #"~/Library/Application Support/myApp/file.txt";
if ([fileManager copyItemAtPath:sourceFile toPath:destFile error:&error] == YES) {
NSLog (#"[FILE] Copied.");
// NSLog (#"Copy successful");
} else {
NSLog (#"[FILE] Copy failed.");
NSLog (#" %# %#",sourceFile, destFile);
// NSLog (#"Copy failed");
}
[fileManager release];
return;
}
- (BOOL)blessHelperWithLabel:(NSString *)label error:(NSError **)error;
{
BOOL result = NO;
AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
AuthorizationRef authRef = NULL;
/* Obtain the right to install privileged helper tools (kSMRightBlessPrivilegedHelper). */
OSStatus status = AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef);
if (status != errAuthorizationSuccess) {
NSLog(#"Failed to create AuthorizationRef, return code %i", status);
} else {
/* This does all the work of verifying the helper tool against the application
* and vice-versa. Once verification has passed, the embedded launchd.plist
* is extracted and placed in /Library/LaunchDaemons and then loaded. The
* executable is placed in /Library/PrivilegedHelperTools.
*/
result = SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, authRef, (CFErrorRef *)error);
}
return result;
}
#end
You're totally missing the point of SMJobBless. It doesn't magically make your current app able to do privileged things. Instead, it installs and runs a separate helper tool, which is allowed to do privileged things, but should do nothing else (as little as possible).
You need to move your code in copyFile to the main function in SMJobBlessHelper.c. (And since that's a C file, you'll have to either rewrite it in C -- perhaps using CoreFoundation -- or you'll have to change the tool to use Objective-C. Nobody said this would be easy.)

Getting RSSIValue from IOBluetoothHostController

I'm trying to write a simple application that gathers the RSSIValue and displays it via NSLog, my code is as follows:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#import <IOBluetooth/IOBluetoothUtilities.h>
#interface getRSSI: NSObject {}
-(void) readRSSIForDeviceComplete:(id)controller device:(IOBluetoothDevice*)device
info:(BluetoothHCIRSSIInfo*)info error:(IOReturn)error;
#end
#implementation getRSSI
- (void) readRSSIForDeviceComplete:(id)controller device:(IOBluetoothDevice*)device
info:(BluetoothHCIRSSIInfo*)info error:(IOReturn)error
{
if (error != kIOReturnSuccess) {
NSLog(#"readRSSIForDeviceComplete return error");
CFRunLoopStop(CFRunLoopGetCurrent());
}
if (info->handle == kBluetoothConnectionHandleNone) {
NSLog(#"readRSSIForDeviceComplete no handle");
CFRunLoopStop(CFRunLoopGetCurrent());
}
NSLog(#"RSSI = %i dBm ", info->RSSIValue);
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 5]];
[device closeConnection];
[device openConnection];
[controller readRSSIForDevice:device];
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"start");
IOBluetoothHostController *hci = [IOBluetoothHostController defaultController];
NSString *addrStr = #"xx:xx:xx:xx:xx:xx";
BluetoothDeviceAddress addr;
IOBluetoothNSStringToDeviceAddress(addrStr, &addr);
IOBluetoothDevice *device = [[IOBluetoothDevice alloc] init];
device = [IOBluetoothDevice withAddress:&addr];
[device retain];
[device openConnection];
getRSSI *rssi = [[getRSSI alloc] init];
[hci setDelegate:rssi];
[hci readRSSIForDevice:device];
CFRunLoopRun();
[hci release];
[rssi release];
[pool release];
return 0;
}
The problem I am facing is that the readRSSIForDeviceComplete seems to work just fine, info passes along a value. The problem is that the RSSI value is drastically different from the one I can view from OS X via option clicking the bluetooth icon at the top. It is typical for my application to print off 1,2,-1,-8,etc while the menu displays -64 dBm, -66, -70, -42, etc.
I would really appreciate some guidance.
The value you get from readRSSIForDeviceComplete is the RSSI value. The value shown via OS X when option/alt-clicking the BlueTooth menu is the Raw RSSI value.
Assuming you have the Developer Tools you can see both values by using "Bluetooth Explorer" (/Developer/Applications/Bluetooth/Bluetooth Explorer). I have no idea what meaningful difference there is between the two values but for me they seem to constantly differ by a value of -60.
So your RSSI values of 1, 2, -1, -8 would correspond to Raw RSSI values of -59, -58, -61, -68.

Problem with creating sockets using CFSocket in Objective-C (iPhone app)

Ok, I have a problem with building a socket using Objective-C. If you'll take a look at my code below, through help with example code and other sources, I was able to build a complete socket I believe. The problem is that when I compile it, it builds fine (no syntax problems), but there are no sockets being created. As you'll notice I've commented out a lot of things in Server2.m and have isolated the problem to the very beginning when I create the struct for the listeningSocket. By the way, if this helps, it is part of the the server side of server-client application. Does anyone know why I would be getting this problem? Everything seemed to be working fine yesterday, and this morning I thought I would take a different approach to building the sockets, so I tried this. Thanks for any help!
Server_TrialViewController.m
#include <CFNetwork/CFSocketStream.h>
#import <UIKit/UIKit.h>
#import "Server2.h"
#import "Client_Test.h"
#interface Server_TrialViewController : UIViewController {
IBOutlet UIButton *ServerButton;
IBOutlet UIButton *ClientButton;
IBOutlet UILabel *statusLabel;
Server2 *server;
Client_Test *client;
}
#property(nonatomic, retain) UILabel *statusLabel;
#property(nonatomic, retain) Server2 *server;
#property(nonatomic, retain) Client_Test *client;
-(IBAction)serverButtonPressed;
-(IBAction)clientButtonPressed;
//-(void)sendMessageWithServer:(Server_Test *)SERVER AndClient:(Client_Test *)CLIENT;
#end
Server_TrialViewController.h
#import "Server_TrialViewController.h"
#implementation Server_TrialViewController
#synthesize statusLabel;
#synthesize server;
#synthesize client;
-(IBAction)serverButtonPressed {
if ([server start]) {
[statusLabel setText:#"Success"];
}
else {
if (server.status == NULL) {
[statusLabel setText: #"No Server: No statUpdate"];
}
else {
[statusLabel setText: #"No Server: Found statUpdate"];
}
}
}
-(IBAction)clientButtonPressed {
if ([client start]) {
[statusLabel setText:#"Client Started"];
}
else {
[statusLabel setText:#"Client Not Started"];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[super dealloc];
}
#end
Server2.h
#import <Foundation/Foundation.h>
#import "Server2Delegate.h"
#interface Server2 : NSObject
{
uint16_t port;
CFSocketRef listeningSocket;
id<Server2Delegate> delegate;
NSNetService* netService;
NSString *status;
}
// Initialize connection
- (BOOL)start;
- (void)stop;
// Delegate receives various notifications about the state of our server
#property(nonatomic,retain) id<Server2Delegate> delegate;
#property(nonatomic, retain) NSString *status;
#end
Server2.m
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <CFNetwork/CFSocketStream.h>
#import "Server2.h"
#import "Connection2.h"
#import "AppConfig2.h"
// Declare some private properties and methods
#interface Server2 ()
#property(nonatomic,assign) uint16_t port;
#property(nonatomic,retain) NSNetService* netService;
-(BOOL)createServer;
-(void)terminateServer;
#end
// Implementation of the Server interface
#implementation Server2
#synthesize delegate;
#synthesize port, netService;
#synthesize status;
// Cleanup
- (void)dealloc
{
self.netService = nil;
self.delegate = nil;
[super dealloc];
}
// Create server and announce it
- (BOOL)start
{
// Start the socket server
if ( ! [self createServer] )
{
status = #"Server Not Created";
return FALSE;
}
status = #"Server Created";
return TRUE;
}
// Close everything
- (void)stop {
[self terminateServer];
}
#pragma mark Callbacks
// Handle new connections
- (void)handleNewNativeSocket:(CFSocketNativeHandle)nativeSocketHandle {
Connection2* connection = [[[Connection2 alloc] initWithNativeSocketHandle:nativeSocketHandle] autorelease];
// In case of errors, close native socket handle
if ( connection == nil ) {
close(nativeSocketHandle);
return;
}
// finish connecting
if ( ! [connection connect] ) {
//status = #"Connection Not Made";
[connection close];
return;
}
//status = #"Connection Made";
// Pass this on to our delegate
[delegate handleNewConnection:connection];
}
// This function will be used as a callback while creating our listening socket via 'CFSocketCreate'
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
Server2 *server = (Server2*)info;
// We can only process "connection accepted" calls here
if ( type != kCFSocketAcceptCallBack ) {
return;
}
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data;
[server handleNewNativeSocket:nativeSocketHandle];
}
#pragma mark Sockets and streams
- (BOOL)createServer
{
//// PART 1: Create a socket that can accept connections
// Socket context
// struct CFSocketContext {
// CFIndex version;
// void *info;
// CFAllocatorRetainCallBack retain;
// CFAllocatorReleaseCallBack release;
// CFAllocatorCopyDescriptionCallBack copyDescription;
// };
CFSocketContext socketContext = {0, self, NULL, NULL, NULL};
listeningSocket = CFSocketCreate(
kCFAllocatorDefault,
PF_INET, // The protocol family for the socket
SOCK_DGRAM, // The socket type to create
IPPROTO_UDP, // The protocol for the socket. TCP vs UDP.
0, //kCFSocketAcceptCallBack, // New connections will be automatically accepted and the callback is called with the data argument being a pointer to a CFSocketNativeHandle of the child socket.
NULL, //(CFSocketCallBack)&serverAcceptCallback,
&socketContext );
// Previous call might have failed
if ( listeningSocket == NULL ) {
status = #"listeningSocket Not Created";
return FALSE;
}
else {
status = #"listeningSocket Created";
return TRUE;
}
}
/*
// getsockopt will return existing socket option value via this variable
int existingValue = 1;
// Make sure that same listening socket address gets reused after every connection
setsockopt( CFSocketGetNative(listeningSocket),
SOL_SOCKET, SO_REUSEADDR, (void *)&existingValue,
sizeof(existingValue));
//// PART 2: Bind our socket to an endpoint.
// We will be listening on all available interfaces/addresses.
// Port will be assigned automatically by kernel.
struct sockaddr_in socketAddress;
memset(&socketAddress, 0, sizeof(socketAddress));
socketAddress.sin_len = sizeof(socketAddress);
socketAddress.sin_family = AF_INET; // Address family (IPv4 vs IPv6)
socketAddress.sin_port = 0; // Actual port will get assigned automatically by kernel
socketAddress.sin_addr.s_addr = htonl(INADDR_ANY); // We must use "network byte order" format (big-endian) for the value here
// Convert the endpoint data structure into something that CFSocket can use
NSData *socketAddressData =
[NSData dataWithBytes:&socketAddress length:sizeof(socketAddress)];
// Bind our socket to the endpoint. Check if successful.
if ( CFSocketSetAddress(listeningSocket, (CFDataRef)socketAddressData) != kCFSocketSuccess ) {
// Cleanup
if ( listeningSocket != NULL ) {
status = #"Socket Not Binded";
CFRelease(listeningSocket);
listeningSocket = NULL;
}
return FALSE;
}
status = #"Socket Binded";
//// PART 3: Find out what port kernel assigned to our socket
// We need it to advertise our service via Bonjour
NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease];
// Convert socket data into a usable structure
struct sockaddr_in socketAddressActual;
memcpy(&socketAddressActual, [socketAddressActualData bytes],
[socketAddressActualData length]);
self.port = ntohs(socketAddressActual.sin_port);
//// PART 4: Hook up our socket to the current run loop
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0);
CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes);
CFRelease(runLoopSource);
return TRUE;
}
*/
- (void) terminateServer {
if ( listeningSocket != nil ) {
CFSocketInvalidate(listeningSocket);
CFRelease(listeningSocket);
listeningSocket = nil;
}
}
#pragma mark -
#pragma mark NSNetService Delegate Method Implementations
// Delegate method, called by NSNetService in case service publishing fails for whatever reason
- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict {
if ( sender != self.netService ) {
return;
}
// Stop socket server
[self terminateServer];
}
#end
For people looking for information about CFSocket server here's the answer: The code above is working fine if you change "SOCK_DGRAM" to "SOCK_STREAM".
Have you tried setting kCFSocketAcceptCallBack to something other than 0?
If you're interested in socket programming on Mac OS X or the iPhone, I suggest you look at this example from Apple's documentation.