ABRecordCopyValue not working when object doesn't exist - xcode

This line of code in Swift causes me problems when the address book has a contact with no last name.
I've tried to resolve it a number of ways to no avail. Is there some sort of try catch statement or error handling I can use? Or check if AnyObject is null (the return type of
ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue()).
I've tried using optional types but it doesn't seem to work since the app stops running the moment you select a contact with no last name - and the line of code below gets highlighted with the error Thread
1: EXC_BAD_ACCESS
let lName = ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as String

ABRecordCopyValue can actually return nil so you should unwrap it. Also, casting to String didn't work for me so I'm using NSString.
if let firstName = ABRecordCopyValue(abContact, kABPersonFirstNameProperty)?.takeRetainedValue() as? NSString {
println("FIRST NAME: \(firstName)")
}
else {
println("No Name")
}
Another thing you could try is instead of getting the first and lastName individually, you could also try to get the composed name.
if let fullName = ABRecordCopyCompositeName(abContact)?.takeRetainedValue() as String? {
println("Full Name: \(fullName)")
}
P.S: I've also tried all the answers from this question but despite of making perfect sense and working when debugging the app, they crashed when I deployed the archive directly into the phone.

Related

Eureka forms gives error EXC_BAD_INSTRUCTION INVOP

I have just started using Eureka Forms in my Swift 3 project.
I have created my first form with a TextRow that looks like this:
<<< TextRow() {
$0.tag = tagname
$0.title = title
$0.placeholder = placeholder
}
It displays correctly on the simulator, however when I click in the field I immediately get a crash:
EXC_BAD_INSTRUCTION (code =EXC_1386,INVOP,subcode=0x0)
On the guard line (from the Eureka framework):
private func displayValue(useFormatter: Bool) -> String? {
guard let v = row.value else { return nil }
It must be something simple, can anybody point me in the right direction please?
That error seems to say that the row.value is needed, so maybe you can add an initial value ("") in the init callback. It may be an old or unreleased commit. Please try the latest version or see if the version you used has that particular bug.

Converting AppleScript to SwiftAutomation

This AppleScript that I found here does what I want:
tell application "iTunes"
set matchtrack to tracks in playlist 1 whose persistent ID is "C70EA9CDC276CB6D"
if matchtrack is not {} then
return name of item 1 of matchtrack
else
return "no track found"
end if
end tell
That is, finds a track based on the persistent ID. I'm trying to get it to work in a MacOS Cocoa Swift application using the Swift Automation as an Apple Event Bridge. I can retrieve the value with:
let trackID = try iTunes.tracks[1].persistentID.get()
I've tried all sorts of statements. This one seems to show the most promise:
let trackRow = try iTunes.tracks[ITUItem.persistentID == "C70EA9CDC276CB6D"].get() as ITUItem
When I run it, I get the error:
Instance member 'persistentID' cannot be used on type 'ITUItem'
The framework is in beta at the best so it may be a bug. Any suggestions on what else to try? I'd ask the author, but I can't find a way to contact him.
Here is part of the sdef file that was generated by the app.
item n : an item
properties
class_ (type, r/o) : the class of the item
container (specifier, r/o) : the container of the item
id (integer, r/o) : the id of the item
index (integer, r/o) : The index of the item in internal application order.
name (text) : the name of the item
persistentID (text, r/o) : the id of the item as a hexadecimal string. This id does not change over time.
properties (record) : every property of the item
Track is a subclass of item.
Fix
When I first tried #matt's suggestion of ITUIts.persistentID, it wouldn't compile. I got the error:
Binary operator '==' cannot be applied to operands of type 'ITUItem' and 'String'
After some back and forth, I realized the problem was that I was missing an import:
import SwiftAutomation
I had it in originally and wasn't sure I needed it so I commented it out. A dozen other calls to it worked fine without it before I got to this one.
Use ITUIts and get rid of as ITUItem. So:
let trackRow = try itunes.tracks[ITUIts.persistentID == "C70EA9CDC276CB6D"].get()
// result will be something along these lines:
// [ITunes().sources.ID(66).libraryPlaylists.ID(93639).fileTracks.ID(95018)]
Herewith, a complete working test along with the results on my machine (of course your numbers will be different):
let itunes = ITunes()
let trackID = try itunes.tracks[1].persistentID.get() as String
print("track ID is", trackID)
// track ID is 689006177BB39343
let trackRows = try itunes.tracks[ITUIts.persistentID == trackID].get() as [ITUItem]
if let trackRow = trackRows.first {
print(trackRow)
// ITunes().sources.ID(66).libraryPlaylists.ID(93639).fileTracks.ID(95018)
}

Are breakpoints not working as they should in DartEditor?

I'm getting some unexpected behaviour in the most recent Dart editor (version 0.4.0_r18915).
I have this minimal command line app that was intended to either take a command line argument or not and print a hello -somenoe- message. The application works just fine. But the debuggins fails to stop at the breakpoints set inside each of the if statement bodies. (I wanted to look at the state of the application weather the options.arguments.isEmpty was true or false)
var person;
main(){
var options = new Options();
if(options.arguments.isEmpty){
person = "someone who forgot to pass a command-line argument";
} else {
person = options.arguments[0];
}
print("Hello, $person!");
}
Debugger will stop at breakpoints in other lines but not in:
person = "someone who forgot to pass a command-line argument";
or in:
person = options.arguments[0];
Yes, file a bug. My suspicion is that the debugger can only stop at what's called a "safepoint" and that the assignment of a constant to a variable doesn't create one. Adding some line above it, like
print("breakpoint");
should help if that's the case. But I've also seen other problems with breakpoints not firing.

Core Data - can't set empty string as default value for attribute

I have an entity in my datamodel with a string attribute that is currently optional, and I'd like to convert this to a required attribute with a default value of the empty string.
As others have discovered, leaving the default value blank in the Xcode Core Data data modeler results in validation errors (since the designer interprets this as NULL), but trying '', "", or #"" as the default value results in those literal characters being interpreted as the default, rather than the empty zero-length string, as desired.
I did find this thread on Google, however, apart from the solution being really ugly (model definition split between the .xcdatamodel and objc source), it also doesn't work for lightweight migrations because those migrations are done solely based on the .xcdatamodel files and the objc logic from your entity implementations isn't loaded.
Is there any way to achieve this in the data model designer?
This is a very interesting question. After some testing I don't think this is possible because of the way the text field in the data model is configured.
In principle, you could use the unicode empty-set character of \u2205 to represent a default empty string but the text field does not seem to accept any escapes so it converts any attempt to escape a unicode character code to the literal string of the code characters themselves e.g. entering '\u2205' ends up as the literal text '\u2205'.
In theory you could write a utility app to read in the graphically generated managed object model file and then programmatically set the attribute default to equal an empty string and then save the file back to disk. I say "in theory" because there is no documented way to way to save a managed object model file from code. You can read one and modify it in memory but not persist the changes.
Bit of an oversight, I think.
I don't think you have any choice but to set the default empty string pragmatically when the model first loads. That is simple to do but it's ugly and you'll have to remember you did (especially if you migrate versions) but I think right now that is the only choice.
Whip out your favorite XML editor (I just used Emacs) and dive down to the contents file inside the .xcdatamodel bundle inside the .xcdatamodeld bundle. Then just add a defaultValueString="" XML attribute to the <attribute>...</attribute> element inside the <entity>...</entity> brackets.
Here's an example:
<attribute name="email" attributeType="String" defaultValueString="" syncable="YES"/>
I can't speak to whether this survives migration since I haven't had to do that yet.
I resolved this by overriding the getter for my field - if it contains null, I return an empty string instead:
-(NSString *)unit {
if ([self primitiveValueForKey:#"unit"] == NULL) {
return #"";
} else {
return [self primitiveValueForKey:#"unit"];
}
}
So far it seems to be doing the trick, and I would imagine it wouldn't impact migrations (although I don't know enough about them to say for sure). I don't really care whether there's a null or an empty string in the db, after all - so long as I get "" instead of null when I ask for the field.
My approach to resolving this issue was to create an NSManagedObject subclass and handle the substitution of empty strings for NULL values in awakeFromInsert. I then set all entities as children of this subclass rather than children of NSManagedObject. The assumption here is that I want every string attribute within a given entity to be set to an empty string by default (it wouldn't work, or would at least require extra logic, if you wanted some to remain NULL within the same entity).
There's probably a more efficient way of doing this, but since it's only called upon entity creation, I don't think it is too much of a performance hit.
- (void)awakeFromInsert {
[super awakeFromInsert];
NSDictionary *allAttributes = [[self entity] attributesByName];
NSAttributeDescription *oneAttribute;
for (NSString *oneAttributeKey in allAttributes) {
oneAttribute = [allAttributes objectForKey:oneAttributeKey];
if ([oneAttribute attributeType] == NSStringAttributeType) {
if (![self valueForKey:[oneAttribute name]]) {
[self setValue:#"" forKey:[oneAttribute name]];
}
}
}
}
You can do it manually.
In your model class, override awakeFromInsert and set your strings to empty string
Swift:
override func awakeFromInsert()
{
super.awakeFromInsert()
self.stringProperty = ""
}
Objective-C
- (void) awakeFromInsert
{
[super awakeFromInsert];
self.stringProperty = #"";
}
A simpler solution based on Scott Marks answer to avoid syntax errors:
First, temporarily set the default value to be easy to find, something like here you are. Open with any text editor the contents file inside the .xcdatamodel bundle inside the .xcdatamodeld bundle. Then just do a search with replacing the string "here you are" with the "" in this file.
The migration took place without problems.
Here is the Swift solution based on David Ravetti's answer and edelaney05's comment. In addition, I added optionality check.
This solution works fine in my projects.
class ExampleEntity: NSManagedObject {
...
override func awakeFromInsert() {
super.awakeFromInsert()
for (key, attr) in self.entity.attributesByName {
if attr.attributeType == .stringAttributeType && !attr.isOptional {
if self.value(forKey: key) == nil {
self.setPrimitiveValue("", forKey: key)
}
}
}
}
...
}
Maybe I'm late with this answer, but I was Googling and found this forum.
The solution is very simple:
When you click on the xcdatamodelId (On the left screen)
Change the Entity View to Graph
Double Click on any Attribute you want and the menu will appear on the right.
All changes are easy.
Part 2
Part 3
This appears to have been fixed at some point. Using Xcode 13:
Null String, unchecked Default Value:
<attribute name="myAttributeName" optional="YES" attributeType="String"/>
Empty String, now shown in Xcode interface:
<attribute name="myAttributeName" defaultValueString="" optional="YES" attributeType="String"/>
Entering "" into the field seems wrong and produces """" in the XML:
<attribute name="myAttributeName" defaultValueString="""" optional="YES" attributeType="String"/>

NSTokenField Suggest but don't complete

I feel like this must be a common issue that I'm just struggling to figure out, but I couldn't find anyone else who asked the question so...
Basically I have an NSTokenField and when the user begins typing I make a SOAP request and get names that are similar to what they have entered. The issue is my suggestions don't necessarily match what they have typed. For example, I match email and last names, but a persons full name appears in the suggestion array. Since the letters don't match, NSTokenField changes what has already been typed to the first item in the array. Is there a way to turn off autocomplete and just have the suggestion box appear?
- (NSArray *)tokenField:(NSTokenField *)tokenField completionsForSubstring:(NSString *)substring indexOfToken:(NSInteger)tokenIndex indexOfSelectedItem:(NSInteger *)selectedIndex
{
*selectedIndex = -1;
return NSArray;
}
It turns out that I was assigning selectedIndex incorrectly but if you just set it to -1 then nothing is selected.
In swift the answer is:
if selectedIndex != nil {
selectedIndex.memory = -1
}

Resources