Update Contentful entry with a link to a reference filed (in Contentful) - xcode

I want to update a field on an entry that is a link to another entry inside Contentful (they call it 'Reference'). However they don't have any clear documentation on how to call the data using SwiftUI. I just need to know how to format that API call when using the Contentful Management API. Didn't get any help from the Contentful team nor in their Slack community, so I'm trying here.
The specific entry in question will be the episodes variable which is linked to [HPtvSeriesEpisodes]. No matter what format I try I get the error "Cannot convert value of type '[Entry]' to expected argument type '[HPtvSeriesEpisodes]"
I just want to know how should I format that specific line of code
This is my Data Model
struct HPtvSeries: Identifiable {
var id = UUID()
var seriesTitle: String
var seriesDescription: String
var seriesDate: String
var seriesType: String
var numberofEpisodes: Int
var seriesBanner: URL
var seriesThumbnail: URL
var episodes: [HPtvSeriesEpisodes]
}
The Content Management API call
class HPtvSeriesStore: ObservableObject{
#Published var hptvSermons: [HPtvSeries] = []
init(){
getArray(id: "hPtvSeries") { (items) in
items.forEach { (item) in
self.hptvSermons.append(HPtvSeries(
seriesTitle: item.fields["seriesTitle"] as! String,
seriesDescription: item.fields["seriesDescription"] as! String,
seriesDate: item.fields["seriesDate"] as! String ,
seriesType: item.fields["seriesType"] as! String ,
numberofEpisodes:item.fields["numberOfEpisodes"] as! Int,
seriesBanner: item.fields.linkedAsset(at: "seriesBanner")?.url ?? URL(string: "https://dl.dropbox.com/s/kqmc8c2i3banhhi/HPtv%20Logo_2.png?dl=0")!,
seriesThumbnail: item.fields.linkedAsset(at: "seriesThumbnail")?.url ?? URL(string: "https://dl.dropbox.com/s/kqmc8c2i3banhhi/HPtv%20Logo_2.png?dl=0")!,
// episodes: item.fields.linkedEntries(at: "episode") // Not sure how to format the code here
))
}
}
}
}

Related

How to make a String accept a certain type of characters if you have to use it on a url request

