I have two lighbulb-type accessories enabled on the Accessory Simulator. When I try to read the serviceType of HMService, it returns something like:
0000003E-0000-1000-8000-0026BB765291 instead of HMServiceTypeLightbulb
for (int i = 0; i < [homeKitController.accessories count]; i++) {
HMAccessory *accessory = [homeKitController.accessories objectAtIndex:i];
NSArray *services = accessory.services;
for (int i = 0; i < [services count]; i++) {
HMService *service = [services objectAtIndex:i];
NSLog(#"%#", service.serviceType);// <-returns 0000003E-0000-1000-8000-0026BB765291
}
}
The exact code above was working during Xcode beta 1 (before Xcode 6 GM came out). It used to print out the type of the service as a NSString. Now it prints this odd value. Any ideas or thoughts are appreciated.
HMServiceTypeLightbulb is a string constant, and is defined as that hex string you provided. The HomeKit system of accessories/services/characteristics mimics how BLE works, probably to simplify the implementation of BLE HomeKit accessories. The long hex string is the same format as a BLE UUID. When looking for a specific service, just check string equality on the HMxxx constants provided by HomeKit.
Related
I need the user's device token as a hexadecimal string to send push notifications. I used to do this by using the deviceToken 'description', but this doesn't work anymore in iOS13. I found several solutions for this in Objective-C and Swift.
How can I convert my device token (NSData) into an NSString?
The problem however, is that deviceToken.bytes returns a Pointer in RubyMotion. And calling bytes[index] will give a different result compared to doing it in Objective-C. I've tried a lot of different things, but I can't seem to get it to work. The RubyMotion docs about Pointers are also really barebones, so that doesn't help. I found the following regarding the Pointers on SO:
Pointer handling with RubyMotion
It says that I have to do bytes + index instead of bytes[index]. I currently have the following code:
def application(application, didRegisterForRemoteNotificationsWithDeviceToken: device_token)
string = token_to_string(device_token)
end
def token_to_string(device_token)
data_length = device_token.length
if data_length == 0
nil
else
data_buffer = device_token.bytes
token = NSMutableString.stringWithCapacity(data_length * 2)
index = 0
begin
buffer = data_buffer + index
token.appendFormat('%02.2hhx', buffer)
index += 1
end while index < data_length
token.copy
end
end
It's not giving any errors, but the generated device token doesn't seem to be correct. Any advice would be greatly appreciated!
I've been trying to think of a pure RubyMotion solution, but I just don't understand 'Pointers' enough to get it to work. So I've gone a different way: extending the NSData class.
1) Create the folder vendor/NSData+Hex inside your project.
2) Create the file NSData+Hex.h inside the folder and put this inside:
#import <Foundation/Foundation.h>
#interface NSData (Hex)
#property (nonatomic, copy, readonly) NSString *hexString;
#end
3) Now create NSData+Hex.m in the same folder and put this in the file:
#import "NSData+Hex.h"
#implementation NSData (Hex)
- (NSString *) hexString
{
NSUInteger len = self.length;
if (len == 0) {
return nil;
}
const unsigned char *buffer = self.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(len * 2)];
for (int i = 0; i < len; ++i) {
[hexString appendFormat:#"%02x", buffer[i]];
}
return [hexString copy];
}
#end
4) Add this to your Rakefile:
app.vendor_project('vendor/NSData+Hex', :static)
5) Now you can simply call deviceToken.hexString within def application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken).
If anyone can think of a pure RubyMotion solution, feel free to answer. For now, it's at least working. :)
Hi I use the following to convert NSData to an NSString that can then be manipulated in ruby.
class NSData
def to_s
NSString.alloc.initWithBytes(bytes, length: length, encoding: NSUTF8StringEncoding)
end
end
I do the following:
token = deviceToken.description.gsub(/[<> ]/, '')
I was getting the users mail addresses from Libraray/Preferences/com.apple.mail.plist. They are not there any more in Lion :P Do I have to use scripting bridge? Any hints? Thanks
I would get them right out of Address Book. That should work regardless of what email app is being used.
// Find 'me' card in address book.
ABPerson* meCard = [[ABAddressBook sharedAddressBook] me];
if( meCard == nil ) {
NSLog( #"Could not find me!" );
return;
}
// Get my email addresses.
ABMultiValue* anEmailList = [meCard valueForProperty:kABEmailProperty];
if( anEmailList == nil ) {
NSLog( #"I have no email!" );
return;
}
// Output them.
for( NSUInteger index = 0; index < [anEmailList count]; index++ ) {
NSString* aLabel = [anEmailList labelAtIndex:index];
NSString* aValue = [anEmailList valueAtIndex:index];
NSLog( #"%#: %#", aLabel, aValue );
}
Mail in Lion stores the equivalent in ~/Library/Mail/V2/MailData/Accounts.plist. Note however that you are assuming that the user uses the Apple Mail program, unless that's what you really need you may want to have alternative methods for getting the address. You may for instance depending on how the system has been set up use the CSIdentity APIs, such as CSIdentityGetEmailAddress().
Apple script will get the job done.
I have an XCode project, with the XIB interface files built using Interface Builder. I'm building localized XIB files by using ibtool to extract strings, translating them, and using ibtool again to build localized XIB files.
However, doing this means I have to translate all items in the application menus, including those that are completely standard (File, Save, Open, Minimize, etc.). Is there a way to avoid that?
i've developed a solution to this problem.
https://www.corecode.io/index_opensource.html
look for "Translator", it will translate your MainMenu.strings file into a dozen languages with the standard Apple translations for the standard menu item strings.
if you find some strings or languages missing that Aapple has included in their base apps, please send a patch over.
So, apparently no way around this.
I have been looking for a similar solution for a while and I found this resource
http://www.bdunagan.com/2009/03/15/ibtool-localization-made-easy/
It quotes toward the end of the article:
ibtool will look through MainMenu.xib for every user-visible string and insert that string with an associated ObjectID into MainMenu.strings, essentially a dictionary of strings keyed by ObjectID. Even better, the tool sorts them by ObjectID, so versioned .strings files are nicely diff’able. I can easily see what strings are added, removed, or just changed. Let me repeat this because it’s so incredibly handy: .strings files diff well! Of course, these .strings files are unicode, so they are not grep’able. Below is an example of the output for a string:
Go ahead and take a look I really hope it helps you as much as it helped me!
Translator by https://github.com/core-code/MiscApps/blob/master/Translator/Translator/MainMenuTranslations.plist
is cool but if you do not want to deal with 30 MainMenu.string files in your build (I personally don't) - you can just add MainMenuTranslations.plist to your resources (230KB uncompressed is tiny) and do it on the fly like this:
- (void) processMenu: (NSString*) app {
NSDictionary* d = [self loadMenuTranslations: app];
NSMenu* mm = NSApplication.sharedApplication.mainMenu;
for (int i = 0; i < mm.numberOfItems; i++) {
NSMenuItem* mi = [mm itemAtIndex: i];
mi.title = [self translateMenu: mi.title withDictionary: d];
NSMenu* sm = [[mm itemAtIndex: i] submenu];
sm.title = [self translateMenu: sm.title withDictionary: d];
for (int j = 0; j < sm.numberOfItems; j++) {
NSMenuItem* mi = [sm itemAtIndex: j];
mi.title = [self translateMenu: mi.title withDictionary: d];
}
}
}
- (NSString*) translateMenu: (NSString*) key withDictionary: (NSDictionary*) dictionary {
for (NSString* lang in dictionary) {
NSDictionary* translation = dictionary[lang];
NSString* t = translation[key];
if (t != null) {
return t;
}
}
return key;
}
- (NSDictionary*) loadMenuTranslations: (NSString*) app {
NSArray* langs = [NSUserDefaults.standardUserDefaults objectForKey: #"AppleLanguages"];
NSURL* url = [NSBundle.mainBundle URLForResource:#"MainMenuTranslations.plist" withExtension: null];
NSMutableDictionary* r = NSMutableDictionary.new;
NSDictionary* translations = [NSDictionary dictionaryWithContentsOfURL: url];
for (NSString* lang in langs) {
NSString* locale = [NSString stringWithFormat:#"%#.lproj", lang];
NSDictionary* translation = translations[locale];
NSMutableDictionary* d = [NSMutableDictionary.alloc initWithCapacity: translations.count * 3 / 2];
for (NSString* k in translation) {
NSString* v = translation[k];
NSString* key = k;
if ([k indexOf: #"APPLICATIONNAME"] >= 0) {
key = [k stringByReplacingOccurrencesOfString: #"APPLICATIONNAME" withString: app];
}
if ([v indexOf: #"APPLICATIONNAME"] >= 0) {
v = [v stringByReplacingOccurrencesOfString: #"APPLICATIONNAME" withString: app];
}
d[key] = v;
}
if (d.count > 0) {
r[lang] = d;
}
}
return r;
}
just call it from
- (void) applicationDidFinishLaunching: (NSNotification*) n {
// ...
[self processMenu: #"<your app name>"];
}
I wish there is a UniversalTranslation.plist somewhere (which could be probably collected automatically via creative use of translate.google.com)
I have an NSMutableArray that I'm trying to store and access some structs. How do I do this? 'addObject' gives me an error saying "Incompatible type for argument 1 of addObject". Here is an example ('in' is a NSFileHandle, 'array' is the NSMutableArray):
//Write points
for(int i=0; i<5; i++) {
struct Point p;
buff = [in readDataOfLength:1];
[buff getBytes:&(p.x) length:sizeof(p.x)];
[array addObject:p];
}
//Read points
for(int i=0; i<5; i++) {
struct Point p = [array objectAtIndex:i];
NSLog(#"%i", p.x);
}
As mentioned, NSValue can wrap a plain struct using +value:withObjCType: or -initWithBytes:objCType::
// add:
[array addObject:[NSValue value:&p withObjCType:#encode(struct Point)]];
// extract:
struct Point p;
[[array objectAtIndex:i] getValue:&p];
See the Number and Value guide for more examples.
You are getting errors because NSMutableArray can only accept references to objects, so you should wrap your structs in a class:
#interface PointClass {
struct Point p;
}
#property (nonatomic, assign) struct Point p;
This way, you can pass in instances of PointClass.
Edit: As mentioned above and below, NSValue already provides this in a more generic way, so you should go with that.
You could use NSValue, or in some cases it might make sense to use a dictionary instead of a struct.
I have some cross platform DNS client code that I use for doing end to end SMTP and on windows I can find the current DNS server ip addresses by looking in the registry. On the Mac I can probably use the SystemConfiguration framework as mentioned in the first answer, however the exact method of doing so is not immediately obvious.
For instance SCDynamicStoreCopyDHCPInfo returns some of the dynamic DHCP related data but not the DNS server addresses.
I know its very late to answer this question but may be helpful for the others.
This Code will help out for this task ..
SCPreferencesRef prefsDNS = SCPreferencesCreate(NULL, CFSTR("DNSSETTING"), NULL);
CFArrayRef services = SCNetworkServiceCopyAll(prefsDNS);
long servicesCount = CFArrayGetCount(services);
for (long i = 0; i < servicesCount; i++) {
const SCNetworkServiceRef service = (const SCNetworkServiceRef)CFArrayGetValueAtIndex(services, i);
CFStringRef interfaceServiceID = SCNetworkServiceGetServiceID(service);
CFStringRef primaryservicepath = CFStringCreateWithFormat(NULL,NULL,CFSTR("State:/Network/Service/%#/DNS"),interfaceServiceID);
SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, CFSTR("DNSSETTING"), NULL, NULL);
CFPropertyListRef propList = SCDynamicStoreCopyValue(dynRef,primaryservicepath);
if (propList) {
CFDictionaryRef dict = (CFDictionaryRef)propList;
CFArrayRef addresses = (CFArrayRef)CFDictionaryGetValue(dict, CFSTR("ServerAddresses"));
long addressesCount = CFArrayGetCount(addresses);
for (long j = 0; j < addressesCount; j++) {
CFStringRef address = (CFStringRef)CFArrayGetValueAtIndex(addresses, j);
// Print address
CFShow(address);
}
CFRelease(propList);
}
CFRelease(dynRef);
CFRelease(primaryservicepath);
}
CFRelease(services);
CFRelease(prefsDNS);
I know it's been a long time since you needed this, but there is nothing worse than a old unsolved answer. You can't access them from "/etc/resolv.conf" because of permission issues. After much searching, and a little luck I discovered you can get it via res_ninit() function.
// Get native iOS System Resolvers
res_ninit(&_res);
res_state res = &_res;
for (int i = 0; i < res->nscount; i++) {
sa_family_t family = res->nsaddr_list[i].sin_family;
int port = ntohs(res->nsaddr_list[i].sin_port);
if (family == AF_INET) { // IPV4 address
char str[INET_ADDRSTRLEN]; // String representation of address
inet_ntop(AF_INET, & (res->nsaddr_list[i].sin_addr.s_addr), str, INET_ADDRSTRLEN);
} else if (family == AF_INET6) { // IPV6 address
char str[INET6_ADDRSTRLEN]; // String representation of address
inet_ntop(AF_INET6, &(res->nsaddr_list [i].sin_addr.s_addr), str, INET6_ADDRSTRLEN);
}
}
res_ndestroy(res);
You can use the SystemConfiguration framework. It's in C.
Update: apparently the rest of the web is harder to use than I thought. Search for the key "State:/Network/Service/ServiceID/DNS" where ServiceID is the ID of the service.
They are also available from
/etc/resolv.conf
You could read from /etc/resolv.conf.