Get a macOS Keychain certificate's SHA1 hash in Swift - macos

I've retrieved the set of certificates in my keychain using this code:
let query: [String: Any] = [
kSecClass as String: kSecClassCertificate,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: false,
kSecReturnData as String: true
]
var result: CFTypeRef?
var results : Set<CertsResult> = []
let status = SecItemCopyMatching(query as CFDictionary, &result)
//[Check status]
guard let certificateData = result as? [CFData] else {
//[Handle]
}
From here, I loop through certificateData and gather information about the certificates, but I need to get the SHA1 hash of the certificates as well. I've gathered from researching that I need to use import CommonCrypto and CC_SHA1, but what I've read doesn't use a CFData.
Is there a good way to get from this point to its SHA1?

You can achieve it by performing the hash yourself. The fingerprints are not part of the certificate itself. More info about that over here.
import CryptoKit
let certificate = ...
let der = SecCertificateCopyData(certificate) as Data
let sha1 = Insecure.SHA1.hash(data: der)
let sha256 = SHA256.hash(data: der)
This can be created in an extension too. I've used CommonCrypto in the extension.
import CommonCrypto
extension SecCertificate {
var sha1: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA1(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
var sha256: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA256(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
}
I'd like to mention that SHA-1 hashes of certificates are deprecated since like 2017 and websites and tech giants are starting to drop support for them.
Playground example
import CryptoKit
import Foundation
class CertificateStuff: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.rejectProtectionSpace, nil)
return
}
for index in 0 ..< SecTrustGetCertificateCount(serverTrust) {
let certificate = SecTrustGetCertificateAtIndex(serverTrust, index)!
let der = SecCertificateCopyData(certificate)
let sha1 = Insecure.SHA1.hash(data: der as Data)
let sha256 = SHA256.hash(data: der as Data)
print(certificate)
print(sha1)
print(sha256)
print()
}
completionHandler(.performDefaultHandling, nil)
}
func request(_ done: #escaping (Result<Data, Error>) -> Void) {
let url = URL(string: "https://security.stackexchange.com/questions/14330/what-is-the-actual-value-of-a-certificate-fingerprint")!
let request = URLRequest(url: url)
URLSession(configuration: .default, delegate: self, delegateQueue: nil).dataTask(with: request) { (d, r, e) in
if let e = e {
print(e)
return
}
print(d!)
}.resume()
}
}
CertificateStuff().request { result in print(result) }

Related

Storing/retriving a Codable Dictionary of structs in UserDefaults doesn't work for me

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.

OSStatus Code -1009, com.apple.LocalAuthentication

