Contact without name causes app to crash on iPhone - swift

I am trying to retrieve contact names, here's how:
func getContactNames() {
let adbk : ABAddressBook? = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
let people = ABAddressBookCopyArrayOfAllPeople(adbk).takeRetainedValue() as [ABRecord]
for person in people {
contactList.append(ABRecordCopyCompositeName(person).takeRetainedValue() as String)
}
}
When all contacts do have names it works, although when there are some contacts without names, app crashes and I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
I tried using ? like this:
let contact2 = (ABRecordCopyCompositeName(person)?.takeRetainedValue() as? String)
if contact2 != nil {
contactList.append(contact2!)
}
Then I would always get nil.
Any ideas what I am doing wrong?

In my experience you have to do it step-by-step: first check if ABRecordCopyCompositeNameis not nil and then take it and convert to string.
if let tmpName = ABRecordCopyCompositeName(person) {
let contact2 = tmpName.takeRetainedValue() as String
contactList.append(contact2)
}

Related

Always got Unexpectedly found nil while unwrapping an Optional value inside of CallDirectoryHandler

I added target Call Directory Extension to app, made a group, put main app and extension there, share appname.xcdatamodeld with main app and extension and create CoreDataHandler class to access CoreData with group and appname.sqlite
Then I made interface in main app to add phone numbers and save them in Int64 format. It works fine - numbers saved. And I can get them inside main app like:
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "NumberList")
do {
let entries = try managedContext.fetch(fetchRequest)
for item in entries {
print(item.value(forKeyPath: "number") as! Int64)
}
}
catch {}
All works, numbers printed... BUT inside CallDirectoryHandler with absolutely same situation I always got Fatal error: Unexpectedly found nil while unwrapping an Optional value, when Call Directory Extension reloads.
What am I do wrong? And actually it all works fine BEFORE update to last Xcode and IOS version (13.4)
private func addOrRemoveIncrementalBlockingPhoneNumbers(to context: CXCallDirectoryExtensionContext, since date: Date) {
print(numbers.count) //<-- It shows numbers count properly!
for entity in self.numbers {
print(entity) //<-- It shows entity!
let number: Int64? = entity.value(forKeyPath: "number") as? Int64
print(entity.value(forKey: "number")!) //<-- But fall here with nil, but attribute exists in main app!
let isHidden: Bool? = (entity.value(forKeyPath: "is_hidden") as? Bool)!
if isHidden != nil { context.addBlockingEntry(withNextSequentialPhoneNumber: number!) } else { context.removeBlockingEntry(withPhoneNumber: number!) }
}
}

AutoreleasingUnsafeMutablePointer crashes the app

I receive some data from internet and need to guess the encoding if it's not provided, so I use this function stringEncoding(for:encodingOptions:convertedString:usedLossyConversion:), and it requires passing AutoreleasingUnsafeMutablePointer for receiving the converted string, I wrote code like this:
var str = "Hello, playground"
func decode(data: Data) -> String? {
var covertedString = NSString()
let stringPointer = AutoreleasingUnsafeMutablePointer<NSString?>(&covertedString)
guard NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: stringPointer, usedLossyConversion: nil) != 0 else {
return nil
}
return covertedString as String
}
let data = str.data(using: .utf8)!
decode(data: data)
While the covertedString I got out of the function call is correct, the app always crashes. Any idea why AutoreleasingUnsafeMutablePointer is make it crashes? I tried to not passing convertedString, then it's not crashing any more, so looks like it's the root case. Any idea why it's crashing?
I am using Xcode Version 10.1 (10B61), with Swift 4
In your particular case the problem is that you have created an NSString, but then taken a pointer to a NSString?, which is a different thing.
But that doesn't really matter here. You don't create AutoreleasingUnsafeMutablePointer directly (or generally any kind of UnsafePointer). They're not promised to be valid by the time you use them. Instead, you create them implicitly using &.
func decode(data: Data) -> String? {
var convertedString: NSString? = "" // <- Make sure to make this optional
guard NSString.stringEncoding(for: data,
encodingOptions: nil,
convertedString: &convertedString, // <- Use &
usedLossyConversion: nil) != 0
else {
return nil
}
return convertedString as String?
}

How to specify the type of text in UITextField

I'm trying to determine whether or not a user is entering a proper email address into a UITextField . I'm using this code but am getting the following error message.
func isValidEmail(testStr:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let range = testStr.rangeOfString(emailRegEx, options:.RegularExpressionSearch) // I'm getting the error message here.
let result = range != nil ? true : false
return result
}
#IBAction func logIn(sender: AnyObject) {
let validLogin = isValidEmail(testStr: field.text!)
if validLogin {
print("User entered valid input")
} else {
print("Invalid email address")
}
}
This is the error message I'm getting: "Value of type 'String' has no member 'rangeOfString'"
I think this is because I don't have RegExKitLite installed but I'm not 100% sure. Even so, I tried installing the kit but I couldn't figure out how. I downloaded the file but I can't figure out how to add it in Xcode.
If you are using Swift 3 or later, the lines:
let range = testStr.rangeOfString(emailRegEx, options:.RegularExpressionSearch)
let result = range != nil ? true : false
return result
needs to be:
let range = testStr.range(of: emailRegEx, options: [ .regularExpression ])
return range != nil
None of this needs a third party library.
You probably also want to make sure the whole string matches so you should add ^ to the start of the regular expression and add $ to the end.

Workaround for EKParcipant URL accessing crash?

