I imported Alamofire the way I used to successfully import it, and when I had this code, there comes an error:
Alamofire.request(.GET, postEndPoint).responseJSON {(request, response, result) in
//Error: '(_, _, _) -> Void' is not convertible to 'Response<AnyObject, NSError> -> Void'
guard let value = result.value else {
print("Error: did not receive data")
return
}
guard result.error == nil else {
print("error calling GET on /posts/1")
print(result.error)
return
}
let post = JSON(value)
print("The post is: " + post.description)
if let title = post["title"].String {
print("The title is: " + title)
} else {
print("Error parsing /posts/1")
}
}
I didn't use CocoaPods for Alamofire.
See the Alamofire 3.0 Migration Guide.
Alamofire.request(.GET, postEndPoint).responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
This is the new way of getting the JSON.
response.result is the new way of getting result.
Alamofire.request(.GET, postEndPoint).responseJSON { response in
guard let value = response.result.value else {
print("Error: did not receive data")
return
}
guard response.result.error == nil else {
print("error calling GET on /posts/1")
print(response.result.error)
return
}
let post = JSON(value)
print("The post is: " + post.description)
if let title = post["title"].String {
print("The title is: " + title)
} else {
print("Error parsing /posts/1")
}
}
Related
I have a set up, using Ashley Mills Reachability, which is supposed to send a notification, using NotificationCenter, to the application when the connection of the app changes. It is set up as follows:
func reachabilityChanged(note: Notification) {
let reachability = note.object as! Reachability
switch reachability.connection {
case .wifi:
print("Reachable via WiFi")
case .cellular:
print("Reachable via Cellular")
case .none:
print("Network not reachable")
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.reachabilityChanged), name: .reachabilityChanged, object: reachability)
do{
try reachability.startNotifier()
}catch{
print("could not start reachability notifier")
}
}
With my computer (which is what I am running the simulator on) connected to wifi, the console accurately prints "Reachable via Wifi". Turning off the wifi causes the console to, again, accurately print "Network not reachable". I run into an issue, though, when I turn the wifi back on. "Reachable via Wifi" does not get printed to the log... in fact, nothing gets printed to the log. I do not know why this is happening nor how to fix it. Anyone have any ideas?
This is the reachability code:
import SystemConfiguration
import Foundation
public enum ReachabilityError: Error {
case FailedToCreateWithAddress(sockaddr_in)
case FailedToCreateWithHostname(String)
case UnableToSetCallback
case UnableToSetDispatchQueue
}
#available(*, unavailable, renamed: "Notification.Name.reachabilityChanged")
public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification")
extension Notification.Name {
public static let reachabilityChanged = Notification.Name("reachabilityChanged")
}
func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
guard let info = info else { return }
let reachability = Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue()
reachability.reachabilityChanged()
}
public class Reachability {
public typealias NetworkReachable = (Reachability) -> ()
public typealias NetworkUnreachable = (Reachability) -> ()
#available(*, unavailable, renamed: "Conection")
public enum NetworkStatus: CustomStringConvertible {
case notReachable, reachableViaWiFi, reachableViaWWAN
public var description: String {
switch self {
case .reachableViaWWAN: return "Cellular"
case .reachableViaWiFi: return "WiFi"
case .notReachable: return "No Connection"
}
}
}
public enum Connection: CustomStringConvertible {
case none, wifi, cellular
public var description: String {
switch self {
case .cellular: return "Cellular"
case .wifi: return "WiFi"
case .none: return "No Connection"
}
}
}
public var whenReachable: NetworkReachable?
public var whenUnreachable: NetworkUnreachable?
#available(*, deprecated: 4.0, renamed: "allowsCellularConnection")
public let reachableOnWWAN: Bool = true
/// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`)
public var allowsCellularConnection: Bool
// The notification center on which "reachability changed" events are being posted
public var notificationCenter: NotificationCenter = NotificationCenter.default
#available(*, deprecated: 4.0, renamed: "connection.description")
public var currentReachabilityString: String {
return "\(connection)"
}
#available(*, unavailable, renamed: "connection")
public var currentReachabilityStatus: Connection {
return connection
}
public var connection: Connection {
guard isReachableFlagSet else { return .none }
// If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi
guard isRunningOnDevice else { return .wifi }
var connection = Connection.none
if !isConnectionRequiredFlagSet {
connection = .wifi
}
if isConnectionOnTrafficOrDemandFlagSet {
if !isInterventionRequiredFlagSet {
connection = .wifi
}
}
if isOnWWANFlagSet {
if !allowsCellularConnection {
connection = .none
} else {
connection = .cellular
}
}
return connection
}
fileprivate var previousFlags: SCNetworkReachabilityFlags?
fileprivate var isRunningOnDevice: Bool = {
#if (arch(i386) || arch(x86_64)) && os(iOS)
return false
#else
return true
#endif
}()
fileprivate var notifierRunning = false
fileprivate let reachabilityRef: SCNetworkReachability
fileprivate let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability")
required public init(reachabilityRef: SCNetworkReachability) {
allowsCellularConnection = true
self.reachabilityRef = reachabilityRef
}
public convenience init?(hostname: String) {
guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { return nil }
self.init(reachabilityRef: ref)
}
public convenience init?() {
var zeroAddress = sockaddr()
zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size)
zeroAddress.sa_family = sa_family_t(AF_INET)
guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { return nil }
self.init(reachabilityRef: ref)
}
deinit {
stopNotifier()
}
}
public extension Reachability {
// MARK: - *** Notifier methods ***
func startNotifier() throws {
guard !notifierRunning else { return }
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = UnsafeMutableRawPointer(Unmanaged<Reachability>.passUnretained(self).toOpaque())
if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) {
stopNotifier()
throw ReachabilityError.UnableToSetCallback
}
if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) {
stopNotifier()
throw ReachabilityError.UnableToSetDispatchQueue
}
// Perform an initial check
reachabilitySerialQueue.async {
self.reachabilityChanged()
}
notifierRunning = true
}
func stopNotifier() {
defer { notifierRunning = false }
SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
}
// MARK: - *** Connection test methods ***
#available(*, deprecated: 4.0, message: "Please use `connection != .none`")
var isReachable: Bool {
guard isReachableFlagSet else { return false }
if isConnectionRequiredAndTransientFlagSet {
return false
}
if isRunningOnDevice {
if isOnWWANFlagSet && !reachableOnWWAN {
// We don't want to connect when on cellular connection
return false
}
}
return true
}
#available(*, deprecated: 4.0, message: "Please use `connection == .cellular`")
var isReachableViaWWAN: Bool {
// Check we're not on the simulator, we're REACHABLE and check we're on WWAN
return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet
}
#available(*, deprecated: 4.0, message: "Please use `connection == .wifi`")
var isReachableViaWiFi: Bool {
// Check we're reachable
guard isReachableFlagSet else { return false }
// If reachable we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi
guard isRunningOnDevice else { return true }
// Check we're NOT on WWAN
return !isOnWWANFlagSet
}
var description: String {
let W = isRunningOnDevice ? (isOnWWANFlagSet ? "W" : "-") : "X"
let R = isReachableFlagSet ? "R" : "-"
let c = isConnectionRequiredFlagSet ? "c" : "-"
let t = isTransientConnectionFlagSet ? "t" : "-"
let i = isInterventionRequiredFlagSet ? "i" : "-"
let C = isConnectionOnTrafficFlagSet ? "C" : "-"
let D = isConnectionOnDemandFlagSet ? "D" : "-"
let l = isLocalAddressFlagSet ? "l" : "-"
let d = isDirectFlagSet ? "d" : "-"
return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
}
}
fileprivate extension Reachability {
func reachabilityChanged() {
guard previousFlags != flags else { return }
let block = connection != .none ? whenReachable : whenUnreachable
DispatchQueue.main.async {
block?(self)
self.notificationCenter.post(name: .reachabilityChanged, object:self)
}
previousFlags = flags
}
var isOnWWANFlagSet: Bool {
#if os(iOS)
return flags.contains(.isWWAN)
#else
return false
#endif
}
var isReachableFlagSet: Bool {
return flags.contains(.reachable)
}
var isConnectionRequiredFlagSet: Bool {
return flags.contains(.connectionRequired)
}
var isInterventionRequiredFlagSet: Bool {
return flags.contains(.interventionRequired)
}
var isConnectionOnTrafficFlagSet: Bool {
return flags.contains(.connectionOnTraffic)
}
var isConnectionOnDemandFlagSet: Bool {
return flags.contains(.connectionOnDemand)
}
var isConnectionOnTrafficOrDemandFlagSet: Bool {
return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty
}
var isTransientConnectionFlagSet: Bool {
return flags.contains(.transientConnection)
}
var isLocalAddressFlagSet: Bool {
return flags.contains(.isLocalAddress)
}
var isDirectFlagSet: Bool {
return flags.contains(.isDirect)
}
var isConnectionRequiredAndTransientFlagSet: Bool {
return flags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]
}
var flags: SCNetworkReachabilityFlags {
var flags = SCNetworkReachabilityFlags()
if SCNetworkReachabilityGetFlags(reachabilityRef, &flags) {
return flags
} else {
return SCNetworkReachabilityFlags()
}
}
}
it get's triggered only for one time per run in the simulator, but on a real iOS device, it works normally.
Sample code:
if let result = Locksmith.loadDataForUserAccount("UserAccount") {
do {
account = Account(
id: result["id"] as! String,
uid: result["uid"] as! String,
username: result["username"] as! String,
name: result["name"] as! String,
image: result["image"] as! String,
token: result["token"] as! String,
client: result["client"] as! String
)
} catch _ {
// Xcode show this warning:
// 'catch' block is unreachable because no errors are thrown in 'do' block
}
}
Any of the result's keys can be nil.
Is there a way to just catch the error in case any of them is nil?
You can't do that because the force unwrap of nil value does generate a fatal_error (which is not like throwing an ErrorType).
Failable Initializer
But you can solve your problem simply adding a failable initializer to Account (it's easier if it's a struct).
struct Account {
let id: String
let uid: String
let username: String
let name: String
let image: String
let token: String
let client: String
init?(dict:[String:Any]) {
guard let
id = dict["id"] as? String,
uid = dict["uid"] as? String,
username = dict["username"] as? String,
name = dict["name"] as? String,
image = dict["image"] as? String,
token = dict["token"] as? String,
client = dict["client"] as? String else { return nil }
self.id = id
self.uid = uid
self.username = username
self.name = name
self.image = image
self.token = token
self.client = client
}
}
Now
if let
result = Locksmith.loadDataForUserAccount("UserAccount"),
account = Account(dict:result) {
// use account here
}
Throwing Initializer
If knowing that some field is missing is not enough for you, and you need to know which is the first missing field that stopped the initialisation of Account you can define a throwing initializer.
First of all you need a way to represent the error
enum AppError: ErrorType {
case MissingField(String)
}
Next
struct Account {
let id: String
let uid: String
let username: String
let name: String
let image: String
let token: String
let client: String
init(dict:[String:Any]) throws {
guard let id = dict["id"] as? String else { throw AppError.MissingField("id") }
guard let uid = dict["uid"] as? String else { throw AppError.MissingField("uid") }
guard let username = dict["username"] as? String else { throw AppError.MissingField("username") }
guard let name = dict["name"] as? String else { throw AppError.MissingField("name") }
guard let image = dict["image"] as? String else { throw AppError.MissingField("image") }
guard let token = dict["token"] as? String else { throw AppError.MissingField("token") }
guard let client = dict["client"] as? String else { throw AppError.MissingField("client") }
self.id = id
self.uid = uid
self.username = username
self.name = name
self.image = image
self.token = token
self.client = client
}
}
Let's see how it does work
do {
let dict:[String:Any] = ["id": "1", "uid": "1234"]
let account = try Account(dict: dict)
} catch let appError {
print(appError)
}
Output
MissingField("username")
In the new Contacts framework there appears to be a way to search by name:
let predicate = CNContact.predicateForContactsMatchingName("john")
let toFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
do {
let contacts = try store.unifiedContactsMatchingPredicate(
predicate, keysToFetch: toFetch)
for contact in contacts{
print(contact.givenName)
print(contact.familyName)
print(contact.identifier)
}
}
catch let err {
print(err)
}
But no apparent way to search by e-mail that I can find in the documentation or searches.
How do I search contacts by e-mail address?
These articles here and here have been helpful in learning the new framework but neither of these have revealed how to search by e-mail.
Having the same problem, I managed to find a solution. I have solved this by fetching all the contacts and then iterating over them to find matching contacts.
The code can surely be refactored.
import UIKit
import Contacts
public class ContactFinder: NSObject {
private lazy var store = CNContactStore()
private var allContacts: [CNContact] = []
private let keysToFetch: [CNKeyDescriptor] = [CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey, CNContactBirthdayKey, CNContactImageDataKey, CNContactImageDataAvailableKey]
public func requestAccess(completion:((success: Bool, error: NSError?)->())) {
let status = CNContactStore.authorizationStatusForEntityType(.Contacts)
if status == .NotDetermined {
store.requestAccessForEntityType(.Contacts, completionHandler: { [weak self](success, error) -> Void in
if success {
self?.getAllContacts(completion)
} else {
completion(success: false, error: error)
}
})
} else if status == .Authorized {
getAllContacts(completion)
} else {
completion(success: false, error: nil)
}
}
public func searchForContactUsingEmail(email: String, completion:((contacts: [CNContact])->())) {
var contacts: [CNContact] = []
for contact in allContacts {
let em = contact.emailAddresses
if em.count > 0 {
let results = em.filter({ val in
let ce = (val.value as! String).trimmedString.lowercaseString
return ce == email.trimmedString.lowercaseString
})
if results.count > 0 {
contacts.append(contact)
}
}
}
completion(contacts: contacts)
}
private func getAllContacts(completion:((success: Bool, error: NSError?)->())) {
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
request.predicate = nil
request.unifyResults = true
do {
try store.enumerateContactsWithFetchRequest(request, usingBlock: { [weak self](contact, _) -> Void in
self?.allContacts.append(contact)
})
} catch let e as NSError {
print(e.localizedDescription)
completion(success: false, error: e)
return
}
completion(success: true, error: nil)
}
}
I'm designing an iOS App using Swift. I have a View Controller, DisplayBrand that's displaying data fetched from a backend. I have made another class, FetchAndParseBrand, for fetching and parsing data from the backend.
The view controller is calling a method, getBrand, in the fetching class. This method then revieces data asynchronously from the backend.
How do I return the received data to the View Controller? I guess there are multiple ways of solving this problem, do you have any suggestion for the best practice?
Here is some code for context:
DisplayBrand
class DisplayBrand: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var brand = Brand()
FetchAndParseBrand().getBrand()
self.updateUI()
}
...
}
FetchAndParseBrand:
class FetchAndParseBrand: NSObject {
// Fetches one brand on static url
func getBrand() -> Brand{
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.valueForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.valueForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.valueForKey("Id") as? Int {
brand.id = newId
}
brand.printBrand()
}
}
}
}
}
return brand
}
}
Brand
class Brand: NSObject {
var id: Int?
var name: String?
var image: UIImage?
var gender: String?
func printBrand() {
if id != nil { println("ID:\t\t\(self.id!)") }
if name != nil { println("Name:\t\(self.name!)") }
if image != nil { println("Image:\t\(self.image?.description)") }
if gender != nil { println("Gender:\t\(self.gender!)") }
}
}
You can use a completion handler for this, this will return the variable when it is read:
class FetchAndParseBrand: NSObject {
class func getBrand(completion: (brand: Brand) -> ()) {
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.valueForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.valueForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.valueForKey("Id") as? Int {
brand.id = newId
}
completion(brand: brand)
brand.printBrand()
}
}
}
}
}
}
}
Then you can call the function like so:
FetchAndParseBrand.getBrand { (brand) -> () in
println(brand)
}
there are many ways. with callback function and delegates. you can also use singleton if you will call back only to a specific view controller:
class DisplayBrand: UIViewController {
static var sharedInstance :DisplayBrand?
override func viewDidLoad() {
super.viewDidLoad()
DisplayBrand.sharedInstance = self;
}
.
.
.
//on async over:
displayBrand.sharedInstance?.someFunction();
There is a major issue in your code:
FetchAndParseBrand.getBrand() is supposed to be a class method and has no parameter
The easiest way to get a asynchronous callback is a closure
Try this
Class DisplayBrand
class DisplayBrand: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
FetchAndParseBrand.getBrand() { (brand) -> () in
// do something with variable brand
self.updateUI() }
}
...
}
Class FetchAndParseBrand
class FetchAndParseBrand: NSObject {
// Fetches one brand on static url
class func getBrand(completion: (Brand)->()) {
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.objectForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.objectForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.objectForKey("Id") as? Int {
brand.id = newId
}
brand.printBrand()
completion(brand)
}
}
}
}
}
}
}
Side note: valueForKey is a key value coding method. The standard method to get a value from a dictionary by key is objectForKey or use subscripting
I've issue with Swift when using Parse's signup in background with block
func Signup()
{
var user = PFUser()
user.username = self.username.text
user.password = self.password.text
user.email = "email#example.com"
user.signUpInBackgroundWithBlock {
(succeeded: Bool! , error:NSError!) -> Void in
if (error != nil)
{
}
else
{
let errorString = error.userInfo["error"] as? NSString
}
}
}
then I got these two errors
http://s15.postimg.org/geexdxwob/image.png
func Signup()
{
var user = PFUser()
user.username = self.username.text
user.password = self.password.text
user.email = "email#example.com"
user.signUpInBackgroundWithBlock {
(succeeded: Bool , error:NSError!) -> Void in
if (error != nil)
{
}
else
{
let errorString = error.userInfo["error"] as? NSString
}
}
}
You can't set the userInfo like that. You should use the error.code instead.
Check what the error-codes represent.
Also the other error should disappear, because I've tested it, and it works. So just remove the line
let errorString = error.userInfo["error"] as? NSString
`And work with error.code instead and with switch-cases where you handle the different error-codes and build a errorString by yourself.
user.signUpInBackgroundWithBlock {
(succeeded: Bool! , error:NSError!) -> Void in
if (error != nil)
{
var errorString:String!
switch error.code{
case 100:
errorString = "Error 100 appeared. It means..."
case 101:
errorString = "Error 101 appeared. It means..."
case 102:
errorString = "Error 102 appeared. It means..."
default:
break
}
}
else{
}
}