I have the following code in my program.
var detailItem: RSSItem? {
didSet {
self.configureView()
}
}
func configureView() {
if let item: RSSItem = self.detailItem
{
if let webView = self.itemWebView
{
if let templateURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("template", ofType: "html")!)?
{
if var template = NSString(contentsOfURL: templateURL, encoding: NSUTF8StringEncoding)?
{
if let title = item.title
{
template = template.stringByReplacingOccurrencesOfString("###TITLE###", withString: title)
}
if let content = item.content
{
template = template.stringByReplacingOccurrencesOfString("###CONTENT###", withString: content)
}
else if let description = item.itemDescription
{
template = template.stringByReplacingOccurrencesOfString("###CONTENT###", withString: description)
}
if let date = item.pubDate
{
var formatter = NSDateFormatter()
formatter.dateFormat = "MMM dd, yyyy"
template = template.stringByReplacingOccurrencesOfString("###DATE###", withString: formatter.stringFromDate(date))
}
if let author = item.author
{
template = template.stringByReplacingOccurrencesOfString("###AUTHOR###", withString: author)
}
webView.loadHTMLString(template, baseURL: nil)
}
}
else
{
if let content = item.content
{
webView.loadHTMLString(content, baseURL: nil)
}
else if let description = item.itemDescription
{
webView.loadHTMLString(description, baseURL: nil)
}
}
}
}
}
On the line:
if let templateURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("template", ofType: "html")!)?
I get the following error:
-Could not find an overload for 'pathForResource' that accepts the supplied arguments
Can someone explain this error to me and maybe suggest a solution. I have been reading through various searches on Google but can't quite seem to get what I need.
Thanks in advance.
NSURL(fileURLWithPath:) is now return a NSURL so just don't use if let here.
#available(iOS 2.0, *)
init(fileURLWithPath path: String, isDirectory isDir: Bool)
init(fileURLWithPath path: String) // Better to use initFileURLWithPath:isDirectory: if you know if the path is a directory vs non-directory, as it saves an i/o.
#available(iOS 2.0, *)
class func fileURLWithPath(path: String, isDirectory isDir: Bool) -> NSURL
class func fileURLWithPath(path: String) -> NSURL // Better to use fileURLWithPath:isDirectory: if you know if the path is a directory vs non-directory, as it saves an i/o.
for the line: if var template = NSString(contentsOfURL: templateURL, encoding: NSUTF8StringEncoding)?
Its decleration is:
convenience init(contentsOfURL url: NSURL, encoding enc: UInt) throws
convenience init(contentsOfFile path: String, encoding enc: UInt) throws
So it not returns optional NSString either, so you cannot use if let again. You have to use do-try-catch here.
Apple's document about this:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508
Also, you should use multiple unwrapping optional:
if let x = OptionalX, y = OptionalY { ... }
Your code is a pyramid of doom :)
Related
Xcode newbie.(me.dumb)
First time poster.
I have the following code and it tested flawlessly in Xcode Playground(h/t D.Budd Data Science).
I want to turn the code into a MacOS app and I cannot fathom the correct Project and Settings
to do this or do I need to import the Playground into a Project and modify it?
Also this app needs to run in the background (ie, no Window/UI) as this would destroy the current window scene(MagicMirror), sort of like an Automater app can do. It would have an app icon, of course.
Thanks so much for any help.
import Cocoa
extension String{
// get the file name from string
func fileName() -> String{
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
// get the file extension from a string
func fileExtension() -> String{
return URL(fileURLWithPath: self).pathExtension
}
}
func readFile(inputFile: String) -> String{
//split the file extension and file name
let fileExtension = inputFile.fileExtension()
let fileName = inputFile.fileName()
//get the file URL
let fileURL = try! FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let inputFile = fileURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
//get the data
do{
let saveData = try String(contentsOf: inputFile)
return saveData
} catch {
return error.localizedDescription
}
}
func writeFile(outputFile: String, stringData: String){
let fileExtension = outputFile.fileExtension()
let fileName = outputFile.fileName()
//get the fileURL
let fileURL = try! FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let outputFile = fileURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
//save the data
guard let data = stringData.data(using: .utf8) else {
print("No Can Do")
return
}
do {
try data.write(to: outputFile)
print("data written: /data")
} catch {
print(error .localizedDescription)
}
let myData = readFile(inputFile: "test.txt")
writeFile(outputFile: "output.txt", stringData: myData)
}
Swift (v 5/5.1) newbie here, having a hard time with Codables...hoping to get some advise from the experts here.
Okay, I have a simple dictionary from struct where the key is a string. I want to store the dictionary in UserDefaults (and later retrieve). There are some quite similar questions here, but these are mainly addressing nested struct's.
First attempt (error handling removed for simplicity):
public struct PriceStruct:Codable {
var myPrice: Double
var myTime: TimeInterval
var selected: Bool
var direction: Int
var myHigh, myLow: Double
enum CodingKeys: String, CodingKey {
case myPrice = "myPrice"
case myTime = "myTime"
case selected = "selected"
case direction = "direction"
case myHigh = "myHigh"
case myLow = "myLow"
}
}
var myPrices: [String: PriceStruct] = [:]
// [fill myPrices with some data...]
func savePrices() {
// error: Attempt to set a non-property-list object
UserDefaults.standard.set(myPrices, forKey: "prices")
}
func loadPrices() {
// obviously this doesn't work either
let myPrices = UserDefaults.standard.data(forKey: "prices")
}
While I assumed from the documentation, that UserDefaults is capable of storing dictionaries, it doesn't - at least for me.
Next thing I tried was using JSONEncoder like this:
// this time with prior JSON encoding
func savePrices() {
// this works
let json = try! JSONEncoder().encode(myPrices)
UserDefaults.standard.set(json as Data, forKey: "prices")
}
func loadPrices() {
// this doesn't work
let json = UserDefaults.standard.data(forKey: "prices")
let decoder = JSONDecoder()
let decoded = try! decoder.decode(PriceStruct.self, from json!)
}
Unfortunately I'm getting an error when trying to load data back from UserDefaults:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "myPrice", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"myPrice\", intValue: nil) (\"myPrice\").", underlyingError: nil))
Other variants I tried is converting the encoded JSON to an UTF8 encoded string and storing/retrieving this one:
func savePrices() {
// this works too
let json = try! JSONEncoder().encode(myPrices)
UserDefaults.standard.set(String(data: json, encoding: .utf8), forKey: "prices")
}
func loadPrices() {
// and this doesn't work either
let json = UserDefaults.standard.string(forKey: "prices")!.data(using: .utf8)
}
So, from the error raised, CodingKeys seems to be the root of the problem. I tried to switch over using NSKeyedArchiver and NSKeyedUnarchiver` with no success.
I'm really wondering if there is a simple/universal solution to save/load a Dictionary in UserDefaults?
All your comments and suggestions are appreciated. Thanks!
I tried with the below code in my project that will work for me.
User Model
public protocol UserModel: Codable, PrimaryKey {
var id: String { get }
var firstName: String? { get }
var lastName: String? { get }
var userName: String? { get }
var emails: [String] { get }
}
public struct User: UserModel {
public let id: String
public let firstName: String?
public let lastName: String?
public let userName: String?
public let emails: [String]
public enum CodingKeys: String, CodingKey {
case id = "Id"
case firstName = "FirstName"
case lastName = "LastName"
case userName = "UserName"
case emails = "Emails"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
self.id = try container.decode(String.self, forKey: .id)
self.firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
self.lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
self.userName = try container.decodeIfPresent(String.self, forKey: .userName)
self.emails = try container.decodeIfPresent([String].self, forKey: .emails) ?? []
}
catch let error {
debugPrint(error)
throw error
}
}
}
I have stored in userDefault using below way
User Data Class
class UserData: NSObject
{
let userDefaultKey = "user_information"
var userData: User?
func getDictionary() -> [String: Data]
{
var dicInfo = [String: Data]()
do
{
let _userData = try JSONEncoder().encode(userData)
dicInfo["userData_default"] = _userData
}catch let error{
print("error while save data in User Default: \(error.localizedDescription)")
}
return dicInfo
}
func saveToDefault()
{
let userDefault = UserDefaults.standard
userDefault.set(getDictionary(), forKey: userDefaultKey)
userDefault.synchronize()
}
func loadFromDefault()
{
let userDefault = UserDefaults.standard
if let dicInfo = userDefault.object(forKey: userDefaultKey) as? [String: Data]
{
update(dicInfo)
}
}
func update(_ dictionaryInfo: [String: Data])
{
do
{
if let _userData_data = dictionaryInfo["userData_default"]
{
if let _userData = try? JSONDecoder().decode(User.self, from: _userData_data) {
userData = _userData
}
}
saveToDefault()
}catch let error{
print("error while load From Default data in User Default: \(error.localizedDescription)")
}
}
}
Hope this will help you.
Good evening,
I am trying to call data to post onto a card very similar to tinder. When I run my code, everything works and I see the print statement in the console. However the card view shows a default image with default text.
I was wondering if anyone has encountered this issue and could possibly help explain what I am doing wrong.
fileprivate func fetchUsersFromDatabase() {
Database.database().reference().child("JobPost").observeSingleEvent(of: .value, with: {(Snapshot) in
if let eachDict = Snapshot.value as? NSDictionary{
for each in eachDict{
//I think the issue is caused by the let post = poster
let post = Poster(dictionary: Snapshot.value as! [String : Any])
self.cardViewModels.append(post.toCardViewModel())
print(each.value )
}
}
self.setupDummyCards()
}, withCancel: {(Err) in
})
}
// the struct is in an extension file.
struct Poster: ProducesCardViewModel{
var jobName : String?
var price: Int?
var postName: String?
var ImageUrl1: String?
var uid: String?
init(dictionary: [String: Any]) {
self.price = dictionary["cost"] as? Int
self.jobName = dictionary["category"] as? String
self.postName = dictionary["description"] as? String ?? ""
self.ImageUrl1 = dictionary["JobImageUrl"] as? String ?? ""
self.uid = dictionary["fromId"] as? String ?? ""
}
func toCardViewModel() -> CardViewModel {
let attributedText = NSMutableAttributedString(string: jobName ?? "", attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)])
let priceString = price != nil ? "\(price!)" : "N\\A"
attributedText.append(NSAttributedString(string: " \(priceString)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)]))
let jobString = jobName != nil ? jobName! : "Not available"
attributedText.append(NSAttributedString(string: "\n\(jobString)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)]))
return CardViewModel(imageNames: [ImageUrl1 ?? "" ], attributedString: attributedText, textAlignment: .left)
}
}
Example
// toCardViewModel
import UIKit
protocol ProducesCardViewModel {
func toCardViewModel() -> CardViewModel
}
class CardViewModel {
let JobimageName: [String]
let attributedString: NSAttributedString
let textAlignment: NSTextAlignment
init(imageNames: [String], attributedString: NSAttributedString, textAlignment: NSTextAlignment) {
self.JobimageName = imageNames
self.attributedString = attributedString
self.textAlignment = textAlignment
}
fileprivate var imageIndex = 0 {
didSet {
let imageName = JobimageName[imageIndex]
let image = UIImage(named: imageName)
imageIndexObserver?(imageIndex, image)
}
}
var imageIndexObserver: ((Int, UIImage?) -> ())?
func advanceToNextPhoto() {
imageIndex = min(imageIndex + 1, JobimageName.count - 1)
}
func goToPreviousPhoto() {
imageIndex = max(0, imageIndex - 1)
}
}
Thank you in advance!
// P.S I previously posted this question without lack of sufficient detail. I decided to just re post it with the quality material. I really appreciate your time.
I have figured out an answer and it was quite obvious to me. I noticed that
print(each.value )
would print the value, so I just substituted
let post = Poster(dictionary: Snapshot.value as! [String : Any])
to
let post = Poster(dictionary: each.value as! [String : Any])
and everything started to work just fine!
My app works perfectly fine with iOS 8 but yesterday I upgraded to iOS 9 and Xcode 7 then my app crashes. The error message is cannot invoke 'dataTaskwithURL' with an argument list of type '(NSURL, (_, ,)throws -> Void)'. I googled it and found a similar question here but the solution didn't really work (the solution was to add the do/catch blocks around the code). Can anyone help me with mine problem? Thank you!!!
Here's my code
import UIKit
import Foundation
import CoreLocation
class GoogleDataProvider {
let apiKey = "AIzaSyCQo-clIkek87N99RVh2lmFX9Mu9QPhAtA"
let serverKey = "AIzaSyBzmv7wPFcPAe1ucy5o6dqaXnda9i9MqjE"
var photoCache = [String:UIImage]()
var placesTask = NSURLSessionDataTask()
var session: NSURLSession {
return NSURLSession.sharedSession()
}
func fetchPlacesNearCoordinate(coordinate: CLLocationCoordinate2D, radius:Double, types:[String],keyword:String, completion: (([GooglePlace]) -> Void)) -> ()
{
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=\(serverKey)&location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(radius)&keyword=\(keyword)&rankby=prominence&sensor=true"
let typesString = types.count > 0 ? types.joinWithSeparator("|") : "food"
urlString += "&types=\(typesString)"
urlString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
if placesTask.taskIdentifier > 0 && placesTask.state == .Running {
placesTask.cancel()
}
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
do{
//******************Here's the line that displays error
placesTask = session.dataTaskWithURL(NSURL(string: urlString)!) {
(data, response, error) -> Void in
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
var placesArray = [GooglePlace]()
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? NSDictionary {
if let results = json["results"] as? NSArray {
for rawPlace:AnyObject in results {
let place = GooglePlace(dictionary: rawPlace as! NSDictionary, acceptedTypes: types)
placesArray.append(place)
if let reference = place.photoReference {
self.fetchPhotoFromReference(reference) { image in
place.photo = image
}
}
}
}
}
dispatch_async(dispatch_get_main_queue()) {
completion(placesArray)
}
}
}catch{
}
placesTask.resume()
}
func fetchDirectionsFrom(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D, completion: ((String?) -> Void)) -> ()
{
let urlString = "https://maps.googleapis.com/maps/api/directions/json?key=\(serverKey)&origin=\(from.latitude),\(from.longitude)&destination=\(to.latitude),\(to.longitude)&mode=walking"
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
do{
//******************************Here too ****************************
session.dataTaskWithURL(NSURL(string: urlString)!) {
(data, response, error) -> Void in
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
var encodedRoute: String?
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? [String:AnyObject] {
if let routes = json["routes"] as AnyObject? as? [AnyObject] {
if let route = routes.first as? [String : AnyObject] {
if let polyline = route["overview_polyline"] as AnyObject? as? [String : String] {
if let points = polyline["points"] as AnyObject? as? String {
encodedRoute = points
}
}
}
}
}
dispatch_async(dispatch_get_main_queue()) {
completion(encodedRoute)
}
}.resume()
}catch{
}
}
}
Sorry this is my first time posting the code style is a little bit confusing sorry about the indentation mess :)
Thanks again!!!
dataTaskWithURL:completionHandler: does not throw error.
Put do and catch inside dataTaskWithURL method.
for example:
session.dataTaskWithURL(NSURL(string: urlString)!) {
(data, response, error) -> Void in
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
var encodedRoute: String?
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? [String:AnyObject] {
if let routes = json["routes"] as AnyObject? as? [AnyObject] {
if let route = routes.first as? [String : AnyObject] {
if let polyline = route["overview_polyline"] as AnyObject? as? [String : String] {
if let points = polyline["points"] as AnyObject? as? String {
encodedRoute = points
}
}
}
}
}
} catch {
}
dispatch_async(dispatch_get_main_queue()) {
completion(encodedRoute)
}
}.resume()
I just updated to Xcode7 and am trying to switch my project to using the Swift 2.0 Syntax when I ran into this error in a file from an open source library I'm using. Here's the relevant code:
public lazy var cookies:[String:NSHTTPCookie] = {
let foundCookies: [NSHTTPCookie]
if let responseHeaders = (self.response as? NSHTTPURLResponse)?.allHeaderFields {
foundCookies = NSHTTPCookie.cookiesWithResponseHeaderFields(responseHeaders, forURL:NSURL(string:"")!) as! [NSHTTPCookie]
} else {
foundCookies = []
}
var result:[String:NSHTTPCookie] = [:]
for cookie in foundCookies {
result[cookie.name] = cookie
}
return result
}()
The error reads: Cannot assign a value of type '[NSHTTPCookie]' to a value of type '[NSHTTPCookie]'
Is there something I'm missing here?
Change your code to this:
public lazy var cookies:[String:NSHTTPCookie] = {
let foundCookies: [NSHTTPCookie]
if let responseHeaders = (self.response as? NSHTTPURLResponse)?.allHeaderFields as? [String:String] {
foundCookies = NSHTTPCookie.cookiesWithResponseHeaderFields(responseHeaders, forURL:NSURL(string:"")!)
} else {
foundCookies = []
}
var result:[String:NSHTTPCookie] = [:]
for cookie in foundCookies {
result[cookie.name] = cookie
}
return result
}()
Changes:
if let responseHeaders ... line - did add as? [String:String], because allHeadersFields return type is [NSObject : AnyObject] and not [String:String] required by cookiesWithResponseHeaderFields...
removed as! [NSHTTPCookie] - it has no sense, because cookiesWithResponseHeaderFields return type is already [NSHTTPCookie]
Just check cookiesWithResponseHeaderFields signature:
class func cookiesWithResponseHeaderFields(headerFields: [String : String],
forURL URL: NSURL) -> [NSHTTPCookie]
Please read How do I ask a good question. At least, you should point out to lines where the problem is, etc.