I'm making an app that calls a weather api. As expected in the url request you can't use any character that you want. The app haves a TextField in which you can search the city that you want to have the general weather. Searching something with special characters like: "España" or "México" crashes the App because the url request doesn't accept "ñ" or "é". I already fixed the problem made by putting a blank space in the textField on the firsts lines of the fetch() function.
Thanks for the time. Sorry for the band English.
class WeatherClass: ObservableObject {
#Published var weatherAddress: String = ""
#Published var weatherDays: [WeatherDays] = []
#Published var city: String = ""
var cityTrimmed: String = ""
func fetch() {
city = city.trimmingCharacters(in: [" "])
for letter in city {
if letter != " " {
cityTrimmed.append(letter)
}
}
let apiKey = "AP8LUYMSTHFQAF"
let url = URL(string: "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/\(cityTrimmed)?key=\(apiKey)")!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
if let weather = try? JSONDecoder().decode(WeatherData.self, from: data) {
DispatchQueue.main.async {
self.cityTrimmed.removeAll()
self.weatherAddress = weather.resolvedAddress
self.weatherDays = weather.days
}
struct searchBarUI: View {
#ObservedObject var weatherModel: WeatherClass
var body: some View {
TextField("Search city", text: $weatherModel.city).frame(height:30).multilineTextAlignment(.center).background().cornerRadius(25).padding(.horizontal)
}
}

Extracting single String/Int from fetched API data list

I am struggling to extract single values (strings and Int) from the data I fetched from an API. I fetch the data in the form of a list:
class apiCall {
func getRockets(completion:#escaping ([Rockets]) -> ()) {
guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let rockets = try! JSONDecoder().decode([Rockets].self, from: data!)
print(rockets)
DispatchQueue.main.async {
completion(rockets)
}
}
.resume()
}
}
Then when I try using it in a View I succeed when using a List to view the values, like it shows the list of the names from the API data. But then when I want to view a single value like this:
import SwiftUI
struct RocketStatistics: View {
#State var rockets: [Rockets] = []
var body: some View {
VStack{
Text(rockets[1].name)
}
.onAppear {
apiCall().getRockets { (rockets) in
self.rockets = rockets
}
}
}
}
struct RocketStatistics_Previews: PreviewProvider {
static var previews: some View {
RocketStatistics()
}
}
It does not even give me an error, but my preview just won't update and keeps crashing.
So my question is how can I extract single values from this API data in List form and use these single values in my whole project?
To keep it simple and make it work first I started just with fetching the "name" from the API:
import Foundation
import SwiftUI
struct Rockets: Codable, Identifiable {
let id = UUID()
let name : String
}
When it all works I would also want to use Integer values from the API in my project, so tips on how to that are also welcome!
Never ever get items by a hard-coded index in a SwiftUI view without any check. When the view is rendered the first time the array is empty and any index subscription will crash.
Always check if the array contains the required number of items. In this case the array must contain at least two items
VStack{
Text(rockets.count > 1 ? rockets[1].name : "")
}

Swift Firestore: Getting all data from firestore and inputing data into Table View

The goal here is to retrieve all the documents of a Firestore Database, then putting the data of those retrieved documents into a Table View. I'm using Xcode and Firebase.
For example, if I had a Firestore database with this data and documents:
Document 1
Name: Bob
Email: bob#gmail.com
Phone: 408-111-1234
Document 2
Name: Joe
Email: joe#yahoo.com
Phone: 408-338-4321
I would like to have it all in a table view, maybe something like this:
Bob - bob#gmail.com - 408-111-1234
Joe - joe#yahoo.com - 408-338-4321
So far, I have this set up:
TableViewController:
class PeopleViewController: UITableViewController {
#IBOutlet var table: UITableView!
var peopleArray = [] as [Array<Any>]
private var document: [DocumentSnapshot] = []
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView(frame: CGRect.zero)
self.table.delegate = self
self.table.dataSource = self
loadData()
}
And the loadData() func:
func loadData() {
FirebaseFirestore.root.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
let email = data["email"] as? String ?? ""
let phone = data["phone"] as? String ?? ""
let newPerson = ["name": name, "email": email, "phone": phone]
self.peopleArray.append(newPerson)
print(self.peopleArray)
}
self.table.reloadData()
}
}
}
}
The first thing wrong here is that there is this error:
Cannot convert value of type '[String : String]' to expected argument type '[Any]'
When I try to append a newPerson in the PeopleArray. I have searched this up, but none of the answers matched this specific error. The second thing is that I have no clue how to even start adding the data from the PeopleArray into the Table View.
I would love some help for both the error and the Table View. Anything is appreciated!
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
After doing what Vadian recommended, I am stuck with a new error.
attempt to insert row 2 into section 0, but there are only 1 rows in section 0 after the update
This is the updated func loadData:
func loadData() {
FirebaseFirestore.root.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
self.table.beginUpdates()
let data = document.data()
let name = data["name"] as? String ?? ""
let zip = data["zip"] as? String ?? ""
let sport = data["sport"] as? String ?? ""
let newPerson = Person(name: name, zip: zip, sport: sport)
self.people.append(newPerson)
let newIndexPath = IndexPath(row: self.people.count, section: 0)
self.table.insertRows(at: [newIndexPath], with: .automatic)
self.table.endUpdates()
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
}
}
I've read about this on another question about how the rows are off by 1, but even when I let indexPath equal to people.count+1 or people.count+2, the error persists. What am I doing wrong?
First of all the syntax
var peopleArray = [] as [Array<Any>]
is bad practice. If you want to declare an empty array write
var peopleArray : [Array<Any>] = []
or
var peopleArray = [Array<Any>]()
Second of all your array is a nested array which is not intended. You mean
var peopleArray = Array<Any>()
which is more descriptive using the alternative syntax
var peopleArray = [Any]()
Third of all as your data is clearly a more specific type than [Any] declare the array
var peopleArray = [[String:String]]()
This fixes the error.
Fourth of all the recommended data source is a custom struct
struct Person {
let name, email, phone: String
}
In this case declare the array (we know that people is plural so the suffix array is redundant).
var people = [Person]()
And populate it (you might reload the table view on the main thread)
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
let email = data["email"] as? String ?? ""
let phone = data["phone"] as? String ?? ""
let newPerson = Person(name: name, email: email, phone: phone)
self.people.append(newPerson)
}
DispatchQueue.main.async {
self.table.reloadData()
}
I researched a lot and fixed the edited part in the question I posted. A simple -1 in the insert rows fixed the problem.
let newIndexPath = IndexPath(row: self.people.count-1, section: 0)
Vadian also helped me with the original problem, which was using
var peopleArray = [[String:String]]()
instead of the other incorrect format. I was then able to find how to insert rows by just simply using the table.insertrows function.
(I'm just posting this for a simplified answer for anyone else experiencing problems).

Swift2 access component with string-name

Im more familiar with ActionScript3 and see many similarities in Swift2, kind of why i am trying out basic coding in Swift2 and Xcode.
Here's my example:
#IBOutlet weak var b1CurrSpeed: NSTextField!
I want to store b1CurrSpeed as a string so i could access the actual textfield component to set its default value when application is loaded.
I'm aiming for Swift2 for osx apps.
Here is a fictional example, not related to any actual code:
var tf:NSTextField = this.getItem("b1CurrSpeed");
tf.stringValue = "Hello world";
Reason to this approach is following...
I would like to store textfield value in NSUserDefaults, the key for defaults would be name of that textfield. So when looping thru the defaults, i would like to get key as string and when ive got that i'd have access to actual component to set its stringvalue property.
Tho, is that good approach in Swift / xCode ?
If you want to create a function for it, do someting like this:
func getStringForKey(key: String) -> String {
guard let result = NSUserDefaults.standardUserDefaults().objectForKey(key) as! String else { return "" }
return result
}
You can set the TextFields value with myTextField.text
Swift's Mirror type can get you close to it but it is limited to NSObject subclasses, can only access stored properties and is read-only.
Yet, there are ways around these limitations if your requirements will allow.
For example, here's an extension that will save and restore defaults values for all UITextfields on a view controller using the name you gave to each IBOutlet.
extension UIViewController
{
func loadDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
// add variants for each type/property you want to support
if let field = prop.value as? UITextField,
let name = prop.label
{
if let defaultValue = userDefaults.objectForKey(name) as? String
{ field.text = defaultValue }
}
}
}
func saveDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
if let field = prop.value as? UITextField,
let name = prop.label
{
if let currentValue = field.text
{ userDefaults.setObject(currentValue, forKey: name) }
}
}
}
}

