ErrorType protocol in weather app - error-handling

I am very new to Swift and trying to create weather app. I have protocol func weatherManagerFailedToLoadCityWithError(error: ErrorType). In weatherManager.swift have some delegate
} else if status == 404 {
// City not found
if self.delegate != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate?.weatherManagerFailerToLoadCityWithError(.InvalidResponse)
})
}
} else {
// Some other here?
if self.delegate != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate?.weatherManagerFailerToLoadCityWithError(.MissingData)
})
}
}
What should i do i weatherController.swift in this code block
func weatherManagerFailedToLoadCityWithError(error: ErrorType) {
}
Any suggestion?

You can do it like this:
private struct ErrorInformation {
static let Domain = "us.firmaName"
static let ServerNotFoundDomain = "\(ErrorInformation.Domain).notFound"
}
private extension NSError {
static func serverNotFound() -> NSError {
let userInfo = [
NSLocalizedDescriptionKey: NSLocalizedString("Server Not Found", comment: ""),
NSLocalizedFailureReasonErrorKey: NSLocalizedString("The Server you are asking for is not available.", comment: ""),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Please proof your URL bla-bla-bla.", comment: "")
]
return NSError(domain: ErrorInformation.ServerNotFoundDomain, code: 404, userInfo: userInfo)
}
And then call your function:
weatherManagerFailedToLoadCityWithError(error: NSError.serverNotFound())
If you want you can handle the Error in your function:
func weatherManagerFailedToLoadCityWithError(error: ErrorType) {
print("Error description: \(error.userInfo. NSLocalizedDescriptionKey)")
}
If you want more explanation, just post more of your code.

Related

how to fetch data from API and set to the Image

This is my Model And I want to fetch data of publisherBanner and set
to the View But I can not set the image in view
import Foundation
public struct Banner: Decodable {
public let publisherBanners: [PublisherBanner]
public init(publisherBanners: [PublisherBanner]) {
self.publisherBanners = publisherBanners
}
}
public struct PublisherBanner: Decodable, Hashable {
public var id = UUID()
// public let bannerFor: String
// public let imageName: String
public let url: String
public init(url: String) {
self.url = url
}
}
This is my ViewModel
class BannerVM: ObservableObject {
#Published var datas = [PublisherBanner]()
let url = "apiUrlExample"
init() {
getData(url: url)
}
func getData(url: String) {
guard let url = URL(string: "\(url)") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
do {
let results = try JSONDecoder().decode(Banner.self, from: data)
DispatchQueue.main.async {
self.datas = results.publisherBanners
}
}
catch {
print(error)
}
}
}.resume()
}
}
And this is My View where I want to set Image
struct BannerView: View {
#StateObject var bannerObject = BannerVM()
var body: some View{
ScrollView(.horizontal,showsIndicators: false){
HStack(spacing:15) {
ForEach(bannerObject.datas, id: \.id){ item in
AsyncImage(url: URL(string: "\(item.url)")) { image in
image
.resizable().padding(4)
.frame(width: 150, height: 215)
} placeholder: {
Image("logo_gray").resizable().padding(1)
.frame(width: 150, height: 215)
}
}
}
}
.padding(8)
}
}
please help me for fetch the Image of My API
I am trying to fetch but i failed many times and please help me. And
thank you in advance.
Please read the error message you get
typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "publisherBanners", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
It says that the value for key id in PublisherBanner is an Int, you have to declare
public struct PublisherBanner: Decodable, Hashable, Identifiable {
public let id: Int
public let url: URL
}
By the way you can decode the url directly to URL and the init method is for free.
And as PublisherBanner already conforms to Identifiable the code to load the image can be shortened to
ForEach(bannerObject.datas) { item in
AsyncImage(url: item.url) { image in
Another by the way is that String Interpolation in URL(string: "\(url)") is redundant because url is already a String. This is sufficient: URL(string: url)

How to update nested structure of a published variable using another api call

I have a published var called spotWallet in my BinanceStore class which conforms to observaleObject. I can create a publisher and subscribe to it. but there is property in my top level struct called balances which is an array of Balance struct. What I want to achieve is would like to update a property of Balance called price which comes from another network call. I cannot get that to work, in clean combine swiftui way.
class BinanceStore: ObservableObject, Identifiable {
#Published var spotWallet: SpotWallet?
#Published var loadingError: String = ""
#Published var showAlert: Bool = false
private var cancellableSet: Set<AnyCancellable> = []
private let binanceFetcher: BinanceFetchable
init(binanceFetcher: BinanceFetchable) {
self.binanceFetcher = binanceFetcher
}
func AccountDetailsOnSpot() async {
binanceFetcher.getAccountDetailsOnSpot()
.sink { (dataResponse) in
if dataResponse.error != nil {
self.createAlert(with: dataResponse.error!)
} else {
self.spotWallet = dataResponse.value!
}
}.store(in: &cancellableSet)
}
func createAlert( with error: NetworkError ) {
loadingError = error.localizedDescription
self.showAlert = true
}
}
struct SpotWallet: Codable, Hashable {
let makerCommission, takerCommission, buyerCommission, sellerCommission: Int
let canTrade, canWithdraw, canDeposit: Bool
let updateTime: Int
let accountType: String
let balances: [Balance]
let permissions: [String]
}
protocol BinanceFetchable {
func getAccountDetailsOnSpot() -> AnyPublisher<DataResponse<SpotWallet, NetworkError>, Never>
}
class BinanceFetcher {
var coinPublisher = PassthroughSubject<CoinBalance, Never>()
}
extension BinanceFetcher: BinanceFetchable {
func getAccountDetailsOnSpot() -> AnyPublisher<DataResponse<SpotWallet, NetworkError>, Never> {
let endpoint = "/api/v3/account"
let params = BinanceWrapper.shared.makeRequestReady(queries: nil)
let url = URL(string: "\(BinanceWrapper.BINANCE_BASE_URL)\(endpoint)")!
return AF.request(url, parameters: params, encoding: URLEncoding.default, headers: BinanceWrapper.BINANCE_HTTP_HEADERS)
.validate()
.publishDecodable(type: SpotWallet.self)
.map { response in
response.mapError { error in
return NetworkError.parsing(description: error.localizedDescription)
}
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}

RxAlamofire - how to get the response on error?

I need the response body when an error occurs in an RxAlamofire call. I've seen this hack but I wonder if there's a cleaner way.
Inspired by it, I created this RxAlamofire fork with a similar hack. With it, errors will usually be an instance of DataResponseError so you can do this:
RxAlamofire.data(method, url).subscribe(
onError: { error in
if let error = error as? DataResponseError<Data> {
// Get response body (in this case, convert it to a String)
if let data = error.response.data {
let message = String(data: data, encoding: String.Encoding.utf8)
print("Message: \(message)")
}
// Get status code
if let statusCode = error.response.response?.statusCode {
print("Status code: \(statusCode)")
}
}
}
)
Issue description. I'm using RxAlamofire to make network requests, and I needed to get information from the body of the error response.
I've made a hack in a folloing way:
Added a PError:
import UIKit
import Alamofire
import ObjectMapper
class PError: Error, Mappable {
var message: String?
var statusCode: Int?
required init?(map: Map) {
}
func mapping( map: Map) {
message <- map["error.message"]
statusCode <- map["error.statusCode"]
}
}
And now added such extensions to DataRequest:
import Alamofire
import ObjectMapper
extension DataRequest {
//Overriding several methods from Alamofire Validation
#discardableResult
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
return validate { [unowned self] _, response, bodyData in
return self.validate(statusCode: acceptableStatusCodes, response: response, bodyData: bodyData)
}
}
//Overriding several methods from Alamofire Validataion
fileprivate func validate<S: Sequence>(
statusCode acceptableStatusCodes: S,
response: HTTPURLResponse, bodyData: Data?)
-> ValidationResult
where S.Iterator.Element == Int
{
if acceptableStatusCodes.contains(response.statusCode) {
return .success
} else {
var error: Error = AFError.responseValidationFailed(reason: AFError.ResponseValidationFailureReason.unacceptableStatusCode(code: response.statusCode))
if let bodyData = bodyData {
if let jsonString = String(data: bodyData, encoding: .utf8) {
if let errorNew = Mapper<PError>().map(JSONString: jsonString)
{
error = errorNew
}
}
}
return .failure(error)
}
}
}
Next, somewhere in the code you'll be able to work with this custom error object:
if let err = error as? PError {
status = err.message ?? "No message in the error description"
}
else

Load PageViewController After Asynchronous Task Complete

I'm having trouble loading the PageViewController after the async call is complete. I was considering using NSNotification, but not sure what is the best approach.
Async func to fetch images
func fetchArrayImages() {
var query = PFQuery(className: "FoodPhoto")
query.orderByDescending("Votes")
query.findObjectsInBackgroundWithBlock ({(objects:[AnyObject]!, error: NSError!) in
if(error == nil){
let imageObjects = objects as [PFObject]
for object in objects {
let photoUploaded = object["PhotoUploaded"] as PFFile
photoUploaded.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let image = UIImage(data:imageData)
//image object implementation
self.photosUploadedArray.append(image!)
}
})
}
}
else{
println("Error in retrieving \(error)")
}
})
}
Func to be called after async download images
This loads the UIPageViewController
func loadPhotosFromArray() {
var array = photosUploadedArray
view1 = PhotoCollevtionView(outerFrame: self.view.frame, photoArray: array, currentNumber: 0)
self.view.addSubview(view1!)
}
You can check your uploaded image is equals with last image in your imageObjects array and in this case you can call your loadPhotosFromArray() code like this:
self.photosUploadedArray.append(image!)
if ( imageObjects.last.isEqual(image!)) { //Use this code
loadPhotosFromArray()
}

