Cannot force unwrap value of non-optional type 'Reachability' for swift 5 - unwrap

I get the warning xcode 11.1 for iOS 13 (swift 5)
Cannot force unwrap value of non-optional type 'Reachability' on the line let reachability = Reachability()!
I tried, if statements and try do, but none seem to work.Removing the ! gives the warning "Call can throw, but errors cannot be thrown out of a property initializer"
import Reachability
class ReachabilityDetect {
let reachability = Reachability()!
var dm = DataModel()
func addObservers(datamodel: DataModel) {
self.dm = datamodel
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
do{
try reachability.startNotifier()
}catch{
Util.DLog("Reachability notifier niet te starten.")
}
}
#objc func reachabilityChanged(note: Notification) {
let reachability = note.object as! Reachability
switch reachability.connection {
case .wifi:
Util.DLog("WiFi is actief")
self.dm.dataConnectionisWifi = true
self.dm.dataConnectionisCellular = false
case .cellular:
Util.DLog("Celluar data is actief")
self.dm.dataConnectionisWifi = false
self.dm.dataConnectionisCellular = true
case .none:
Util.DLog("geen celluar of wifi data actief")
self.dm.dataConnectionisWifi = false
self.dm.dataConnectionisCellular = false
default: break
}
}
}

I had the same problem.
Instead of
let reachability = Reachability()!
use this
let reachability = try! Reachability()

Related

SwiftUI Error Regarding Publishing Changes from Background Thread

I am not quite sure how to fix an error that appears three times in my View Model:
import SwiftUI
class GHViewModel: ObservableObject {
let columns: [GridItem] = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
#Published var followers: [Follower] = []
#Published var page = 1
#Published var user: User = PlaceholderData.user
#Published var networkError: GFError = .invalidURL
#Published var shouldShowNetworkError = false
#Published var shouldShowFollowerDetails = false
func getFollowers(userID: String) {
NetworkManager.shared.getFollowers(for: userID, page: self.page) { result in
switch result {
case .success(let followers):
if !followers.isEmpty {
self.followers += followers
print("**** GHViewModel -> FollowersScreen has retrieved the followers. I see \(self.followers.count) followers for this user.")
}
case .failure(let error):
self.shouldShowNetworkError = true
self.networkError = error
}
}
}
func getFollowerDetails(userID: String) {
NetworkManager.shared.getUserInfo(for: userID) { result in
switch result {
case .success(let ghUser):
self.user = ghUser
self.shouldShowFollowerDetails = true
case .failure(let error):
self.shouldShowNetworkError = true
self.networkError = error
}
}
}
}
The 3 warnings are happening at:
#Published var followers: [Follower] = []
and..
case .success(let ghUser):
self.user = ghUser
self.shouldShowFollowerDetails = true
What is this error actually saying? I suppose it's suggesting that I should be using
DispatchQueue.main.async { // code here }
around the code that's generating the warnings?
Please advise?
Yes, it is telling your to use DispatchQueue.main.async. Remember, network calls are asynchoronous. You don't know when you are getting a response, so they are automatically performed on a background thread, but you always need to update your data that is being displayed on your main thread. Therefore, every time you hit .success, you need to update the variables on the main thread by enclosing the updates in DispatchQueue.main.async.
case .success(let followers):
if !followers.isEmpty {
DispatchQueue.main.async {
self.followers += followers
print("**** GHViewModel -> FollowersScreen has retrieved the followers. I see \(self.followers.count) followers for this user.")
}
}
and
case .success(let ghUser):
DispatchQueue.main.async {
self.user = ghUser
self.shouldShowFollowerDetails = true
}
I suspect the #Published error will go away as I suspect that is thrown due to the other issues.

StoreKit problem while restoring In-App Purchase

I tried to find a tutorial about IAP, and I found : This one
But when I call function to restore, nothing happens. OnTap button I call : store.restorePurchase()
extension Store {
func product(for identifier: String) -> SKProduct? { ...}
func purchaseProduct (_ product: SKProduct){ ...}
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
print("restore")
}
}
restore is print in my console. It is the only thing that is printed in console. But in the code above, you can see .restored:that should print "UserIsPremium"
extension Store : SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions : [SKPaymentTransaction]){
for transaction in transactions {
var shouldFinishTransaction = false
switch transaction.transactionState{
case .purchased, .restored:
completedPurchases.append(transaction.payment.productIdentifier)
print("UserIsPremium")
shouldFinishTransaction = true
case .failed:
print("NotPremium")
shouldFinishTransaction = true
case .deferred, .purchasing:
print("...")
break
#unknown default:
print("unknown")
break
}
if shouldFinishTransaction {
print("shouldfinish")
SKPaymentQueue.default().finishTransaction(transaction)
DispatchQueue.main.async {
self.purchaseCompletionHandler?(transaction)
self.purchaseCompletionHandler = nil
}
}
}
}
}
My class Store :
import StoreKit
typealias FetchCompletionHandler = (([SKProduct]) -> Void)
typealias PurchaseCompletionHandler = ((SKPaymentTransaction?) -> Void)
class Store: NSObject, ObservableObject {
#Published var allFullVersion = [FullVersion]()
private let allProductsIdentifiers = Set ([
"ClemPapplis.OrientationEPS.versionFull"
])
private var completedPurchases = [String]() {
didSet {
DispatchQueue.main.async{ [weak self] in
guard let self = self else { return }
for index in self.allFullVersion.indices {
if self.completedPurchases.contains(self.allFullVersion[index].id){
print("completedPurchases")
UserDefaults.standard.setValue(true, forKey: "Premium")
}
self.allFullVersion[index].isLocked =
!self.completedPurchases.contains(self.allFullVersion[index].id)
}
}
}
}
I use sandbox testers.
If I buy item, no problem, datas are print in console.
Did I forget something ?
It's been a few years since I used StoreKit, but I remember having an issue with restore purchases also (and yes, #ElTomato is correct, use a separate case - I'm surprised your code actually builds). I found that the following worked:
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
delegate.purchaseComplete()
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
default:
break
}}}
}
func restorePurchases() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
for _ in queue.transactions {
alreadyPurchased = true
delegate.purchasesRestored()
}
showAlert("Purchases have been restored.")
}
Don't worry about showAlert, that's just an extension I have for UIViewController. But what I'm not seeing in your code is paymentQueueRestoreCompletedTransactionsFinished(_ queue:).
I followed the same tutorial i think you did not select the select the none in here
configuration
to restore in your simulator you should select none when you select none it tries to connect apple developers in app purchase configuration. give it a try and let me know.

