Hey there,
I'm currently walking through Apple's official SwiftUI tutorial at https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation.
In Section 8, you can show multiple devices in the canvas preview using ForEach for example. Unfortunately, my canvas always shows the first devices instead of all. In my case, "iPhone XS Max" is not showing.
Does anyone know how to fix this? And where do I find the names of all other device types to add them to the device collection?
Thanks in advance!
Post
Replies
Boosts
Views
Activity
I'm trying to fetch update news from rss feed every hour.
Unfortunately, I sometimes get the following error:
simplexml_load_file(https://developer.apple.com/news/releases/rss/releases.rss): failed to open stream: HTTP request failed! HTTP/1.0 401 Unauthorized
I do not need to login to access that feed directly in my browser. But for some reason, my server runs into some problems with this.
Anyone has an idea how to fix this?
Thank you
Hello everyone, I need some help.
I have absolutely no clue why my notifications are not working.
My AppDelegate looks like this:
import UIKit
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
/// The base UIWindow
var window: UIWindow?
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("token: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for remote notifications with error: \(error))")
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self
UIApplication.shared.registerForRemoteNotifications()
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .badge, .sound],
completionHandler: { (granted, error) in
print("access granted!")
guard granted else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
print("Registered: \(UIApplication.shared.isRegisteredForRemoteNotifications)")
}
})
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
The problem is, function "didRegisterForRemoteNotificationsWithDeviceToken" is never getting called.
I also created "Apple Push Notification service SSL Certificates" in my developer account, added "Push Notifications" to my capabilities in Xcode but it's still not working. Every time I tab on "Allow" to allow notifications, it says "access granted", but it does not print out any device token. "isRegisterForRemoteNotifications" also returns true.
I also searched on the internet for around 2-3 hours, but I couldn't find any solution yet.
I'm using Xcode 12 Beta 2 and iOS 14 Dev Beta 2.
Thank you.
Hello everyone,
I‘m trying to fetch JSON data from an url, building objects and saving them into core data. For example:
{
code: 200,
data: [
	{
	 „id“: 1,
	 „name“: „Peter“,
	 „age“: 35,
	 „email“: „peter@example.com“
	},
	{
	 „id“: 2,
	 „name“: „Tim“,
	 „age“: 62,
	 „email“: „tim@example.com“
	},
	...
]
}
„data“ contains „Person“ objects (or customers in my case containing name, address, etc. ... for example).
Unfortunately, there‘s only one of these values stored in CoreData (in my example the firstname of a Person or „name“ in this case). CoreData creates new „Person“ objects for each but only stores one of it‘s values only.
Do you have an idea why CoreData doesn‘t store everything and just a single value only? Everything else is always nil, I checked the debug.
Thank you in advance.
Hello everyone.
Let me say, I have the following template (I created something similar using PHP and HTML, but I want to create an app as well):
img.mickedplay.de/apple-forum-screenshot-1.png
Dark gray = Header
Light blue = Image
Green = Checkbox
Yellow = Textlabel
Orange = Textfield (some of them have different functions)
Purple = Textarea
Red = signature field
Dark blue = some other similar content
This could be something like a contract paper I want to create using Swift. Almost every element is surrounded by a small border and text fields for example are invisible (the user just need to tap into the empty field inside of the border lines to take action. He could always use two fingers to zoom in (but the paper should keep it's static size). Finally after the user filled out everything, it should create a pdf file of it for example.
Unfortunately I don't know how to start and how to keep the original size (it's measured in cm).
It should have the size of A4 and should look the same on iPhone, iPad and macOS without distortion.
So, I think I have to create a new view and draw lines next to each others, adding text fields, checkboxes, labels and so on.
I hope you understand what I'm trying to do, my english is horrible. 🙄
Thanks for helping.
Hey everybody.I'm in trouble with UISplitViewController which does not seem to work right as I want. On iPhone, everthing looks working fine, but not on the iPad.After the app has started, it looks like this: https://img.mickedplay.de/apple-1.pngAfter selecting a cell from the left table view, it looks like this: https://img.mickedplay.de/apple-2.pngThe title bar disappeared for an unknown reason. Do you know how to fix this? I also took a look into the Storyboard of Apple's MasterDetailApp demo but I could not see any difference between those and mine.And do you know how to move the plus-icon from the title bar next to the search bar? (https://img.mickedplay.de/apple-3.png).Thank you in advance and have a nice day!
Hey everybody,first of all, I'm new in Swift (but not new in software development).I'm currently trying to create a private app which is used at work for another person.It should be possible to create/add/edit or delete customers which are displayed in UITableView and Managed in UITableViewController.I created a small caching system:1. Fetch data from API (an URL which returns JSON)2. Store data (in my case, an array of customers: [Customer]) on device (as JSON or is there any better way to store an array with custom objects?)3. If user restarts app or refreshes table within X minutes, load data from cache, otherwise fetch again from API.Fetching first time from the API works fine and the data is getting inserted into the table. But getting the data from cache, does not work even if it's exactly the same array [Customer].I found out, thatoverride func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {is not even get called, and I have absolutely no idea why it works fetching from API but not from cache.CustomerListController://
// CustomerController.swift
//
// Copyright © 2020 XXXXX. All rights reserved.
//
import UIKit
class CustomerListController: BaseTableController {
/// The table view instance
@IBOutlet var customerTableView: UITableView!
/// List of all customers, fetched from the API
var customersList: [Customer] = []
/// List of all customers, filtered by search controller
var filteredCustomersList: [Customer] = []
/// Array of customers, sorted in sections
var sectionCustomers: [Dictionary<String, [Customer]>.Element] = []
/// Array of filtered customers, sorted in sections
var filteredSectionCustomers: [Dictionary<String, [Customer]>.Element] = []
/// The search controller
var searchController: UISearchController!
/**
* viewDidLoad()
*/
override func viewDidLoad() {
super.viewDidLoad()
// self.customerTableView.tintColor = Constants.COLOR_ON_PRIMARY_BLUE
// self.navigationController?.navigationBar.barTintColor = .red
UITabBar.appearance().tintColor = Constants.COLOR_ON_PRIMARY_BLUE
UITabBar.appearance().barTintColor = Constants.COLOR_PRIMARY_BLUE
// Do any additional setup after loading the view.
self.customerTableView.delegate = self
self.customerTableView.dataSource = self
self.title = "Customers"
self.configureRefreshControl()
self.configureSearchController()
// @NOTE: SectionBar foreground color
self.customerTableView.tintColor = Constants.COLOR_PRIMARY_BLUE
// @NOTE: Loads the data into the table
self.loadData()
}
/**
* Loads the data from cache or refreshes it from the JSON API.
*/
func loadData() {
let current = Date()
let lastCustomerFetch = UserDefaults.standard.object(forKey: Constants.UD_CUSTOMER_LAST_FETCH) as? Date
print("Last fetch: \(lastCustomerFetch!)")
print("--------------------------------------------------------------------")
if lastCustomerFetch != nil {
if lastCustomerFetch!.distance(to: current) >= 900 {
self.fetchJSONDataAndUpdateTable(cachedAt: current, onFetchComplete: { () -> Void in
self.reloadTableData()
})
} else {
do {
let storedObjItem = UserDefaults.standard.object(forKey: Constants.UD_CUSTOMER_CACHE)
self.customersList = try JSONDecoder().decode([Customer].self, from: storedObjItem as! Data)
} catch {
Alert(title: "Error", message: error.localizedDescription).show()
}
print("Updated customers from cache (\(self.customersList.count) entries)")
print(self.customersList)
// @NOTE: Reload table view
self.reloadTableData()
}
} else {
self.fetchJSONDataAndUpdateTable(cachedAt: current, onFetchComplete: { () -> Void in
self.reloadTableData()
})
}
}
/**
* Fetches the json data from an url.
* - Parameter cachedAt: The caching time.
* - Parameter onFetchComplete: What happens, if the fetch has been completed.
*/
func fetchJSONDataAndUpdateTable(cachedAt: Date, onFetchComplete: @escaping () -> Void) {
// @NOTE: Direct JSON URL
var apiParameters = [
"method": "customers",
"limit": 25
]
guard let url = APIUtilities.buildAPIURL(parameters: &apiParameters) else {
Alert(title: "Error", message: "Failed to build api url.").show()
return
}
// @NOTE: Fetches the JSON from tha API-URL and handles it's result.
JSONUtilities.getJSONFromURL(url: url, handleURLData: { (data) in
do {
let decodedResponseJSON = try JSONDecoder().decode(GetCustomerJSONStruct.self, from: data)
// @NOTE: Store basic data in memory
self.customersList = decodedResponseJSON.data ?? []
// @NOTE: Sort into alphabetical sections
self.sectionCustomers = self.orderCustomersIntoSections()
// @NOTE: Store basic data in cache
if let encodedCustomers = try? JSONEncoder().encode(self.customersList) {
UserDefaults.standard.set(encodedCustomers, forKey: Constants.UD_CUSTOMER_CACHE)
}
UserDefaults.standard.set(cachedAt, forKey: Constants.UD_CUSTOMER_LAST_FETCH)
print("Updated customers from API (\(self.customersList.count) entries)")
print(self.customersList)
onFetchComplete()
} catch {
DispatchQueue.main.async {
Alert(title: "Error", message: "Failed to decode JSON.").show()
}
}
})
}
/*
* Reloads the table view containing the customer data.
*/
func reloadTableData() {
print("Refreshing data...")
DispatchQueue.main.async {
self.customerTableView.reloadData()
print("Refresh complete!")
}
}
/**
* Order the customers into alphabet-based bsections.
*/
func orderCustomersIntoSections() -> [Dictionary<String, [Customer]>.Element] {
var orderedSections: [String:[Customer]] = [:]
// @NOTE: Order customers into sections relative to the first character of their display name
for customer in self.customersList {
var firstDisplayCharacter = String(customer.getDisplayName().string.prefix(1)).uppercased()
let range = firstDisplayCharacter.rangeOfCharacter(from: CharacterSet.letters)
if range == nil {
firstDisplayCharacter = "#"
}
// @NOTE: Create new section if it doesn't exist yet
if orderedSections[firstDisplayCharacter] == nil {
orderedSections[firstDisplayCharacter] = []
}
orderedSections[firstDisplayCharacter]?.append(customer)
}
return orderedSections.sorted { (first, second) -> Bool in
return first.key < second.key
}
}
/**
* Adds the refresh control to the table view.
*/
func configureRefreshControl () {
self.customerTableView.refreshControl = UIRefreshControl()
self.customerTableView.refreshControl?.tintColor = Constants.COLOR_ON_PRIMARY_BLUE
self.customerTableView.refreshControl?.addTarget(self, action: #selector(handleRefreshControl), for: .valueChanged)
self.customerTableView.refreshControl?.attributedTitle = NSAttributedString(
string: "Pull down to refresh",
attributes: [.foregroundColor: Constants.COLOR_ON_PRIMARY_BLUE]
)
}
/**
* Adds the search field on top of the table.
*/
func configureSearchController() {
self.searchController = super.configureSearchController(
placeholder: "Search...",
scopeButtonTitles: [Constants.CUSTOMER_SEARCH_SCOPE_NAME, Constants.CUSTOMER_SEARCH_SCOPE_ADDRESS],
tintColor: Constants.COLOR_ON_PRIMARY_BLUE,
backgroundColor: Constants.COLOR_PRIMARY_BLUE
)
self.searchController.searchResultsUpdater = self
self.searchController.searchBar.delegate = self
self.navigationItem.searchController = self.searchController
self.navigationItem.hidesSearchBarWhenScrolling = false
}
/**
* Handles the table view refresh control.
*/
@objc func handleRefreshControl() {
// @TODO: Refresh
// Dismiss the refresh control.
DispatchQueue.main.async {
self.customerTableView.refreshControl?.endRefreshing()
}
}
/**
* Filters the customers by the given scope.
*/
func filterCustomersForSearch(searchText: String, scope: String) {
let lowercasedSearchText = searchText.lowercased()
self.filteredSectionCustomers = []
if self.isSearchBarEmpty() {
self.filteredSectionCustomers = self.sectionCustomers
} else {
switch(scope) {
case Constants.CUSTOMER_SEARCH_SCOPE_NAME:
for (sectionTitle, entry) in self.sectionCustomers {
var tempCustomers: [Customer] = []
for customer in entry {
if customer.getDisplayName().string.lowercased().contains(lowercasedSearchText) {
tempCustomers.append(customer)
}
}
if tempCustomers.count > 0 {
self.filteredSectionCustomers.append((sectionTitle, tempCustomers))
}
}
break;
case Constants.CUSTOMER_SEARCH_SCOPE_ADDRESS:
for (sectionTitle, entry) in self.sectionCustomers {
var tempCustomers: [Customer] = []
for customer in entry {
if customer.getDisplayAddress().lowercased().contains(lowercasedSearchText) {
tempCustomers.append(customer)
}
}
if tempCustomers.count > 0 {
self.filteredSectionCustomers.append((sectionTitle, tempCustomers))
}
}
break;
default:
break;
}
}
self.reloadTableData()
}
/**
* - Returns: TRUE, if the search bar is empty, otherwise FALSE.
*/
func isSearchBarEmpty() -> Bool {
return self.searchController.searchBar.text?.isEmpty ?? true
}
/**
* - Returns: TRUE, if the user is searching, otherwise FALSE.
*/
func isUserSearching() -> Bool {
let searchBarScopeIsFiltering = self.searchController.searchBar.selectedScopeButtonIndex != 0
return self.searchController.isActive && (!self.isSearchBarEmpty() || searchBarScopeIsFiltering)
}
}
extension CustomerListController {
/**
* This is called for every table cell.
*/
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomerCell") as? CustomerCell else { return UITableViewCell() }
let currentCustomer = self.isUserSearching()
? self.filteredSectionCustomers[indexPath.section].value[indexPath.row]
: self.sectionCustomers[indexPath.section].value[indexPath.row]
cell.customerDisplayName.attributedText = currentCustomer.getDisplayName()
cell.customerDisplayAddress.text = currentCustomer.getDisplayAddress()
return cell
}
/**
* Returns the amount of sections.
*/
override func numberOfSections(in tableView: UITableView) -> Int {
return self.isUserSearching() ? self.filteredSectionCustomers.count : self.sectionCustomers.count
}
/**
* Returns the amount of rows in a section.
*/
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.isUserSearching()
? self.filteredSectionCustomers[section].value.count
: self.sectionCustomers[section].value.count
}
/**
* Set's the title for sections.
*/
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (ListUtilities.getKeysFromTuples(
tuple: (self.isUserSearching() ? self.filteredSectionCustomers : self.sectionCustomers)
) as? [String])?[section]
}
/**
* The section index title on the right side of the table view.
*/
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return ListUtilities.getKeysFromTuples(
tuple: self.isUserSearching() ? self.filteredSectionCustomers : self.sectionCustomers
) as? [String]
}
/**
* Called, whenever a user deselectes a table cell.
*/
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showCustomerDetail", sender: self)
}
/**
* Prepares the segue to open.
*/
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? CustomerDetailController {
let indexPath = self.customerTableView.indexPathForSelectedRow
guard let selectedSection = indexPath?.section else { return }
guard let selectedRow = indexPath?.row else { return }
let customerList = self.isUserSearching()
? self.filteredSectionCustomers
: self.sectionCustomers
destination.customer = customerList[selectedSection].value[selectedRow]
}
}
}
extension CustomerListController: UISearchBarDelegate, UISearchResultsUpdating {
/**
* Called when the suer changes the search scope.
*/
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
self.filterCustomersForSearch(searchText: searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
/**
* Called when the search results need tp be updated (e.g. when the user types a character in the search bar)
*/
func updateSearchResults(for searchController: UISearchController) {
let searchBar = self.searchController!.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
self.filterCustomersForSearch(searchText: self.searchController!.searchBar.text!, scope: scope)
}
}Class "BaseTableController" extends from UITableViewController and does not contain important things.I hope, you can help me and I reall appreciate your support.Thank you!