Get Contact Image Data using ABPersonCopyImageData

I'm trying to get the contact details out of the address book on the Mac. I can get the first name and last name fields etc, but I'm struggling with the syntax for ABPersonCopyImageData.
Now according to the documentation ABPersonCopyImageData takes a single parameter of type ABPerson.
Here is my code:
import AddressBook
let thisPerson : ABPerson
let addressBook = ABAddressBook.sharedAddressBook()
rec = addressBook.recordForUniqueId("0005A360-327F-4E12-BBB9-24A842497E12:ABPerson")
let firstName = rec.valueForProperty(kABFirstNameProperty) as! String
let lastName = rec.valueForProperty(kABLastNameProperty) as! String
println("\(firstName) \(lastName)")
let contactImage = ABPersonCopyImageData(thisPerson)
The last line stops the compiler with an error: Cannot invoke 'ABPersonCopyImageData' with an argument list of type (ABPerson). As far as I can tell thisPerson is of type ABPerson. What is going wrong?
I found out how to do this in ElCapitan:
import Contacts
func getContactImage(name:String) -> NSImage?
{
let store = CNContactStore()
do
{
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName(name), keysToFetch:[CNContactImageDataKey])
if contacts.count > 0
{
if let image = contacts[0].imageData
{
return NSImage.init(data: image)
}
}
}
catch
{
}
return nil
}

Resources