2014-09-23 2 views
11

Ich bin relativ neu zu iOS-Entwicklung und swift. Aber bis zu diesem Punkt war ich immer in der Lage, mir etwas Forschung über Stackoverflow und verschiedene Dokumentationen und Tutorials zu leisten. Allerdings gibt es ein Problem, ich konnte noch keine Lösung finden.Swift - Überprüfen unmanaged Adressbuch Einzelwert-Eigenschaft für Null

Ich möchte einige Daten aus dem Adressbuch des Benutzers (z. B. die Einzelwerteigenschaft kABPersonFirstNameProperty) abrufen. Da die .takeRetainedValue()-Funktion einen Fehler auslöst, wenn dieser Kontakt keinen firstName-Wert im Adressbuch hat, muss ich sicherstellen, dass die ABRecordCopyValue()-Funktion einen Wert zurückgibt. Ich habe versucht, dies in einem Verschluss zu überprüfen:

let contactFirstName: String = { 
    if (ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty) != nil) { 
     return ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty).takeRetainedValue() as String 
    } else { 
     return "" 
    } 
}() 

contactReference eine Variable vom Typ ist ABRecordRef!

Wenn ein Adressbuchs Kontakt einen Vornamen Wert liefert, funktioniert alles einwandfrei. Aber wenn es keinen firstName gibt, stürzt die Anwendung durch die .takeRetainedValue() Funktion ab. Es scheint, dass die if-Anweisung nicht hilft, da der nicht verwaltete Rückgabewert der ABRecordCopyValue()-Funktion nicht null ist, obwohl es keinen firstName gibt.

Ich hoffe, ich konnte mein Problem klar machen. Es wäre toll, wenn mir jemand mit einer Gehirnwelle helfen könnte.

Antwort

32

Wenn ich die Werte im Zusammenhang mit verschiedenen Eigenschaften will, verwende ich die folgende Syntax:

let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String 
let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String 

Oder Sie können optional Bindung verwenden:

if let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String { 
    // use `first` here 
} 
if let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String { 
    // use `last` here 
} 

Wenn Sie wirklich ein Nicht-optional, wo fehlende Wert ist eine Zeichenfolge der Länge Null zurückkehren möchten, können Sie den ?? Operator verwenden:

let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String ?? "" 
let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String ?? "" 
+0

Ich mag deine zweite Version. Danke dafür! – Rob

+2

Nach ein paar Tagen war dies die Antwort, die ich suchte !! Vielen Dank für das Teilen! – kev

+1

Option 1 wirken Wunder – Dean

1

Vielleicht ist es mehr als nur die Beantwortung Ihrer Frage, aber so behandle ich das Adressbuch.

ich einen benutzerdefinierten Operator definiert habe:

infix operator >>> { associativity left } 
func >>> <T, V> (lhs: T, rhs: T -> V) -> V { 
    return rhs(lhs) 
} 

mehrere Aufrufe von Funktionen in einer besser lesbaren Art und Weise, zum Beispiel an der Kette ermöglicht:

funcA(funcB(param)) 

wird

param >>> funcB >>> funcA 

Dann benutze ich diese Funktion, um eine Unmanaged<T> in einen schnellen Typ zu konvertieren:

func extractUnmanaged<T, V>(value: Unmanaged<T>?) -> V? { 
    if let value = value { 
     var innerValue: T? = value.takeRetainedValue() 
     if let innerValue: T = innerValue { 
      return innerValue as? V 
     } 
    } 
    return .None 
} 

und ein Gegenstück mit CFArray arbeiten:

func extractUnmanaged(value: Unmanaged<CFArray>?) -> [AnyObject]? { 
    if let value = value { 
     var innerValue: CFArray? = value.takeRetainedValue() 
     if let innerValue: CFArray = innerValue { 
      return innerValue.__conversion() 
     } 
    } 
    return .None 
} 

und dies ist der Code, um das Adressbuch zu öffnen, rufen Sie alle Kontakte, und für jeden Vornamen und Organisation (im Simulator liest Vornamen immer einen Wert hat, während Abteilung nicht der Fall, so zum Testen ist es gut):

let addressBook: ABRecordRef? = ABAddressBookCreateWithOptions(nil, nil) >>> extractUnmanaged 
let results = ABAddressBookCopyArrayOfAllPeople(addressBook) >>> extractUnmanaged 
if let results = results { 
    for result in results { 
     let firstName: String? = (result, kABPersonFirstNameProperty) >>> ABRecordCopyValue >>> extractUnmanaged 
     let organization: String? = (result, kABPersonOrganizationProperty) >>> ABRecordCopyValue >>> extractUnmanaged 
     println("\(firstName) - \(organization)") 
    } 
} 

Beachten Sie, dass die println Anweisung des optionalen druckt, so dass Sie 0 in der Konsole angezeigt werdenstatt nur David. Natürlich dient dies nur der Demonstration.

Die Funktion, die auf Ihre Frage beantwortet extractUnmanaged ist, eine optionale nicht verwalteten nimmt, es auszupacken, den gehaltenen Wert als optional abruft, auszupacken es, und am Ende versucht, eine Guss in den Zieltyp, der String für die IS Vorname Eigenschaft. Type infertral kümmert sich darum, herauszufinden, was T und V sind: T ist der in Unmanaged eingeschlossene Typ, V ist der Rückgabetyp, der bekannt ist, weil er beim Deklarieren der Zielvariablen let firstName: String? = ... angegeben wird.

Ich nehme an, Sie bereits um die Überprüfung und fragt den Benutzer um Erlaubnis genommen haben auf das Adressbuch zuzugreifen.

+0

Danke Antonio für Ihre ausführliche Antwort! Es ist auch schön zu sehen, wie flexibel und wiederverwendbar Sie damit umgehen. Eine leicht modifizierte Version Ihrer 'extractUnmanaged()' Funktion (ohne Generika) löste mein Problem. – Rob

+0

Die nicht-generische Version war mein erster Entwurf, dann mussten verschiedene Typen konvertiert werden. Ich kam mit dieser generischen Lösung und allen anderen Dingen. Btw froh, dass es dir geholfen hat – Antonio

5

Ich habe es durch diese Funktion hier:

func rawValueFromABRecordRef<T>(recordRef: ABRecordRef, forProperty property: ABPropertyID) -> T? { 
    var returnObject: T? = nil 
    if let rawValue: Unmanaged<AnyObject>? = ABRecordCopyValue(recordRef, property) { 
     if let unwrappedValue: AnyObject = rawValue?.takeRetainedValue() { 
      println("Passed: \(property)") 
      returnObject = unwrappedValue as? T 
     } 
     else { 
      println("Failed: \(property)") 
     } 
    } 
    return returnObject 
} 

Sie es in Ihrer Eigenschaft wie folgt verwendet werden:

let contactFirstName: String = { 
    if let firstName: String = rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNameProperty) { 
     return firstName 
    } 
    else { 
     return "" 
    } 
}() 
+0

Warum unten abstimmen? Ich kann nicht korrigieren, wenn Sie nicht sagen, was los ist? – Logan

+0

Vielen Dank für Ihre Antwort. Schön, so viel Hilfe zu sehen! Übrigens habe ich nichts abgegeben. Was meinen Sie? – Rob

+0

@Rob - Ich wollte nicht sagen, dass du es warst, aber jemand hat meine Antwort abgelehnt. Ich würde gerne wissen, warum, denn das ist was ich benutze und es funktioniert für mich. – Logan