Some of my users have been sent me logs identifying a EXC_BREAKPOINT (SIGTRAP) Error on this line of code. I've been been trying to make it safe but all of the properties of EKParticipant are non optional so comparing to nil just gives me a warning saying it will always be true. If something is nil here how should I handle it?
Error Line
let participantEmail : String? = participant.url.absoluteString.lowercased().replacingOccurrences(of: "mailto:", with: "")
Apple Error Description
Trace Trap [EXC_BREAKPOINT // SIGTRAP]
Similar to an Abnormal Exit, this exception is intended to give an
attached debugger the chance to interrupt the process at a specific
point in its execution. You can trigger this exception from your own
code using the __builtin_trap() function. If no debugger is attached,
the process is terminated and a crash report is generated. Lower-level
libraries (e.g, libdispatch) will trap the process upon encountering a
fatal error. Additional information about the error can be found in
the Additional Diagnostic Information section of the crash report, or
in the device's console. Swift code will terminate with this exception
type if an unexpected condition is encountered at runtime such as:
a non-optional type with a nil value
a failed forced type conversion Look at the Backtraces to determine where the unexpected condition was encountered. Additional
information may have also been logged to the device's console. You
should modify the code at the crashing location to gracefully handle
the runtime failure. For example, use Optional Binding instead of
force unwrapping an optional."
Full Method
/**
Parses participants for a given event.
Goes through the EKEvents attendees array to build Attendee objects used to model a participant.
- parameter event: The calendar event we'll be finding the participants for.
- returns: An array of Attendee objects with the participants name, email, required/optional status and whether they've accepted their invitation to the event.
*/
private static func parseParticipantsIn(event: EKEvent) -> [Attendee] {
var participants = [Attendee]()
if let attendees = event.attendees, event.attendees?.isEmpty == false {
for participant in attendees {
let participantName : String? = parse(EKParticipantName: participant)
let participantEmail : String? = participant.url.absoluteString.lowercased().replacingOccurrences(of: "mailto:", with: "")
let isRequiredParticipant : Bool = participant.participantRole == EKParticipantRole.required
let hasAccepted : Bool = participant.participantStatus == EKParticipantStatus.accepted
guard (participantName != nil && participantEmail != nil)
else
{
log.error("Participant could not be parsed")
continue
}
let attendee = Attendee(name: participantName!, email: participantEmail!, required: isRequiredParticipant, hasAccepted: hasAccepted)
participants.append(attendee)
}
}
return participants
}
This appears to be a problem with the EKParticipant.url property. Any attempted access of EKParticipant.url causes a crash if you have a participant with the following email field within it.
"Bill Gates" <billgates#google.com>
I'd guess the quotation marks end the String prematurely. It is fine when accessed from EKParticipant.description so I intend to parse it from there.
This is a ridiculous issue, and Deco pinpointed it exactly. I used a different approach to get around it though: Since I'm already working in a mixed code base (obj-c and Swift), I created a class method on one of my obj-c classes that takes an EKParticipant and returns its URL as a string. Then, in Swift, I call that class method to get the URL instead of directly accessing the property (and crashing). It's hacky, but better than crashing and saved me from parsing the description.
This is rather old question but yet I hit this issue myself. My solution is to fallback to ObjC in order to workaround it.
Just add this ObjC functions to swift bridging header file and you are good to use them in swift.
static inline BOOL
participantHasNonNilURL (EKParticipant* _Nonnull participant) {
return participant.URL != nil;
}
static inline NSURL* _Nullable
participantURL(EKParticipant* _Nonnull participant) {
if (participant.URL != nil) {
return participant.URL;
}else {
return nil;
}
}
Example of usage:
extension EKParticipant {
var optionalURL: URL? {
return participantURL(self)
}
var hasURL: Bool {
return participantHasNonNilURL(self)
}
}
This is still an issue on macOS 11.2... I have reported it to Apple. I encourage anyone else hitting this issue to do the same.
The only Swift-only workaround that worked for me is:
extension EKParticipant {
public var safeURL: URL? {
perform(#selector(getter: EKParticipant.url))?.takeUnretainedValue() as? NSURL? as? URL
}
}
Validations are added incorrectly, please check the below response about how the guard could be used.
if let attendees = event.attendees, event.attendees?.isEmpty == false {
for participant in attendees {
guard let participantName : String? = parse(EKParticipantName: participant) else{
log.error("error in participant name")
return
}
guard let participantEmail : String? = participant.url.absoluteString.lowercased().replacingOccurrences(of: "mailto:", with: "") else{
log.error("error in participant email")
return
}
let isRequiredParticipant : Bool = participant.participantRole == EKParticipantRole.required
let hasAccepted : Bool = participant.participantStatus == EKParticipantStatus.accepted
/* guard validation is not required here */
if (participantName != nil && participantEmail != nil){
let attendee = Attendee(name: participantName!, email: participantEmail!, required: isRequiredParticipant, hasAccepted: hasAccepted)
participants.append(attendee)
}
}
}
return participants

Retrieve Data in Parse

I have searched through a number of similar topics but have not found a solution as of yet. I am using Parse social and using the login files.
I get the following error:
"AnyObject?" is not convertible to 'String'
I am very new to Swift & Parse - I believe this is the correct method of retrieving data, so please correct me if I am wrong.
var userObjectID = PFUser.currentUser()!.objectId!
var query = PFQuery(className:"User")
query.getObjectInBackgroundWithId("\(userObjectID)") {
(userInfo: PFObject?, error: NSError?) -> Void in
if error == nil && userInfo != nil {
println(userInfo)
let userScore = userInfo["level"] as! String
} else {
println(error)
}
}
Below is the database on Parse
I think you need to unwrap the PFObject you receive:
let userScore = userInfo!["level"] as! String