Getting nil response while passing string to another view controller - Swift3

In FirstViewController i'm fetching the response from JSON and want to pass that fetched response to another view controller.Below is the code which i have used so far for parsing and passing the response.
FirstViewController
var fn:String! //globally declared variable
code i have tried for parsing in FirstViewController
do {
let detailsDictionary:NSDictionary = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! Dictionary<String, AnyObject> as NSDictionary
print(detailsDictionary)
let details = detailsDictionary["Data"] as! [[String:AnyObject]]
print(details)
for dtl in details
{
self.fn = dtl["Father_Name"] as? String ?? "NA"
print(self.fn) //here i'm getting the exact value from JSON
}
}
}
SecondViewController
In SecondViewController there is a Label called profile_name and want to set that parsed string(fn) as Label's text. for that i declared another variable as global.
var pname:String!
below is the code i have used to fetch the value from FirstViewController.
viewDidLoad()
{
let othervc = FirstViewController()
self.pname = othervc.fn
self.profile_name.text = self.pname
}
Problem : I tried my best efforts to get the desired output but i'm getting nil response.
Please Help.
In Second ViewController
let strName:String!
In First ViewController
let strOne = "This is for testing"
let objstory = self.storyboard?.instantiateViewController(withIdentifier: "yout Secoond ViewController Storybord ID") as! YourSecondViewControllerName
objstory.strNam = strOne
self.navigationController?.pushViewController(objstory, animated: true)
Your updated code just won't work.
let othervc = FirstViewController()
creates a new instance of FirstViewController (not the one that got the JSON).
You should be handling it something like this:
In FirstViewController
let fn = dtl["Father_Name"] as? String ?? "NA"
let svc = SecondViewController() // Or maybe instantiate from Storyboard, or maybe you already have a reference to it
svc.pname = fn
present(svc, animated: true, completion: nil)
Then in SecondViewController
override func viewDidLoad() {
super.viewDidLoad()
profile_name.text = pname
}
I'd suggest you take some time out and re-read Apple's View Controller programming guide.
Original Answer
The problem you have here…
vcvalue.profile_name.text = fn
is that profile_name is nil as the view for the view controller hasn't been loaded at this point.
You should handle this by creating a property in LeftSideMenuViewController
var name: String?
Then set
vcvalue.name = fn
And then in LeftSideMenuViewController
override func viewDidLoad() {
super.viewDidLoad()
profile_name.text = name
}
Also, some basic tips…
Don't force unwrap (!) apart from IBOutlets. You may have to write a bit more code, but you will reduce crashes.
Make #IBOutlets private - this will prevent you accidentally assigning to them as you are now
If you're overriding any viewWill/DidDis/Appear methods, you must call super at some point.
You need to re-read the section on switch/case
So this…
let a = indexPath.row
switch(a)
{
case 0 :
if(a == 0)
{
return 45
}
break
etc
could just be…
switch indexPath.row {
case 0...4:
return 45
case 5:
return 50
default:
break
}

NSManagedObjectContext is not saving the NSManagedObject Values in SWIFT 3.0 and XCOde 8

I am facing a problem while saving the NSManagedObject to NSManagedObjectContext in Swift 3.0 and Xcode 8. Adding the code snippets for better Understanding
let config = NSManagedObject(entity: entityDescription!, insertInto: self.moc) as! Config
Here Config class is derived from NSManagedObject
class Config: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
Assigning the Key and value to my config as below and calling a save
config.key = "access_token"
config.value = access_token
do
{
try config.managedObjectContext?.save()
}catch let error as NSError
{
NSLog(error.localizedDescription)
onCompletion("Login Failed")
return
}
This doesnt throw any error to me, but while fetching the value of access_token from NSManagedObject, value is nil
do
{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Config")
let predicate = NSPredicate(format: "key == %#", "access_token")
fetchRequest.predicate = predicate
let fetchResults = try moc.fetch(fetchRequest) as? [Config]
if(fetchResults?.count > 0)
{
//NSLog((fetchResults?.first!.value)!)
return fetchResults?.first!.value
}
} catch let error as NSError{
NSLog(error.localizedDescription)
}
What is wrong with this piece of code?
EDIT: I can see the following code where persistentStoreCoordinator is set for managedObjectContext
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()

Swift 3 and NSURLSession issue

Thanks to Apple my iOS 9 Project 'Swift 2.3' is completely unusable with iOS 10's 'Swift 3'...
I fixed almost everything except that I am having issue with using NSURLSession, Xcode is telling me that it has been renamed to URLSession, if I rename it Xcode will tell me:
use of undeclared type URLSession
Foundation is imported.
What is the issue?!
For example I am using it this way...
lazy var defaultSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "reCoded.BGDownload")
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
let session = URLSession(configuration: configuration, delegate: self, delegateQueue, queue: nil)
return session
}()
and even with the delegate methods the same issue.
Try using Foundation.URLSession where ever you use URLSession.
/Got it to work/ In some cases try to copy your code somewhere else then remove everything in your class that uses URLSession then type the session methods again and put back your copied code you should be fine.
Update your URLSessin functions with;
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.data.append(data as Data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
I can explain how but by playing around with the code I got this to work in SWIFT 3 after two days of frustration. I guess SWIFT 3 removed a lot of unnecessary words.
let task = Foundation.URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>)
Here's where I am right now. It's not perfect but works maybe half of the time.
First, in the class where my URLsession is defined:
import Foundation
class Central: NSObject, URLSessionDataDelegate, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
I don't think all of that is necessary, but there it is. Then here is the function that is called by my background fetch:
func getWebData() {
var defaults: UserDefaults = UserDefaults.standard
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myBGconfig")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
urlString = "https://www.powersmartpricing.org/psp/servlet?type=dayslider"
if let url = URL(string: urlString) {
let rateTask = backgroundSession.downloadTask(with: URL(string: urlString)!)
rateTask.taskDescription = "rate"
rateTask.resume()
}
When the task comes back:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) {
if downloadTask.taskDescription == "rate" { // I run 2 web tasks during the session
if let data = NSData(contentsOf: location) {
var return1 = String(data: data as! Data, encoding: String.Encoding.utf8)!
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.2){
var defaults: UserDefaults = UserDefaults.standard
defaults.set(myNumber, forKey: "electricRate") // myNumber is an extract of the text in returned web data
defaults.set(Date(), forKey: "rateUpdate")
defaults.synchronize()
self.calcSetting() //Calls another function defined in the same class. That function sends the user a notification.
let notificationName = Notification.Name("GotWebData")
NotificationCenter.default.post(name: notificationName, object: nil)
} // Closes the Dispatch
}
if session.configuration.identifier == "myBGconfig" {
print("about to invalidate the session")
session.invalidateAndCancel()
}
}
I haven't figured out yet how to kill the session when BOTH tasks have completed, so right now I kill it when either one is complete, with invalidateAndCancel as above.
And finally, to catch errors:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCompleteWithError: Error?) {
if downloadTask.taskDescription == "rate" {
print("rate download failed with error \(didCompleteWithError)")
}
if downloadTask.taskDescription == "other" {
print("other download failed with error \(didCompleteWithError)")
}
downloadTask.resume() // I'm hoping this retries if a task fails?
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
if let error = error as? NSError {
print("invalidate, error %# / %d", error.domain, error.code)
} else {
print("invalidate, no error")
}
}