I'm trying to test encryption using the iOS keychain.
Domain=com.apple.LocalAuthentication Code=-1009 "ACL operation is not allowed: 'od'" UserInfo={NSLocalizedDescription=ACL operation is not allowed: 'od'}
This is my test code:
func testEncrpytKeychain() {
let promise = expectation(description: "Unlock")
let data: Data! = self.sampleData
let text: String! = self.sampleText
wait(for: [promise], timeout: 30)
let chain = Keychain(account: "tester", serviceName: "testing2", access: .whenPasscodeSetThisDeviceOnly, accessGroup: nil)
chain.unlockChain { reply, error in
defer {
promise.fulfill()
}
guard error == nil else {
// ** FAILS ON THIS LINE WITH OSSTATUS ERROR **
XCTAssert(false, "Error: \(String(describing: error))")
return
}
guard let cipherData = try? chain.encrypt(data) else {
XCTAssert(false, "Cipher Data not created")
return
}
XCTAssertNotEqual(cipherData, data)
guard let clearData = try? chain.decrypt(cipherData) else {
XCTAssert(false, "Clear Data not decrypted")
return
}
XCTAssertEqual(clearData, data)
let clearText = String(data: clearData, encoding: .utf8)
XCTAssertEqual(clearText, text)
}
}
And this is the underlying async unlockChain code:
// context is a LAContext
func unlockChain(_ callback: #escaping (Bool, Error?) -> Void) {
var error: NSError? = nil
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
callback(false, error)
return
}
context.evaluateAccessControl(control, operation: .createItem, localizedReason: "Access your Account") { (reply, error) in
self.context.evaluateAccessControl(self.control, operation: .useItem, localizedReason: "Access your Account") { (reply, error) in
self.unlocked = reply
callback(reply, error)
}
}
}
Here is how the context and control objects are made
init(account: String, serviceName: String = (Bundle.main.bundleIdentifier ?? ""), access: Accessibility = .whenUnlocked, accessGroup: String? = nil) {
self.account = account
self.serviceName = serviceName
self.accessGroup = accessGroup
self.access = access
var error: Unmanaged<CFError>? = nil
self.control = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
access.attrValue,
[.privateKeyUsage],
&error)
if let e: Error = error?.takeRetainedValue() {
Log.error(e)
}
self.context = LAContext()
}
I can't find a single bit of information about this error:
Domain=com.apple.LocalAuthentication Code=-1009
the OSStatus Code site doesn't contain anything for it either
any help is appreciated, thanks.
I solved the same issue by removing the previous private key before creating a new one.
I would guess that on iOS10 (11 was not showing up the error), when you SecKeyCreateRandomKey(...) with the same tag/size but not the same access settings, it would just return true but use the old one (feels odd but who knows)?
Here is a lazy C function I just made to remove it (just remember to set your ApplicationPrivateKeyTag:
void deletePrivateKey()
{
CFStringRef ApplicationPrivateKeyTag = CFSTR("your tag here");
const void* keys[] = {
kSecAttrApplicationTag,
kSecClass,
kSecAttrKeyClass,
kSecReturnRef,
};
const void* values[] = {
ApplicationPrivateKeyTag,
kSecClassKey,
kSecAttrKeyClassPrivate,
kCFBooleanTrue,
};
CFDictionaryRef params = CFDictionaryCreate(kCFAllocatorDefault, keys, values, (sizeof(keys)/sizeof(void*)), NULL, NULL);
OSStatus status = SecItemDelete(params);
if (params) CFRelease(params);
if (ApplicationPrivateKeyTag) CFRelease(ApplicationPrivateKeyTag);
if (status == errSecSuccess)
return true;
return false;
}
FWIW: it looks like apple updated their doc about the Security Framework and the SecureEnclave, it's a bit easier to understand now.

How to use common crypto and/or calculate sha256 in swift 2 & 3

I am trying to make hash a password value according to sha256. I already search this but there is no info about swift 2. This solution did not worked for me
func sha256(data:String) -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
}
It gives error: Use of unresolved identifier CC_SHA256_DIGEST_LENGTH
Add a bridging header and add the import to it:
#import <CommonCrypto/CommonDigest.h>
Swift 3:
func sha256(string: String) -> Data? {
guard let messageData = string.data(using:String.Encoding.utf8) else { return nil }
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData
}
// Test
let shaData = sha256(string:"Here is the test string")
let shaHex = shaData!.map { String(format: "%02hhx", $0) }.joined()
print("shaHex: \(shaHex)")
shaHex: 6f5c446883a3049caf8368b4bad2d2ff045a39d467ee20a8d34d5698e649fe21
Swift 2:
func sha256(string string: String) -> NSData {
let digest = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))!
if let data :NSData = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_SHA256(data.bytes, CC_LONG(data.length),
UnsafeMutablePointer<UInt8>(digest.mutableBytes))
}
return digest
}
//Test:
let digest = sha256(string:"Here is the test string")
print("digest: \(digest)")
Output:
digest: 6f5c4468 83a3049c af8368b4 bad2d2ff 045a39d4 67ee20a8 d34d5698 e649fe21
Example from sunsetted documentation section:
HMAC with MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3+)
These functions will hash either String or Data input with one of eight cryptographic hash algorithms.
The name parameter specifies the hash function name as a String
Supported functions are MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
These functions takes a hash name, message to be hashed, a key and return a digest:
hashName: name of a hash function as String
message: message as Data
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:Data, key:Data) -> Data? {
let algos = ["SHA1": (kCCHmacAlgSHA1, CC_SHA1_DIGEST_LENGTH),
"MD5": (kCCHmacAlgMD5, CC_MD5_DIGEST_LENGTH),
"SHA224": (kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH),
"SHA256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
"SHA384": (kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH),
"SHA512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
guard let (hashAlgorithm, length) = algos[hashName] else { return nil }
var macData = Data(count: Int(length))
macData.withUnsafeMutableBytes {macBytes in
message.withUnsafeBytes {messageBytes in
key.withUnsafeBytes {keyBytes in
CCHmac(CCHmacAlgorithm(hashAlgorithm),
keyBytes, key.count,
messageBytes, message.count,
macBytes)
}
}
}
return macData
}
hashName: name of a hash function as String
message: message as String
key: key as String
returns: digest as Data
func hmac(hashName:String, message:String, key:String) -> Data? {
let messageData = message.data(using:.utf8)!
let keyData = key.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:keyData)
}
hashName: name of a hash function as String
message: message as String
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:String, key:Data) -> Data? {
let messageData = message.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:key)
}
// Examples
let clearString = "clearData0123456"
let keyString = "keyData8901234562"
let clearData = clearString.data(using:.utf8)!
let keyData = keyString.data(using:.utf8)!
print("clearString: \(clearString)")
print("keyString: \(keyString)")
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
let hmacData1 = hmac(hashName:"SHA1", message:clearData, key:keyData)
print("hmacData1: \(hmacData1! as NSData)")
let hmacData2 = hmac(hashName:"SHA1", message:clearString, key:keyString)
print("hmacData2: \(hmacData2! as NSData)")
let hmacData3 = hmac(hashName:"SHA1", message:clearString, key:keyData)
print("hmacData3: \(hmacData3! as NSData)")
Output:
clearString: clearData0123456
keyString: keyData8901234562
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536 32>
hmacData1: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData2: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData3: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3+)
These functions will hash either String or Data input with one of eight cryptographic hash algorithms.
The name parameter specifies the hash function name as a String
Supported functions are MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
a
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
This function takes a hash name and String to be hashed and returns a Data:
name: A name of a hash function as a String
string: The String to be hashed
returns: the hashed result as Data
func hash(name:String, string:String) -> Data? {
let data = string.data(using:.utf8)!
return hash(name:name, data:data)
}
Examples:
let clearString = "clearData0123456"
let clearData = clearString.data(using:.utf8)!
print("clearString: \(clearString)")
print("clearData: \(clearData as NSData)")
let hashSHA256 = hash(name:"SHA256", string:clearString)
print("hashSHA256: \(hashSHA256! as NSData)")
let hashMD5 = hash(name:"MD5", data:clearData)
print("hashMD5: \(hashMD5! as NSData)")
Output:
clearString: clearData0123456
clearData: <636c6561 72446174 61303132 33343536>
hashSHA256: <aabc766b 6b357564 e41f4f91 2d494bcc bfa16924 b574abbd ba9e3e9d a0c8920a>
hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6>

iOS 9(Swift 2.0): cannot invoke 'dataTaskwithURL' with an argument list of type '(NSURL, (_, _,_)throws -> Void)'

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()

Swift 2.0: Could not find overload. Need Explanation

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 :)

Resources