Migration to Swift 2 "Use of unresolved identifier '***'" - swift

I'm upgrading my project from Swift 1.2 to Swift 2.
I use this occasion to upgrade lot of the lib that I use, in particular Alamofire.
But now I got this error on many of my request:
Use of unresolved identifier 'notifTypeJSON'
Here is the code of one of the func:
func getNotifications(failure failure: (NSError) -> (), success: ([Notification]) -> ()) {
Alamofire.request(Router.Notifications)
.validate()
.responseJSON { response in
if let error = response.result.error {
failure(error)
} else {
var json = JSON(response.data!)
let status = json["error"].intValue
if status != 0 {
failure(self.createError(status))
} else {
var notifications = [Notification]()
let notificationsList = json["notification"]
for (index: String, notifTypeJSON: JSON) in notificationsList {
if let notifJSON = notifTypeJSON[NotificationTypes.Generic.rawValue].dictionaryObject {
notifications.append(GenericNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Follow.rawValue].dictionaryObject {
notifications.append(FollowNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Comment.rawValue].dictionaryObject {
notifications.append(CommentNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Like.rawValue].dictionaryObject {
notifications.append(LikeNotification(json: notifJSON))
}
}
DDLogInfo("SeetyAPI getNotifications() success")
success(notifications)
}
}
}
}

In Swift 2 the way we loop over a dictionary with a tuple has changed: the types have to be in a separate tuple.
Example Before:
for (key:String, value:Int) in xxx {
Example After:
for (key, value):(String, Int) in xxx {
So for you, you would need to replace this:
for (index: String, notifTypeJSON: JSON) in notificationsList {
With this:
for (index, notifTypeJSON):(String, JSON) in notificationsList {

Related

Cannot convert return expression of type 'Task<[String], Error>' to return type '[String]' when using Task.init{}

I'm learning how to iterate with async and await in swift.
My current stage is on:
import Foundation
import SwiftUI
import Darwin
enum MyError: Error {
case genError
}
let myString : String = """
https://httpbin.org/anything
https://httpbin.org/ip
https://httpbin.org/user-agent
https://httpbin.org/headers
https://httpbin.org/get
https://httpbin.org/post
https://httpbin.org/put
https://httpbin.org/delete
https://httpbin.org/gzip
https://httpbin.org/status/:code
https://httpbin.org/response-headers?key=val
https://httpbin.org/redirect/:n
https://httpbin.org/relative-redirect/:n
https://httpbin.org/cookies
https://httpbin.org/cookies/set/:name/:value
https://httpbin.org/basic-auth/:user/:passwd
https://httpbin.org/hidden-basic-auth/:user/:passwd
https://httpbin.org/digest-auth/:qop/:user/:passwd
https://httpbin.org/stream/:n
https://httpbin.org/delay/:n
"""
func fetchInfo(for url: String, with index:Int) async throws -> String {
let request = URLRequest(url: URL(string: url)!)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
print("error found\n" + String(index) + "\n" + (url))
throw MyError.genError }
let thisOutput = String(data: data, encoding: .utf8)!
return thisOutput
}
func fetchOnebyOne(urls: String) async throws -> [String] {
var count : Int = 0
var finalArray : [String] = []
for item in myString.components(separatedBy: "\n") {
count += 1
do {
let thisThis: String = try await fetchInfo(for: item, with: count)
finalArray.append(thisThis)
}
catch {
print("\(count) ---------------------------- error found\n\n\n\n")
}
} // : for
return finalArray
}
Task{
let finalOutput = try await fetchOnebyOne(urls: myString)
print(finalOutput)
}
For the fetchOneByOne(), on a webpage, I know I can use async to get the same results, so I rewrite this function:
func fetchOnebyOne(urls: String) {
async {
var count : Int = 0
var finalArray : [String] = []
for item in myString.components(separatedBy: "\n") {
count += 1
do {
let thisThis: String = try await fetchInfo(for: item, with: count)
finalArray.append(thisThis)
}
catch {
print("\(count) ---------------------------- error found\n\n\n\n")
}
} // : for
} //: async
}
fetchOnebyOne(urls: myString)
I'm successful to get the same output. But I got a yellow warning in Xcode, the async should be replaced with Task.init. So I change the async to Task.init. The output still same.
But actually you can see, the fetchOnebyOne() doesn't return a [String] anymore. Because I cannot solve the warnings if I make it return [String]. I tried the below code:
func fetchOnebyOne(urls: String) -> [String] {
Task.init {
var count : Int = 0
var finalArray : [String] = []
for item in myString.components(separatedBy: "\n") {
count += 1
do {
let thisThis: String = try await fetchInfo(for: item, with: count)
finalArray.append(thisThis)
}
catch {
print("\(count) ---------------------------- error found\n\n\n\n")
}
} // : for
return finalArray
} // : Task
}
the warning is:
No 'init' candidates produce the expected contextual result type
'[String]'
and I did some research and change the first line to
func fetchOnebyOne(urls: String) -> [String] {
Task.init {() async throws -> [String] in
var count : Int = 0
var finalArray : [String] = []
for item in myString.components(separatedBy: "\n") {
count += 1
do {
let thisThis: String = try await fetchInfo(for: item, with: count)
finalArray.append(thisThis)
}
catch {
print("\(count) ---------------------------- error found\n\n\n\n")
}
} // : for
return finalArray
} // : Task
}
I got warning:
Cannot convert return expression of type 'Task<[String], Error>' to
return type '[String]'
I stuck here and cannot find useful information about Task.init, especially about the error - 'Task<[String], Error>' on internet.
I did all of this for knowledge, for learning swift. No practical use. Hope people here could help. Thanks.

What is the main difference between the "return function" and the "callback function"?

I think I am going to change all of my code functions from "callback functions" to "return functions". I don't like the "stairs" look of my code.
Do you think it is a good idea?
I don't understand the difference between the two (except for the asynchronous web service calls that force the use of the callback function in my code).
Callback function:
Declaration:
func methodToSelectData(strQuery : String, dataBase: String, completion: #escaping (_ result: [AnyObject]) -> Void) {
let arryToReturn : [AnyObject] = []
let contactDB = FMDatabase(path: String(methodToCreateDatabase(dataBase: dataBase)!.absoluteString) )
if (contactDB?.open())! {
let results:FMResultSet? = contactDB?.executeQuery(strQuery, withArgumentsIn: nil)
while results?.next() == true {
arryToReturn.add(results!.resultDictionary())
}
if arryToReturn.count == 0 {
completion(arryToReturn)
}
contactDB?.close()
} else {
print("Error: \(String(describing: contactDB?.lastErrorMessage()))")
}
completion(arryToReturn)
}
Usage:
DBHandler.sharedInstance.methodToSelectData(strQuery:"SELECT * FROM table", dataBase: "DB.db", completion: { resultQuery in
if (resultQuery.count > 0) {
...
}
})
Return function
Declaration:
func method2ToSelectData(strQuery : String, dataBase: String) -> [AnyObject] {
let arryToReturn : [AnyObject] = []
let contactDB = FMDatabase(path: String(methodToCreateDatabase(dataBase: dataBase)!.absoluteString) )
if (contactDB?.open())! {
let results:FMResultSet? = contactDB?.executeQuery(strQuery, withArgumentsIn: nil)
while results?.next() == true {
arryToReturn.add(results!.resultDictionary())
}
if arryToReturn.count == 0 {
return arryToReturn
}
contactDB?.close()
} else {
print("Error: \(String(describing: contactDB?.lastErrorMessage()))")
}
return arryToReturn
}
Usage:
let resultQuery = DBHandler.sharedInstance.method2ToSelectData(strQuery:"SELECT * FROM table", dataBase: "DB.db")
if (resultQuery.count > 0) {
...
}
What is the best way to use one or the other? I don't understand the subtlety very well.
It's really a matter of what you need in any given situation.
For something as simple as returning a piece of data, you can do just that:
// Definition //
func newString(firstHalf: String, secondHalf: String) -> String {
return firstHalf + secondHalf
}
// Usage //
print(newString(firstHalf: "Hello", secondHalf: "world"))
Something more complicated, like a data call, might need a completion handler or closure:
// Definition //
func getData(fromEndpoint endpoint: String, completion: (String) -> Void) {
let data = serverData(from: endpoint) //Makes the server request.
completion(data)
}
// Usage //
getData(fromEndpoint: "https://www.reddit.com/.json") { data in
doThings(with: data)
}
You don't necessarily need an asynchronous call to use a closure/callback, but it tends to be one of the most common use-cases for one. As you do more coding in Swift, you'll find more use-cases for each.

PromiseKit 6, Alamofire, Xcode 10.2/Swift 5

I'm having trouble writing the following code:
public
func
get(organization inSID: String)
-> Promise<Organization>
{
URLSession.showNetworkActivity()
return firstly
{
let req = buildRequest(path: "/api/\(inUUID)", date: inDate, headers: [.organizationSID : inSID])
self.mgr.request(req).responseJSON()
}
.map()
{ inData, inResp in
return Organization(sid: "")
}
.ensure
{
URLSession.hideNetworkActivity()
}
}
I get an error on firstly: Ambiguous reference to member 'firstly(execute:)'
After adding import PMKAlamofire to the top of my file, and being more explicit, I get this to compile:
public
func
get(organization inSID: String)
-> Promise<Organization>
{
URLSession.showNetworkActivity()
return firstly
{ () -> Promise<(json: Any, response: PMKAlamofireDataResponse)> in
let req = buildRequest(path: "/api/v2/organizations/\(inSID)", headers: [.organizationSID : inSID])
return self.mgr.request(req).responseJSON()
}
.map()
{ inResp in
return Organization(sid: "")
}
.ensure
{
URLSession.hideNetworkActivity()
}
}
Note the added explicit () -> Promise<(json: Any, response: PMKAlamofireDataResponse)> and the explicit return statement in the firstly closure. I don't know if this is now required by Swift 5 or it still can't properly infer types.

How to use the when function in Promisekit loop

I have an array of appointments and I'm trying to grab all of the photos for these appointments from our windows azure blob storage. First, I want to get the list of blobs with the associated appointmentId so I can download and store them properly afterwards.
I'm using PromiseKit but I'm not at all sure about how to use PromiseKit in a loop:
for appointment in appointments {
// Get blobs
}
Here's my code so far. Any help is greatly appreciated!
func getBlobsPromise(appointmentId: Int32) -> Promise<[BlobDownload]> {
return Promise { seal in
var error: NSError?
var blobDownloads = [BlobDownload]()
container = AZSCloudBlobContainer(url: URL(string: containerURL)!, error: &error)
if ((error) != nil) {
print("Error in creating blob container object. Error code = %ld, error domain = %#, error userinfo = %#", error!.code, error!.domain, error!.userInfo)
seal.reject(error!)
}
let prefix: String = "AppointmentFiles/\(appointmentId)"
container?.listBlobsSegmented(with: nil, prefix: prefix, useFlatBlobListing: true, blobListingDetails: AZSBlobListingDetails(), maxResults: 150) { (error : Error?, results : AZSBlobResultSegment?) -> Void in
if error != nil {
seal.reject(error!)
}
for blob in results!.blobs!
{
let blobInfo = blob as! AZSCloudBlob
if blobInfo.blobName.lowercased().contains("jpg") || blobInfo.blobName.lowercased().contains("jpeg") {
let blobDownload: BlobDownload = BlobDownload(appointmentId: Int(jobId), blob: blobInfo)
blobDownloads.append(blobDownload)
}
}
seal.fulfill(blobDownloads)
}
}
}
That returns the blobs as expected but I want to get all of the blobs for all of the appointments before proceeding. Here's what I tried (among other things):
func getBlobsForAllJobs(appointmentIds: [Int32]) -> Promise<[BlobDownload]> {
return Promise { seal in
let count = appointmentIds.count - 1
let promises = (0..<count).map { index -> Promise<[BlobDownload]> in
return getBlobsPromise(agencyCode: agencyCode, appointmentId: appointmentIds[index])
}
when(fulfilled: promises).then({ blobDownloads in
seal.fulfill(blobDownloads)
})
}
}
EDIT 1
I solved this using a DispatchGroup and completion handler. Here's the code in case someone is interested. If there are alternate (better) ways of doing this I'd love to hear them. I'm a c# guy just getting into Swift.
func getBlobsToDownload(appointmentIds: [Int32], completion: #escaping ([BlobDownload]) -> Void) {
var myBlobsToDownload = [BlobDownload]()
let myGroup = DispatchGroup()
for apptId in appointmentIds {
myGroup.enter()
getBlobs(appointmentId: apptId) { (blobDownloads) in
print("Finished request \(apptId)")
print("Blobs fetched from apptId \(apptId) is \(blobDownloads.count)")
for blobDownload in blobDownloads {
myBlobsToDownload.append(blobDownload)
}
myGroup.leave()
}
}
myGroup.notify(queue: .main) {
print("Finished all requests.")
completion(myBlobsToDownload)
}
}

How to use swiftyjson to parse json data to foundation object and loop through data

So I'm trying to get his raw json data and use it to ultimately be viewed in a table(so one table cell would be --> Emirates - $1588.77)
Problem: Having trouble parsing the JSON data.. alamofire apparently does it automatically? but im completely confused with the data types. I keep getting weird errors like 'doesnt have a member named subscript" (I've also got swiftyjson installed but aa non-swiftyjson solution should work as well.
Code:
request(qpxRequest).responseJSON { (request, response, json, error) -> Void in
if response != nil {
//println(response!)
}
if json != nil {
// 1. parse the JSON data into a Foundation object
// 2. Grab the data from the foundation object (so its can be looped though in a table)
}
{
trips = {
data = {
carrier = (
{
name = "Cathay Pacific Airways Ltd.";
},
{
name = Emirates;
},
{
name = "Ethiopian Airlines Enterprise";
},
{
name = "Qantas Airways Ltd.";
},
{
name = "South African Airways";
}
);
};
tripOption = (
{
saleTotal = "AUD1537.22";
},
{
saleTotal = "AUD1588.77";
},
{
saleTotal = "AUD1857.42";
},
{
saleTotal = "AUD1857.42";
},
{
saleTotal = "AUD1922.42";
}
);
};
}
-------- Edit.
Using this model.
class FlightDataModel {
var carrier: String
var price: String
init(carrier: String?, price: String?) {
self.carrier = carrier!
self.price = price!
}
}
How woudl I use your solution to add it to an array of FlightDataModel class
This my my attempt..
var arrayOfFlights : [FlightDataModel] = [FlightDataModel]()
if let tripOptions = trips["tripOption"] as? [[String:AnyObject]] {
for (index, tripOption) in enumerate(tripOptions) {
//println("\(index): " + (tripOption["saleTotal"]! as String))
self.arrayOfFlights[index].carrier = tripOption["saleTotal"]! as String
println("\(self.arrayOfFlights[index].carrier)")
}
Alamofire can do it, but you have to dig into your JSON structure. :)
Like this, using Alamofire's responseJSON method:
Alamofire.request(.GET, YOUR_URL, parameters: nil, encoding: .URL).responseJSON(options: NSJSONReadingOptions.allZeros) { (request, response, json, error) -> Void in
if let myJSON = json as? [String:AnyObject] {
if let trips = myJSON["trips"] as? [String:AnyObject] {
if let data = trips["data"] as? [String:AnyObject] {
if let carriers = data["carrier"] as? [[String:String]] {
for (index, carrierName) in enumerate(carriers) {
println("\(index): " + carrierName["name"]!)
}
}
}
if let tripOptions = trips["tripOption"] as? [[String:AnyObject]] {
for (index, tripOption) in enumerate(tripOptions) {
println("\(index): " + (tripOption["saleTotal"]! as! String))
}
}
}
}
}
Output:
0: Cathay Pacific Airways Ltd.
1: Emirates
...
0: AUD1537.22
1: AUD1588.77
...
It's a bit easier with SwiftyJSON indeed. And for diversity's sake, we'll use Alamofire's responseString method this time:
Alamofire.request(.GET, YOUR_URL, parameters: nil, encoding: .URL).responseString(encoding: NSUTF8StringEncoding, completionHandler: {(request: NSURLRequest, response: NSHTTPURLResponse?, responseBody: String?, error: NSError?) -> Void in
if let dataFromString = responseBody!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
let carriers = json["trips"]["data"]["carrier"].array
for (index, carrier) in enumerate(carriers!) {
println("\(index):" + carrier["name"].string!)
}
let tripOption = json["trips"]["tripOption"].array
for (index, option) in enumerate(tripOption!) {
println("\(index):" + option["saleTotal"].string!)
}
}
})
Output:
0: Cathay Pacific Airways Ltd.
1: Emirates
...
0: AUD1537.22
1: AUD1588.77
...
Note: I've used enumerate as an example for how getting the index of the content at the same time you get the content.