I'm trying to add a special JSONSerializable method to a dateTaken field so I wanted to extend Date. but I discovered that Date is a struct and cannot be extended, so NSDate. but I can't figure out how to get a DateTaken from a Date
class DateTaken : NSDate, JSONRepresentable {
static var formatter = DateFormatter()
var JSONRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" // ISO8601
DateTaken.formatter.timeZone = NSTimeZone(abbreviation: "UTC") as TimeZone!
return DateTaken.formatter.string(from:self as Date) as AnyObject
}
var JSONLocalTimeRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return DateTaken.formatter.string(from:self as Date) as AnyObject
}
}
let d = Date()
let dateTaken : DateTaken = DateTaken.init(timeInterval: 0, since: d)
// exception
(lldb) p NSDate.init(timeInterval: 0, since: dateTaken)
(NSDate) $R0 = 0x0000000156e6fdb0 2014-10-13 08:49:18 UTC
(lldb) p dateTaken.init(timeInterval: 0, since: dateTaken)
error: <EXPR>:3:1: error: 'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type
dateTaken.init(timeInterval: 0, since: dateTaken)
^
type(of: )
This isn't Java. We have extensions for this.
extension NSDate: JSONRepresentable {
static var formatter = DateFormatter()
var JSONRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" // ISO8601
DateTaken.formatter.timeZone = TimeZone(abbreviation: "UTC")
return DateTaken.formatter.string(from: self as Date) as AnyObject
}
var JSONLocalTimeRepresentation: AnyObject {
DateTaken.formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return DateTaken.formatter.string(from: self as Date) as AnyObject
}
}
I also suspect there's a bug in this caused by the sharing of formatter. After the timeZone is set for the first time in JSONRepresentation, JSONLocalTimeRepresentation will act like JSONRepresentation. I may be wrong, but I can't test it now. It's worth checking out.
Related
I searched here in SO but can't find useful thread to accomplish what I want. I'm new with Swift so I don't know much of this. So the scenario is I want to set a specific timezone which can be change by user with the app's settings. What I want is when the user changed the said timezone it should reflect through out the app. Yes I can set the timezone in each "DateFormatter" but I don't want to write it every time I create a DateFormatter.
Is there a way so when I create an instance of the DateFormatter the timezone is already set to the timezone the user selected? Is it possible to do this using an Extension of the DateFormatter?
What you can do is make extension of Date as of you are setting this TimeZone to DateFormatter to formate your date, So make two extension one for Date and one for String.
extension Date {
struct CustomDateFormatter {
static var currentTimeZone = TimeZone.current //Set default timeZone that you want
static func dateFormatter(withFormat format: String) -> DateFormatter {
let formatter = DateFormatter()
formatter.timeZone = CustomDateFormatter.currentTimeZone
formatter.dateFormat = format
return formatter
}
}
func toDateString(withFormat format: String) -> String {
return CustomDateFormatter.dateFormatter(withFormat: format).string(from: self)
}
}
extension String {
func toDate(withFormat format: String) -> Date? {
return Date.CustomDateFormatter.dateFormatter(withFormat: format).date(from: self)
}
}
Now when ever you need to set TimeZone or want to convert Date to String or vice versa use this extension like this.
//Set timezone
Date.CustomDateFormatter.currentTimeZone = TimeZone(abbreviation: "GMT")!
//get date from String
let date = "31-05-2017 07:30:05".toDate(withFormat: "dd-MM-yyyy HH:mm:ss")
//get string from date with specific format
let dateString = Date().toDateString(withFormat: "dd-MM-yyyy HH:mm:ss")
I hope I manage to ask this properly:
I am using Alamofire and SwiftyJSON for managing the JSON files I get from the server.
I have issues with understanding the type of response.result.value , how to cast it to an object I can construct it with SwiftyJSON's JSON(data: data) constructor.
This is my code for the request using Alamofire:
func performRequest() {
// parameters["retry_count"] = retryNum
if let _ = host, let path = path {
let request = Alamofire.request(HOST + path, method: method, parameters: parameters, headers: headers)
request.responseJSON { response in
print("-----")
print(response.response?.statusCode)
print("-----")
// check if responseJSON already has an error
// e.g., no network connection
if let json = response.result.value {
print("--------")
print(json)
print("--------")
}
guard response.result.error == nil else {
print(response.result.error?.localizedDescription ?? "Response Error")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure we got JSON and it's a dictionary
guard let json = response.result.value as? [String: AnyObject] else {
print("didn't get dictionary object as JSON from API")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure status code is 200
guard response.response?.statusCode == 200 else {
// handle status code
self.completionHandler?(response.result.isSuccess, nil)
return
}
self.completionHandler?(response.result.isSuccess, json)
RequestsQueue.sharedInstance.sema.signal()
}
}
This results with this print:
{
numOfShiftsInDay = 3;
shifts = (
{
endTime = "14:00";
startTime = "07:30";
},
{
endTime = "20:00";
startTime = "13:30";
},
{
endTime = "02:00";
startTime = "19:30";
}
);
}
this data type is a [String: AnyObject].
I want to use it to construct a SwiftyJSON JSON object since it is easier for me to parse the data using SwiftyJSON methods..
This is the code I try for parsing it and then using it but obviously it doesn't work:
let json = JSON(data: data)
I get this compilation error:
Cannot convert value of type '[String : AnyObject]?' to expected argument type 'Data'
So how should I go about this?
You need to use JSON(data) instead of JSON(data: data) because this init(data:) wants Data as argument.
Changed line
let json = JSON(data: data)
To
let json = JSON(data)
I got Encrypted data from API hit by below method
URLSession.shared.dataTask(with: url!)
converted data into JSON but still it is encrypted
var json = try(JSONSerialization.jsonObject(with: data!, options: .allowFragments))
converted it into string
let arr:String = json as! String
decrypted it
let jsonText = arr.fromBase64()//extension method, given end of question
now it is in Json Formate as below (this is only 1 record, there are more than 1 records in Json string)
{
"CompanyAlt_Key": 1,
"Company_Name": "XYZ LTD",
"TableName": "CompanyList"
},
I have a model of same type
public class CompanyList {
public var companyAlt_Key : Int?
public var company_Name : String?
public var tableName : String?
}
here is fromBase64 method
func fromBase64() -> String {
let data = NSData.init(base64Encoded: self, options: []) ?? NSData()
return String(data: data as Data, encoding: String.Encoding.utf8) ?? ""
}
I am facing problem to get the Json String into an array of type CompanyList class
Help would be appreciate
You'll need to convert your jsonString to data first:
let jsonData = jsonString.data(using: .utf8)!
The convert the data to an array
let array = JSONSerialization.jsonObject(with: jsonData, options: nil) as? [[String: Any]]
Then iterate through the array…
let companies = array?.map {
return CompanyList(dictionary: $0)
}
Implement an init method for your CompanyList, passing in a dictionary for each record in your response…
public class CompanyList {
public var companyAlt_Key : Int?
public var company_Name : String?
public var tableName : String?
init(dictionary: [String: Any]) {
companyAlt_Key = dictionary["companyAlt_Key"] as? Int
company_Name = dictionary["company_Name"] as? String
tableName = dictionary["tableName"] as? String
}
}
You can also use this to validate the data. If the fields in your class are non-optional, you can use an optional init as follows…
public class CompanyList {
public var companyAlt_Key : Int
public var company_Name : String
public var tableName : String
init?(dictionary: [String: Any]) {
guard let companyAlt_Key = dictionary["companyAlt_Key"] as? Int,
let company_Name = dictionary["company_Name"] as? String,
let tableName = dictionary["tableName"] as? String else {
return nil
}
self.companyAlt_Key = companyAlt_Key
self.company_Name = company_Name
self.tableName = tableName
}
}
If you're using an optional init, use flatMap to ensure you don't have any optional elements in your array…
let companies = array?.flatMap {
return CompanyList(dictionary: $0)
}
I am updating my code to swift3.0 but getting ambiguous refrence to member? What wrong i might be doing. Here is the method I am getting error in.
open class func parseJsonTenantList(_ list: [NSDictionary]?, strElementName: String, attrName1: String, attrNameValue2: String) -> [TenantRegister]
{
var renantList: [TenantRegister] = []
var key: String?
if let dict : [NSDictionary] = list {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1: AnyObject = dict[i].value(forKey: attrName1)
{
key = s1 as? String
}
if let s2: AnyObject = dict[i].value(forKey: attrNameValue2)
{
value = s2 as? String
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
return renantList
}
The issue is you are using NSDictionary, to solved your problem simply cast the list to Swift's native type [[String:Any]] and then use subscript with it instead of value(forKey:)
if let dict = list as? [[String:Any]] {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1 = dict[i][attrName1] as? String
{
key = s1
}
if let s2 = dict[i][attrNameValue2] as? String
{
value = s2
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
In Swift use native type Dictionary [:] and Array [] instead of NSDictionary and NSArray to overcome this type of issues.
I have this simple Swift3 code where if complains about [Channel]:
var channels = [Channel]()
....
for (_, json) in json["entities"] {
let channel = Channel(json: json)
self.channels += [channel]
^ Cannot convert value of type [Channel] to expected argument type inout _
}
This is the channel class:
class Channel {
var uuid: String
var title: String?
var isPublic: Bool
init(uuid: String) {
self.uuid = uuid
self.title = ""
self.isPublic = false
}
init?(json: JSON) {
self.uuid = json["uuid"].stringValue
self.title = json["title"].stringValue
self.isPublic = json["public"].boolValue
}
}
Some postings indicate the message may be related to closures but I can't see a closure here.
How to fix this error in a simple for loop?
The error is coming from the += statement. Channel(json:) is a failable initializer (note the init?) and it returns an Optional which must be unwrapped. So you are trying to apply += to [Channel] and [Channel?] and the types are not compatible. The error message is less than clear, because of the way += is defined.
public func +=<C : Collection>(lhs: inout [C.Iterator.Element], rhs: C)
Swift is unable to reconcile the type of rhs which is [Channel?] with the type of lhs which is [Channel].
The fix is to unwrap the Channel? returned by the failable initializer Channel(json:):
var channels = [Channel]()
....
for (_, json) in json["entities"] {
if let channel = Channel(json: json) {
self.channels += [channel]
}
}
#andig. [1] your example cannot be compiled because both json and JSON are not defined. [2] Now let's assume you made a failable initializer init?(json: JSON){} correctly within Class Channel then vacawama's explanation is correct. Changing your code to the below should work.
for (_, json) in json["entities"] {
if let channel = Channel(json: json) {
self.channels += [channel!]
}
}