I am getting one more issue regarding write block - 'SharedRealm.realm.write()' - Error - "Call can throw, but it is not marked with 'try' and the error is not handled", how it can be handled.
This is the code:
func addItems(items:[Item]) {
do {
let rlm = try Realm()
rlm.write { () -> Void in
for item in items {
rlm.add(item, update: true)
}
}
}
catch let rlmError {
print("Realm() generated error: \(rlmError)")
}
}
Still I am getting the same error - ":13: Call can throw but is not marked with 'try'"
rlm.write { ... } might throw an error as well as the initializer so you will need to add the try operator in front of this invocation, too.
Since Realm() might throw an error you'll need to wrap it in a do { try } catch block
extension Realm {
public class func saveClosure(dbClosure: (Realm)->()) {
do {
let rlm = try Realm()
rlm.write { () -> Void in
dbClosure(rlm)
}
}
catch let rlmError{
print("Realm() generated error: \(rlmError)")
}
}
}
Checkout https://gist.github.com/edwardIshaq/b5810ab35c30df10af24
Related
I am trying to chain some API calls and I think I am confusing some concepts. Would love some clarification & code samples.
I have implemented these functions...
func promiseFetchPayments(for accountId: String) -> Promise <[OperationResponse]> {
return Promise <[OperationResponse]> { seal in
payments(for: accountId) { (records, error) in
if let recs = records {
seal.resolve(.fulfilled(recs))
return
}
if let e = error {
seal.reject(e)
return
}
}
}
}
and
func payments(for accountId: String, completion: #escaping (_ records: [OperationResponse]?, _ error: Error?) -> Void) {
stellar.payments.getPayments(
forAccount: accountId,
order: Order.descending,
limit: 10
) { response in
switch response {
case .success(let paymentsResponse):
DispatchQueue.main.async {
completion(paymentsResponse.records, nil)
}
case .failure(let error):
DispatchQueue.main.async {
completion(nil, error)
}
}
}
}
I am trying to use it like so:
firstly {
promiseFetchPayments(for: "XXX")
}.done { records in
print(records)
} .catch { error in
print(error)
}
Now this actually ^^^ works OK!!!! My problem is I want to be able to change done to then and be able to chain another function / response or many more.
But the error I keep getting is:
Cannot conform to Thenable.
I am looking for something very similar to this (I know the syntax isn't right just logically follow the chain....
firstly {
stellar.promiseFetchPayments(for: "")
}.done { records in
print(records)
}.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
Is this actually possible? Can't seem to get any tutorials on the interwebs to compile. Seems Swift compiler really doesn't like PMK syntax or something.
Any ideas?
The problem is because you're chaining off of a done, which doesn't like that you're trying to then do a call to then off of that.
Instead, you'll want to save the promise and use it for the later calls. You can do something like this:
let promise = firstly {
stellar.promiseFetchPayments(for: "")
}
promise.done { records in
print(records)
}
promise.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
You can even return that promise from a method to use in other places, or pass it around to another method.
I am finding extremely hard to use PromiseKit 6.13.1 in an apparently simple situation.
I have the following two functions returning a Promise<String> but I cannot seem to find a way to use them with ```firstly{}.then{} syntax:
func promiseGetJWTToken() -> Promise<String> {
return Promise<String> { seal in
let error: Error = NSError(domain: "", code: 2000)
getJWTToken { tokenJWT in
guard let tokenJWT = tokenJWT else {
seal.resolve(.rejected(error))
return
}
seal.resolve(.fulfilled(tokenJWT))
}
}
}
func promiseGetBEToken() -> Promise<String> {
return Promise<String> { seal in
let error: Error = NSError(domain: "", code: 2000)
getBEToken { result in
switch result {
case .success(let response):
guard let tokenBE = response.token else {
seal.resolve(.rejected(error))
return
}
seal.fulfill(tokenBE)
case .failure(let error):
debugPrint(error)
seal.resolve(.rejected(error))
case .none:
seal.resolve(.rejected(error))
}
}
}
}
When I try to use the following as follows
firstly {
promiseGetJWTToken()
}.then { tokenJWT in
// no need go on because I have issues already here
}
I receive:
Type '()' cannot conform to 'Thenable'; only struct/enum/class types can conform to protocols
I have also tried, which comes from autocompletion:
promiseGetJWTToken().then { token -> Thenable in
// no need go on because I have issues already here
}
In this case I receive:
Protocol 'Thenable' can only be used as a generic constraint because it has Self or associated type requirements
I decided to give PromiseKit a try because I have three network calls dependent on each other on cascade, but I wouldn't expect this to be so hard. Can anyone show me what am I doing wrong?
The error message is misleading; the real issue is that the .then closure should return a new Thenable. In your examples, the .then closures are empty.
Or just use .done, if not chaining promises.
They replaced that usage of then { } with done { }.
firstly {
promiseGetJWTToken()
}.done { tokenJWT in
// use your token
}
You can try
firstly {
promiseGetJWTToken()
}.then { tokenJWT -> Promise<String> in
return promiseGetBEToken()
}
or
firstly {
promiseGetJWTToken()
}.then { tokenJWT -> Promise<String> in
promiseGetBEToken()
return Promise.value(tokenJWT)
}
I have the following class to return a list of NOAA weather observation stations. I am using it to learn how to deal with XML. However, I am getting a "Use of undeclared type 'wxObservationStations'" as an error at func returnWxStation() -> (wxObservationStations). I am using SWXMLHash to deserialize the XML, but I don't think that is my problem (though I am just learning, so it may be).
class WxObservationStations {
let wxObserStationsURL = URL(string: "http://w1.weather.gov/xml/current_obs/index.xml")
struct wxStation: XMLIndexerDeserializable {
let stationName: String
let stationState: String
let latitude: Double
let longitude: Double
static func deserialize(_ node: XMLIndexer) throws -> wxStation {
return try wxStation(
stationName: node["station_name"].value(),
stationState: node["state"].value(),
latitude: node["latitude"].value(),
longitude: node["longitude"].value()
)
}
}
public var wxObservationStations: [wxStation] = []
private func getStationNamesAndLocations(url: URL, completion:#escaping (XMLIndexer) -> ()) {
Alamofire.request(url).responseJSON { response in
// print(response) // To check XML data in debug window.
let wxStationList = SWXMLHash.parse(response.data!)
print(wxStationList)
completion(wxStationList)
}
}
//The error is here:
func returnWxStation() -> (wxObservationStations) {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
}
Any thoughts? The variable is declared in the class, and I would like to use it to send the data back to the requesting object. Thanks in advance.
The wxObservationStations is not a type, so it doesn't make sense to say
func returnWxStation() -> (wxObservationStations) { ... }
You're returning self.wxObservationStations, which is of type [wxStation]. So the method declaration should be
func returnWxStation() -> [wxStation] { ... }
By the way, your life will be much easier if you stick with Cocoa naming conventions, namely types should start with upper case letters. So rather than the wxStation type, I'd suggest WxStation.
Your following method will not achieve what you want:
func returnWxStation() -> [wxStation] {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
The method getStationNamesAndLocations runs asynchronously and your self.wxObservationStations will not be populated by the time that returnWxStation actually returns.
The entire purpose of the getStationNamesAndLocations method is to provide you a nice asynchronous method with completion handler. I would excise returnWxStation from your code entirely. Or do something like:
func returnWxStation(completionHandler: ([wxStation]?) -> Void) {
getStationNamesAndLocations(url: wxObserStationsURL!) { serverResponse in
do {
let stations = try serverResponse["wx_station_index"]["station"].value()
completionHandler(stations)
} catch {
completionHandler(nil)
}
}
}
And you'd use it like so:
returnWxStation() { stations in
guard let stations = stations else {
// handle error here
return
}
// use `stations` here
}
// but not here
I am trying to do Error handling using swift 2.1,
The following scenario,
var test: NSArray! = ["Test1", "Test2"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
do{
try testing()
} catch {
print("error")
}
}
func testing() throws {
print(test.objectAtIndex(7))
}
At the above case, My application crashes & saying terminating with uncaught exception of type NSException but I am expecting the control is supposed to be inside the Catch block instead of crash.
May I know the solution for this. Can anybody please do the needful on this
The only way you can make that work is to throw an error (as Eric D. pointed out in the comments):
Playground:
enum ArrayError : ErrorType{
case OutOfBounds
}
class SomeClass {
var test: NSArray! = ["Test1", "Test2"]
func testCode() {
do{
try testing(3)
} catch let error{
print("error = \(error)") // for index 3 this would print "error = OutOfBounds\n"
}
}
func testing(index: Int) throws -> String {
guard index < test.count else{
throw ArrayError.OutOfBounds
}
return test[index] as! String
}
}
let sC = SomeClass()
sC.testCode()
Why Does Realm use try! so frequently? It seems like if you're certain your call won't fail then you should not design it to throw - no?
Here is an example, from the Swift page on realm.io:
// Get the default Realm
let realm = try! Realm()
or
// Persist your data easily
try! realm.write {
realm.add(myDog)
}
To me this implies they will never fail so why have the constructor or write() throw?
If you're referring to the examples in the Realm Swift Docs, I suspect try! is used liberally for the sake of brevity. The user is given a quick and dirty overview of core concepts without too much mental overhead.
You probably will encounter errors at some point in your journey using Realm. You'll notice later on in the docs, in the Realms > Error Handling section that a do-catch example is given.
do {
let realm = try Realm()
} catch let error as NSError {
// handle error
}
To me, it's implied that the code examples from the docs are not necessarily production-quality, and the user is encouraged to use the relevant error-handling features of Swift.
From the Realm Swift 2.1.0 guide in the Writes section:
Because write transactions could potentially fail like any other disk
IO operations, both Realm.write() & Realm.commitWrite() are marked as
throws so you can handle and recover from failures like running out of
disk space. There are no other recoverable errors. For brevity, our
code samples don’t handle these errors but you certainly should in
your production applications.
Source: https://realm.io/docs/swift/latest/#writes
The way I deal with this issue is by creating a DatabaseManager class, that handles the unlikely event of realm throwing an error:
public class DatabaseManager {
static var realm: Realm {
get {
do {
let realm = try Realm()
return realm
}
catch {
NSLog("Could not access database: ", error)
}
return self.realm
}
}
public static func write(realm: Realm, writeClosure: () -> ()) {
do {
try realm.write {
writeClosure()
}
} catch {
NSLog("Could not write to database: ", error)
}
}
}
Thanks to that solution the code looks much cleaner whenever I want to read from realm or write to db :)
DatabaseManager.write(realm: realm) {
let queryResult = self.realm.objects(Cookies.self).filter("cookieId == %#", cookieId)
let cookie = queryResult.first
cookie?.expirationDate = expirationDate as NSDate?
}
Why creating a class with static func when we can create an extension of Realm?
extension Realm {
static func safeInit() -> Realm? {
do {
let realm = try Realm()
return realm
}
catch {
// LOG ERROR
}
return nil
}
func safeWrite(_ block: () -> ()) {
do {
// Async safety, to prevent "Realm already in a write transaction" Exceptions
if !isInWriteTransaction {
try write(block)
}
} catch {
// LOG ERROR
}
}
}
Usage Example
Old unsafe code:
let realm = try! Realm()
try! realm.write {
// Your write transaction body
}
Safety refactor with this extension:
guard let realm = Realm.safeInit() else {
// Track Error
return
}
realm.safeWrite {
// Your write transaction body as before
}
From the Realm documentation:
You may have noticed so far that we have initialized access to our realm variable by calling Realm(). That method returns a Realm object that maps to a file called “default.realm” under the Documents folder (iOS) or Application Support folder (OS X) of your app.
Any time you interact with the file system you risk encountering errors such as permissions problems or insufficient disk space. Success is not certain.
So if for any reason Realm is unable to create or write to the realm file, these methods you cite would indeed throw an exception.
I create this for simple init call
import RealmSwift
// MARK: - RealmDB
/// RealmDB import realm in foundation, and add is format for refactoring catch
public class RealmDB {
/// Realm
public static var realm: Realm? {
do {
return try Realm()
} catch let error {
NotificationCenter.default.post(name: .logError, object: "Could not access database: \(error)")
return nil
}
}
/// Write in Realm
///
/// - Parameter writeClosure: Write Closure
public static func write(writeClosure: #escaping (_ realm: Realm) -> ()) {
do {
try self.realm?.write {
// self.realm has so can `!`
writeClosure(self.realm!)
}
} catch let error {
NotificationCenter.default.post(name: .logError, object: "Could not write database: \(error)")
}
}
}