Save unique String Array With #AppStorage - macos

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
}
}
}
}

Related

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] } }

Finding the place of the array

Im using this function to find which place in the array the title has, I'm wondering if it's possible to do this in a better/faster way.
func findId(title:String){
var id = 0
for restaurant in restaurantObjects{
if (restaurant.name == title){
self.idClicked = id
}
else{
id++
}
}
}
The function returns an optional Int, it's nil if the index could not be found.
func findId(title:String) -> Int? {
return restaurantObjects.indexOf{ $0.name == title}
}
struct Restaurant {
let name : String
init(_ name: String) {
self.name = name
}
}
let restaurantObjects : [Restaurant] = [Restaurant("foo"), Restaurant("foofoo"), Restaurant("bar"), Restaurant("foo")]
let title = "bar"
if let pos = restaurantObjects.indexOf({ $0.name == title }) {
print("Found \(title) at index \(pos)") // Found bar at index 2
}
Alternatively, make an array extension for elements of type Restaurant, allowing you to simply call .findFirstId(title: String) on you array of Restaurant objects.
struct Restaurant {
var name : String
init(_ name: String) {
self.name = name
}
}
protocol RestaurantObject {
var name : String { get }
}
extension Restaurant : RestaurantObject {}
extension Array where Element : RestaurantObject {
func findFirstId(title: String) -> Int? {
return self.indexOf { $0.name == title }
}
}
/* Example */
let restaurantObjects : [Restaurant] = [Restaurant("foo"), Restaurant("foofoo"), Restaurant("bar"), Restaurant("foo")]
let title = "bar"
restaurantObjects.findFirstId(title) // 2

Swift Playground: NSXMLParser / parse() always returns false

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.

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