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
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.
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.
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.
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)
}
}
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.