Swift Playground: NSXMLParser / parse() always returns false - xcode

here's my code:
public class Payment: NSObject, NSXMLParserDelegate {
public var buchungsdatum:NSDate
public var valutadatum:NSDate
public var betrag:Double
public var verwendungszweck:String
public var creditorIban:String
public var creditorBic:String
public var creditorName:String
public var debitorIban:String
public var debitorBic:String
public var debitorName:String
override init() {
buchungsdatum = NSDate()
valutadatum = NSDate()
betrag = 0.0
verwendungszweck = ""
creditorIban = ""
creditorBic = ""
creditorName = ""
debitorBic = ""
debitorIban = ""
debitorName = ""
}
override public var description: String {
return "Payment-Information:"
}
func beginParse() {
let url = NSURL(fileURLWithPath: "/Users/Andi/Downloads/c53/CAMT/2015-10-06_C53_DE92300606010303502481_EUR_585142.xml")
let xml = NSXMLParser(contentsOfURL: url)!
xml.delegate = self
xml.parse()
}
#objc public func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
print("get an element")
}
#objc public func parserDidStartDocument(parser: NSXMLParser) {
print("started")
}
#objc public func parserDidEndDocument(parser: NSXMLParser) {
print("ended")
}
public func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
print("Fehler")
}
}
var aPayment:Payment = Payment()
aPayment.beginParse()
Can anyone tell me why the parse() returns false and none of the delegate methods get called. Even the parseError one doesn't. I absolutely don't understand why.
Xcode even shows that the XML-File has content and that url isn't nil (see pic)
Xcode shows the XMLContent in the playground

It is most likely because your NSURL is nil.

Related

Save unique String Array With #AppStorage

In the following example I am saving a String with #AppStorage:
struct ContentView: View {
#AppStorage("text") private var text = ""
var body: some View {
Button("Append text: \(text)") {
text.append("APPEND")
}
}
}
But I want to save a unique String array, something like this:
#AppStorage("text") #State private var customer = [CustomerId]()
//
struct CustomerId: Identifiable {
let id = UUID()
var number: String
init(_ number: String) {
self.number = number
}
//
Button {
customer
.append(CustomerId("New Id"))
}
It's highly recommended to move the entire code to modify the model into an extra class called view model.
The #AppStorage property wrapper reads and writes a JSON string.
The #Published property wrapper updates the view.
In the init method the saved JSON string is decoded to [CustomerId].
The method appendCustomer appends the items, encodes the array to JSON and writes it to disk.
class ViewModel : ObservableObject {
#AppStorage("text") var text = ""
#Published var customer = [CustomerId]()
init() {
customer = (try? JSONDecoder().decode([CustomerId].self, from: Data(text.utf8))) ?? []
}
func appendCustomer(_ sustomerId : CustomerId) {
customer.append(sustomerId)
guard let data = try? JSONEncoder().encode(customer),
let string = String(data: data, encoding: .utf8) else { return }
text = string
}
}
struct ContentView : View {
#StateObject private var model = ViewModel()
#State private var counter = 0
var body: some View {
VStack{
ForEach(model.customer) { item in
Text(item.number)
}
Button("Append text: New Id \(counter)") {
model.appendCustomer(CustomerId(number: "New Id \(counter)"))
counter += 1
}
}
}
}

Ashley Mills reachability not sending notification when wifi turns back on?

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.

Swift: Instance member cannot be used on type

I'm trying to call a variable from my Users class in Data class but I'm thrown this error
Instance member 'user' cannot be used on type 'Data'
Data class code:
import Foundation
class Data {
static let sharedInstance = Data()
let user = Users()
var id = (user.userId).string
//Login.swift
static let quarterlyEndpoint: String = "http://anyapi/api/users/\(id)/quarterly/tasks"
// for prevent from creating this class object
private init() { }
}
Users class code:
var userId: Int {
guard let _userId = getId() else {
return 0
}
return _userId
}
UPDATE
I can call the function but when I print my weeklyEnd I would get http://anyapi/api/users/(Function)/quarterly/tasks instead of http://anyapi/api/users/3/quarterly/tasks
let user = Users()
func getId() -> String
{
let id = String(user.userId)
return id
}
static let weeklyEndpoint: String = "http://anyapi/api/users/\(getId)/quarterly/tasks"
UPDATE 2
import Foundation
class Data {
static let sharedInstance = Data()
let user = Users()
let id:String?
let weeklyEndpoint: String?
// for prevent from creating this class object
private init() {
id = String((user.userId))
//Login.swift
weeklyEndpoint = "http://anyapi/api/users/\(id)/quarterly/tasks"
}
}
I'm having trouble calling my weeklyEndPoint from outside the Data class. I've tried Data.weeklyEndPoint but it's not working
Try making a method inside the Data class like this .
func getusrid ()-> String
{
var id = (user.userId).string
return id ;
}
You cannot get value from instance variable outside any method
EDIT:
class Data {
static let sharedInstance = Data()
let id:String?
let quarterlyEndpoint: String?
let user = Users()
private init()
{
id = String((user.userId))
quarterlyEndpoint = "http://anyapi/api/users/\(id)/quarterly/tasks"
}
}

Convert my model part of MVC from array to dictionary

let's assume that my model has the following:
import Foundation
class Model {
var userName = [String]()
var userAnswer = [String]()
var userInfo = [String]()
var name:String
var answer:String
init(){
self.name = ""
self.answer = ""
}
func addUserInfo(name:String, answer:String) -> Void {
userName.append(name)
userAnswer.append(answer)
}
}
What would one do to transform this model to have userName, and userAnswer become one joined dictionary? where the function addUserInfo would still work. Thanks!
you can use a funcion return a array or a new property with only get methord,just like this
var userNamesAndUserAnswers:[[String:String]]
{
get
{
var values:[[String:String]]=[]
for index in 0..<userName.count
{
values.append(["name":userName[index],"answer":userAnswer[index]])
}
return values
}
}
You can just have a Dictionary that represents user names mapped to user answers like this:
class Model {
var userInfo = [String:String]()
func addUserInfo(name:String, answer:String) {
userInfo[name] = answer
}
}
There's no need to import Foundation since you're not using any Foundation objects.
If you want a [String: String]:
var dict: [String: String] {
var result: [String: String] = [:]
zip(userName, userAnswer).forEach { result[$0.0] = $0.1 }
return result
}
Or if you want [[String: String]]:
var dict2: [[String: String]] { return zip(userName, userAnswer).map { [$0.0: $0.1] } }

Pass asynchronously fetched data to another class

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

Resources