I'm receiving a strange error querying a database with sqlite:
SQLlite error: near "SE": syntax error
I don't really get why he doesn't like the query string I'm sending to it. Here's my code:
-(Wallet*)loadDataFromSQL{
sqlite3 *database;
NSLog(#"opening..");
if (sqlite3_open([[NSString stringWithFormat:#"mywallet.sqlite3"] UTF8String], &database) == SQLITE_OK) {
NSLog(#"opened..");
const char *query = [[NSString stringWithFormat:#"SELECT * FROM 'Transaction';"] UTF8String]; // "insert into \"Transaction\" values (\"2013-01-01\",\"tipo\",\"cat\",1)";
sqlite3_stmt *selectstmt;
NSLog(#"preparing stmnt..");
if(sqlite3_prepare_v2(database, query, SQLITE_OPEN_READWRITE, &selectstmt, nil) == SQLITE_OK) {
NSLog(#"Prepared..");
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSLog(#"row..");
NSString *data = [NSString stringWithUTF8String: (char*)sqlite3_column_text(selectstmt, 0)];
NSString *type = [NSString stringWithUTF8String: (char*)sqlite3_column_text(selectstmt, 1)];
NSString *category = [NSString stringWithUTF8String: (char*)sqlite3_column_text(selectstmt, 2)];
float amount = (float)sqlite3_column_double(selectstmt, 0);
Transaction *t = [[Transaction alloc]init:data transactionType:type transactionCategory:category transactionAmount:[NSString stringWithFormat:#"%f",amount]];
NSLog(#"%#",t);
}
}else{
NSLog([NSString stringWithFormat:#"SQLlite error: %s\n", sqlite3_errmsg(database)]);
}
}
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
return nil;
}
Nothing too fancy.. what's wrong with it?
Thanks!
The third parameter of sqlite3_prepare_v2 must be the length of the query string, or just -1.
sqlite3_prepare_v2(database, query, SQLITE_OPEN_READWRITE, &selectstmt, nil)
With SQLite complaining about "SE", I'd guess that the value of SQLITE_OPEN_READWRITE is 2. :-)
Related
Every Mac has a model identifier, for example "Macmini5,1". (These are shown in the System Information app.)
How can I programatically obtain this model identifier string?
Swift 4+ using IOKit
import IOKit
func getModelIdentifier() -> String? {
let service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"))
var modelIdentifier: String?
if let modelData = IORegistryEntryCreateCFProperty(service, "model" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? Data {
modelIdentifier = String(data: modelData, encoding: .utf8)?.trimmingCharacters(in: .controlCharacters)
}
IOObjectRelease(service)
return modelIdentifier
}
You can use sysctl
#import <Foundation/Foundation.h>
#import <sys/sysctl.h>
NSString *ModelIdentifier()
{
NSString *result=#"Unknown Mac";
size_t len=0;
sysctlbyname("hw.model", NULL, &len, NULL, 0);
if (len) {
NSMutableData *data=[NSMutableData dataWithLength:len];
sysctlbyname("hw.model", [data mutableBytes], &len, NULL, 0);
result=[NSString stringWithUTF8String:[data bytes]];
}
return result;
}
You can also use IOKit.framework. I think it's best choice.
This simple code example shows how to read model identifier from I/O Kit registry to NSString:
- (NSString *)modelIdentifier {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef model = IORegistryEntryCreateCFProperty(service,
CFSTR("model"),
kCFAllocatorDefault,
0);
NSString *modelIdentifier = [[NSString alloc] initWithData:(__bridge NSData *)model
encoding:NSUTF8StringEncoding];
CFRelease(model);
IOObjectRelease(service);
return modelIdentifier;
}
Strings "IOPlatformExpertDevice" and "model" in code above is used to read model identifier from I/O Kit registry. ioreg command line tool is your friend, when you want to find information from I/O Kit registry. This image shows those strings in ioreg output:
I hope this helps to use IOKit.framework.
Answer from Ryan H is correct except improper conversion from null-terminated string to Swift String, giving result with \0 symbol in the end, which you may not expect, performing full match. This is corrected version:
static private func modelIdentifier() -> String? {
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
defer { IOObjectRelease(service) }
if let modelData = IORegistryEntryCreateCFProperty(service, "model" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? Data {
return modelData.withUnsafeBytes { (cString: UnsafePointer<UInt8>) -> String in
return String(cString: cString)
}
}
return nil
}
You can get the same output from the system_profiler command. It has an -xml option that you can use. NSTask can run the command for you and you can parse the result.
Sample code:
#import <Foundation/Foundation.h>
NSString *ModelIdentifier() {
NSPipe *pipe=[NSPipe pipe];
NSTask *task=[[NSTask alloc] init];
[task setLaunchPath:#"/usr/sbin/system_profiler"];
[task setArguments:#[#"-xml", #"SPHardwareDataType"]];
[task setStandardOutput:pipe];
[task launch];
NSData *outData=[[pipe fileHandleForReading] readDataToEndOfFile];
NSString *outString=[[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
return [outString propertyList][0][#"_items"][0][#"machine_model"];
}
CFStringRef model = IORegistryEntryCreateCFProperty(service,
CFSTR("model"),
kCFAllocatorDefault,
0); ? type is ok ?
I think code maybe like this:
CFSDataRef model = IORegistryEntryCreateCFProperty(service,
CFSTR("model"),
kCFAllocatorDefault,
0);
With iOS 16 apps that also build to Mac Catalyst any solution here that uses kIOMasterPortDefault (such as Ryan H's) will generate a build error:
'kIOMasterPortDefault' is unavailable in Mac Catalyst
Attempting to switch kIOMasterPortDefault to kIOMainPortDefault may also give a build error depending on which versions of Catalyst you are targetting – and I found trying to use #available to get around that didn't work either.
If you run into that situation then try the following, which is a reformulation of Parag Bafna's answer into Swift:
var modelIdentifier: String {
#if targetEnvironment(macCatalyst)
var size = 0
sysctlbyname("hw.model", nil, &size, nil, 0)
var modelIdentifier: [CChar] = Array(repeating: 0, count: size)
sysctlbyname("hw.model", &modelIdentifier, &size, nil, 0)
return String(cString: modelIdentifier)
#else
// Handle iOS
#endif
}
Swift version of Parag Bafna excellent answer
var deviceName: String {
var str = "Unknown Device"
var len = 0
sysctlbyname("hw.model", nil, &len, nil, 0)
if len > 0 {
var data = Data(count: len)
sysctlbyname("hw.model", &data, &len, nil, 0)
if let s = String(bytes: data, encoding: .utf8) {
str = s
}
}
return str
}
I have to communicate over bluetooth with a device, the device expects commands to be separated by carriage return + linefeed.
Connection is established using RFCOMMChannel.
Atm it seems that my code is not working since I am expecting a reply from the device, which it does when I send it commands using a simpel terminal program.
This code is run after the connection is established (this is definately working since I can log data coming in from the external device)
NSString *clockRequest = #"C\r\n";
void *clockRequestData = (__bridge void *)([clockRequest dataUsingEncoding:NSASCIIStringEncoding]);
NSLog(#"Data buffer to write: %#", clockRequestData);
[rfcommChannel writeAsync: clockRequestData length:100 refcon:NULL];
//writing data from rfcomm
- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel refcon:(void*)refcon status:(IOReturn)error {
NSLog(#"Macbook wrote to Timecube, status: %d", error);
}
The code for establishing a connection was taken and adjusted from
https://gist.github.com/crazycoder1999/3139668
thx in advance
Add category NSStringHexToBytes to Your project:
NSString+NSStringHexToBytes.h
#import <Foundation/Foundation.h>
#interface NSString (NSStringHexToBytes)
+ (NSData *)dataWithString:(NSString *)string;
#end
NSString+NSStringHexToBytes.m
#import "NSString+NSStringHexToBytes.h"
#implementation NSString (NSStringHexToBytes)
+ (NSData *)dataWithString:(NSString *)string
{
//string = [string stringByReplacingOccurrencesOfString:#"0x" withString:#""];
//NSCharacterSet *notAllowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:#"abcdefABCDEF1234567890"] invertedSet];
//string = [[string componentsSeparatedByCharactersInSet:notAllowedCharacters] componentsJoinedByString:#""];
const char *cString = [string cStringUsingEncoding:NSASCIIStringEncoding];
const char *idx = cString;
unsigned char result[[string length] / 2];
size_t count = 0;
for(count = 0; count < sizeof(result)/sizeof(result[0]); count++)
{
sscanf(idx, "%2hhx", &result[count]);
idx += 2 * sizeof(char);
}
return [[NSData alloc] initWithBytes:result length:sizeof(result)];
}
#end
In Your implementation file import NSString+NSStringHexToBytes.h and add method
-(void)sendMessage:(NSData *)data
{
[rfcommChannel writeSync:(void*)data.bytes length:data.length];
}
and then:
NSString* clockRequest = #"C\r\n";
NSData* data = [NSString dataWithString:clockRequest];
[rfcommChannel sendMessage:data];
I am working on an app that requires me to save audio recordings in the database and retrieve them later. However, while I'm somewhat aware of how to use sqlite, I'm not sure if I'm using the right method when it comes to saving audio recordings, as I have a hard time getting it to work. The current code I'm using to save recordings is listed below and I would appreciate any feedback I receive regarding my code.
self.renameString =[NSString stringWithFormat:#"%#/%#.caf",DOCUMENTS_FOLDER,self.soundName.text];
NSLog(#"%#",self.renameString);
self.soundFileURL2 = [NSURL fileURLWithPath:self.renameString];
NSLog(#"%#",self.soundFileURL2);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *renameError;
[fm moveItemAtURL:self.soundFileURL toURL:self.soundFileURL2 error:&renameError];
[self checkAndCreateDatabase];
self.soundName.text = #"";
BOOL checkQuery;
NSData *blob;
blob = self.voiceData;
sqlite3_stmt *statement;
const char *insert = [self.renameString UTF8String];
if(sqlite3_prepare_v2(soundDB, insert, -1, &statement, NULL)!= SQLITE_OK) {
checkQuery=NO;
}
else {
sqlite3_bind_blob(statement, 1, [blob bytes], [blob length], NULL);
if(SQLITE_DONE != sqlite3_step(statement)) {
checkQuery=NO;
}
else {
// NSLog(#"Query Working nicely");
checkQuery=YES;
sqlite3_reset(statement);
}
}
//}
sqlite3_finalize(statement);
sqlite3_close(soundDB);
I'm also aware that in order to retrieve the saved file, you have to declare a new NSData variable and assign the reading to that variable. Afterwards, you have to extract the NSData and put it in another variable itself. The problem is, while I'm aware of how to extract NSString and UIImage data, I'm not sure about the rules for extracting AVAudioRecorder data. Can someone please help me on this? Thanks.
So the OS X Keychain has three pieces of information:
ServiceName (the name of my app)
Username
Password
I obviously always know the ServiceName. Is there a way to find any saved Username(s) for that ServiceName? (Finding the password is easy once you know the Username.)
I would much prefer to use a nice Cocoa wrapper such as EMKeychain to do this. But EMKeychain requires the UserName to get any keychain item!
+ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceNameString withUsername:(NSString *)usernameString;
How are you expected to fully utilize saving credentials in the Keychain, if you need the Username to find the credentials? Is the best practice to save the Username in the .plist file or something?
SecKeychainFindGenericPassword only returns a single keychain item. To find all generic passwords for a specific service, you need to run a query on the keychain. There are several ways to do this, based on what version of OS X you target.
If you need to run on 10.5 or below, you'll need to use SecKeychainSearchCreateFromAttributes. It's a rather horrible API. Here is a rough cut of a method that returns a dictionary mapping usernames to passwords.
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
OSStatus status;
// Construct a query.
const char *utf8Service = [service UTF8String];
SecKeychainAttribute attr = { .tag = kSecServiceItemAttr,
.length = strlen(utf8Service),
.data = (void *)utf8Service };
SecKeychainAttribute attrList = { .count = 1, .attr = &attr };
SecKeychainSearchRef *search = NULL;
status = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attrList, &search);
if (status) {
report(status);
return nil;
}
// Enumerate results.
NSMutableDictionary *result = [NSMutableDictionary dictionary];
while (1) {
SecKeychainItemRef item = NULL;
status = SecKeychainSearchCopyNext(search, &item);
if (status)
break;
// Find 'account' attribute and password value.
UInt32 tag = kSecAccountItemAttr;
UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
SecKeychainAttributeInfo info = { .count = 1, .tag = &tag, .format = &format };
SecKeychainAttributeList *attrList = NULL;
UInt32 length = 0;
void *data = NULL;
status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attrList, &length, &data);
if (status) {
CFRelease(item);
continue;
}
NSAssert(attrList->count == 1 && attrList->attr[0].tag == kSecAccountItemAttr, #"SecKeychainItemCopyAttributesAndData is messing with us");
NSString *account = [[[NSString alloc] initWithBytes:attrList->attr[0].data length:attrList->attr[0].length encoding:NSUTF8StringEncoding] autorelease];
NSString *password = [[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] autorelease];
[result setObject:password forKey:account];
SecKeychainItemFreeAttributesAndData(attrList, data);
CFRelease(item);
}
CFRelease(search);
return result;
}
For 10.6 and later, you can use the somewhat less inconvenient SecItemCopyMatching API:
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
kSecClassGenericPassword, kSecClass,
(id)kCFBooleanTrue, kSecReturnData,
(id)kCFBooleanTrue, kSecReturnAttributes,
kSecMatchLimitAll, kSecMatchLimit,
service, kSecAttrService,
nil];
NSArray *itemDicts = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)q, (CFTypeRef *)&itemDicts);
if (status) {
report(status);
return nil;
}
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSDictionary *itemDict in itemDicts) {
NSData *data = [itemDict objectForKey:kSecValueData];
NSString *password = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
NSString *account = [itemDict objectForKey:kSecAttrAccount];
[result setObject:password forKey:account];
}
[itemDicts release];
return result;
}
For 10.7 or later, you can use my wonderful LKKeychain framework (PLUG!). It doesn't support building attribute-based queries, but you can simply list all passwords and filter out the ones you don't need.
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
LKKCKeychain *keychain = [LKKCKeychain defaultKeychain];
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (LKKCGenericPassword *item in [keychain genericPasswords]) {
if ([service isEqualToString:item.service]) {
[result setObject:item.password forKey:item.account];
}
}
return result;
}
(I didn't try running, or even compiling any of the above code samples; sorry for any typos.)
You don't need the username. You do with EMKeychain, but that's an artificial distinction that that class imposes; the underlying Keychain Services function does not require a username to find a keychain item.
When using SecKeychainFindGenericPassword directly, pass 0 and NULL for the username parameters. It will return a keychain item that exists on that service.
However, that will return only one item. If the user has multiple keychain items on the same service, you won't know that, or which one you got (the documentation says it returns the “first” matching item, with no specification of what it considers “first”). If you want any and all items for that service, you should create a search and use that.
Generic passwords have a unique key of the service name and the username. Thus, to fetch a single generic keychain entry, you will need to provide both. However, you can iterate over all generic keychain entries for your given service using the SecKeychainFindGenericPassword function.
(Disclaimer: I don't know anything about doing this in EMKeychain.)
I keep getting Clang errors on the following type of code and I can't figure out why they're erroneous or how to resolve them to Clang's satisfaction:
+ (NSString *)checkForLength: (NSString *)theString error: (NSError **)error {
BOOL hasLength = ([theString length] > 0);
if (hasLength) return theString;
else {
*error = [NSError errorWithDomain:#"ErrorDomain" code:hasLength userInfo:nil];
return nil;
}
}
Leaving aside the utterly-contrived nature of the example (which Clang did object to so it's illustrative enough), Clang balks at the error assignment line with the following objection:
Potential null dereference. According to coding standards in 'Creating and Returning NSError Objects' the parameter 'error' may be null.
I like having a pristine Clang report. I've read the cited document and I can't see a way to do what's expected; I checked some open-source Cocoa libraries and this seems to be a common idiom. Any ideas?
The way to do what's expected is shown in listing 3-5 in that document. With your example code:
+ (NSString *)checkForLength: (NSString *)theString error: (NSError **)error {
BOOL hasLength = ([theString length] > 0);
if (hasLength) return theString;
else {
if (error != NULL) *error = [NSError errorWithDomain:#"ErrorDomain" code:hasLength userInfo:nil];
return nil;
}
}
The Cocoa convention is that the return value should indicate success or failure (in this case, you return nil for failure) and the error is filled in with additional information, but only when the caller requests it.
In other words
NSError *error = nil;
NSString *result = [self checkForLength: aString error: &error];
and
NSString *result = [self checkForLength: aString error: NULL];
are both valid ways to invoke the method. So the method body should always check for a NULL error param:
if (error != NULL)
*error = ...;