How can you detect the connection and disconnection of external monitors on the Mac?

Do you have any idea how I can detect additional screens being plugged in / unplugged in a Cocoa application?
I want to detect the moment when the user plugs or unplugs another screen to his Mac. How could I do this?
Your answer lies in Quartz.
#include <ApplicationServices/ApplicationServices.h>
CGError CGDisplayRegisterReconfigurationCallback (
CGDisplayReconfigurationCallBack proc,
void *userInfo
);
And then your proc looks like:
MyCGDisplayReconfigurationCallBack(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo) {
if (flags & kCGDisplayAddFlag || flags & kCGDisplayRemoveFlag) {
DoStuff(display, flags, userInfo);
}
}
In Swift5:
extension ScreenDetector {
static let callback: CGDisplayReconfigurationCallBack = { (displayId, flags, userInfo) in
guard let opaque = userInfo else {
return
}
let mySelf = Unmanaged<ScreenDetector>.fromOpaque(opaque).takeUnretainedValue()
if flags.contains(.addFlag) {
//Add Display...
}else if flags.contains(.removeFlag) {
//Removed Display...
}
}
func addObervers() {
let userData = Unmanaged<ScreenDetector>.passUnretained(self).toOpaque()
CGDisplayRegisterReconfigurationCallback(ScreenDetector.callback, userData)
}
func removeObservers() {
let userData = Unmanaged<ScreenDetector>.passUnretained(self).toOpaque()
CGDisplayRemoveReconfigurationCallback(ScreenDetector.callback, userData)
}
}
If someone is interested in doing this in Swift 2.3, I scratched my head for a little while to translate #iluvcapra 's code:
let userData = UnsafeMutablePointer<ViewController>(Unmanaged.passUnretained(self).toOpaque()) //use the class name of your "self" for future reference inside the callback
CGDisplayRegisterReconfigurationCallback({ (display: UInt32, flags: CGDisplayChangeSummaryFlags, userInfo: UnsafeMutablePointer<Swift.Void>) in
let mySelf = Unmanaged<ViewController>.fromOpaque(COpaquePointer(userInfo)).takeUnretainedValue() //change here to your class name
if flags.rawValue & CGDisplayChangeSummaryFlags.AddFlag.rawValue > 0 {
//do stuff on connect
mySelf.someFunction()
} else if flags.rawValue & CGDisplayChangeSummaryFlags.RemoveFlag.rawValue > 0 {
//do stuff on disconnect
}
}, userData)