i was wondering what's the right design to:
Make an API call
Create a model of the result
Load the image of the model
Wait for everything to be loaded ..and then update the ui
I used (nested) delegation and a dispatch_group. But i am sure, you might have some tips how to do this right, don't you?
Here is my working code:
View Controller
#IBAction func loadLoaction(sender: AnyObject) {
placeManager.loadRandomLoaction(lat, longitude: long)
}
func placeDidLoad(place: Place) {
nameLabel.text = place.name
imageView.image = place.image
}
Classes
import Foundation
import Alamofire
extension Alamofire.Request {
class func imageResponseSerializer() -> Serializer {
return { request, response, data in
if data == nil {
return (nil, nil)
}
let image = UIImage(data: data!, scale: UIScreen.mainScreen().scale)
return (image, nil)
}
}
func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self {
return response(serializer: Request.imageResponseSerializer(), completionHandler: { (request, response, image, error) in
completionHandler(request, response, image as? UIImage, error)
})
}
}
public protocol PlaceDelegate: class {
func placeDidLoad(place: Place)
}
public class Place : NSObject {
var delegate: PlaceDelegate?
public var name: String?
public var category: String?
public var image_url: String?
public var snippet_text: String?
public var address: String?
public var distance: String?
public var url: String?
public var image: UIImage = UIImage()
var dispatch_group = dispatch_group_create()
init(fromResponseDict responseDict: Dictionary<String, String>, delegate: PlaceDelegate) {
self.delegate = delegate
// Set props
self.name = responseDict["name"]
self.category = responseDict["category"]
self.image_url = responseDict["image_url"]!
self.snippet_text = responseDict["snippet_text"]
self.url = responseDict["url"]
self.address = responseDict["address"]!
super.init()
// Load image
self.loadImage()
// Dispatch if success
dispatch_group_notify(self.dispatch_group, dispatch_get_main_queue(), {
self.delegate?.placeDidLoad(self)
})
}
// (3) Load the image
func loadImage() {
dispatch_group_enter(self.dispatch_group);
var request:Alamofire.Request = Alamofire.request(.GET, self.image_url!).responseImage() {
(request, _, image, error) in
if error == nil && image != nil {
NSLog("imageRequestSuccess")
self.image = image!
// Dispatch if success
dispatch_group_leave(self.dispatch_group)
} else {
NSLog("imageRequestFailure")
// Dispatch also to handle failure
dispatch_group_leave(self.dispatch_group)
}
}
}
}
public protocol PlaceManagerDelegate: class {
func placeDidLoad(place: Place)
}
public class PlaceManager : NSObject, PlaceDelegate {
public weak var delegate: PlaceManagerDelegate?
var isLoading = false
enum Router: URLRequestConvertible {
static let baseURLString = "http://api.domain.com"
// Endpoints
case RandomPlace(lat:Double, long:Double)
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]) = {
switch self {
case .RandomPlace (let lat, let long):
let params : [ String : AnyObject] = ["ll": "\(lat),\(long)", "", "debug":"true"]
return ("/getRandomLocation", params)
}
}()
let URL = NSURL(string: Router.baseURLString)
let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
public func loadRandomLoaction(latitude:Double, longitude:Double) {
if isLoading {
return
}
isLoading = true
// (1) Make the API Call
Alamofire.request(PlaceManager.Router.RandomPlace(lat: latitude, long: longitude)).responseJSON() {
(_, _, JSON, error) in
if error == nil {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
if let responseDict = JSON as? Dictionary<String, String> {
// (2) Create the model
let place = Place(fromResponseDict: responseDict, delegate:self)
}
}
} else {
NSLog(error!.localizedDescription)
}
self.isLoading = false
}
}
// (4) Update the UI
public func placeDidLoad(place: Place) {
self.delegate?.placeDidLoad(place)
}
}
Related
In my program code which is posted below, I need to assign a variable of Swift enum type to a variable of NSObject type. However, the compiler doesn't allow this. I know this is not allowed. So, I wonder whether there is a way to change the enum somehow so that it can be assigned to an NSObject variable. Thank you!
Photo+CoreDataClass.swift
import Foundation
import CoreData
#objc(Photo)
public class Photo: NSManagedObject {
}
Photo+CoreDataProperties.swift
import Foundation
import CoreData
extension Photo {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Photo> {
return NSFetchRequest<Photo>(entityName: "Photo")
}
#NSManaged public var datetaken: String?
#NSManaged public var datetakengranularity: NSObject?
#NSManaged public var datetakenunknow: String?
#NSManaged public var farm: Int32
#NSManaged public var heightZ: Int32
#NSManaged public var photoID: String?
#NSManaged public var isfamily: Int32
#NSManaged public var isfriend: Int32
#NSManaged public var ispublic: Int32
#NSManaged public var owner: String?
#NSManaged public var secret: String?
#NSManaged public var server: String?
#NSManaged public var title: String?
#NSManaged public var urlZ: String?
#NSManaged public var widthZ: Int32
}
extension Photo : Identifiable {
}
FlickrPhoto.swift
import Foundation
// MARK: - Photo
struct FlickrPhoto: Codable {
let photoID, owner, secret, server: String
let farm: Int
let title: String
let ispublic, isfriend, isfamily: Int
let datetaken: String
let datetakengranularity: Datetakengranularity
let datetakenunknown: String
let urlZ: String?
let heightZ, widthZ: Int?
enum CodingKeys: String, CodingKey {
case owner, secret, server, farm, title, ispublic, isfriend, isfamily, datetaken, datetakengranularity, datetakenunknown
case photoID = "id"
case urlZ = "url_z"
case heightZ = "height_z"
case widthZ = "width_z"
}
}
enum Datetakengranularity: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Datetakengranularity.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Datetakengranularity"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
extension FlickrPhoto: Equatable {
static func == (lhs: FlickrPhoto, rhs: FlickrPhoto) -> Bool {
// Two Photos are the same if they have the same photoID
return lhs.photoID == rhs.photoID
}
}
PhotoStore.swift
import UIKit
import CoreData
class PhotoStore {
private let session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config)
}()
let imageStore = ImageStore()
let persistenContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Photorama")
container.loadPersistentStores { (description, error) in
if let error = error {
print("Error setting up Core Data (\(error))")
}
}
return container
}()
private func processPhotosRequest (data: Data?, error: Error?) ->
Result<[FlickrPhoto], Error> {
guard let jsonData = data else {
return .failure(error!)
}
//return FlickrAPI.photos(fromJSON: jsonData)
let context = persistenContainer.viewContext
switch FlickrAPI.photos(fromJSON: jsonData) {
case let .success(flickrPhotos):
let _ = flickrPhotos.map { flickrPhoto -> Photo in
let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
let predicate = NSPredicate(format: "\(#keyPath(Photo.photoID)) == \(flickrPhoto.photoID)")
fetchRequest.predicate = predicate
var photo: Photo!
context.performAndWait {
photo = Photo(context: context)
photo.photoID = flickrPhoto.photoID
photo.owner = flickrPhoto.owner
photo.secret = flickrPhoto.secret
photo.server = flickrPhoto.server
photo.farm = Int32(flickrPhoto.farm)
photo.title = flickrPhoto.title
photo.ispublic = Int32(flickrPhoto.ispublic)
photo.isfriend = Int32(flickrPhoto.isfriend)
photo.isfamily = Int32(flickrPhoto.isfamily)
photo.datetaken = flickrPhoto.datetaken
photo.datetakengranularity = flickrPhoto.datetakengranularity // The compiler reports error here:
// Cannot assign value of type 'Datetakengranularity' to type 'NSObject?'
photo.datetakenunknow = flickrPhoto.datetakenunknown
photo.urlZ = flickrPhoto.urlZ
photo.heightZ = Int32(flickrPhoto.heightZ!)
photo.widthZ = Int32(flickrPhoto.widthZ!)
}
return photo
}
return .success(flickrPhotos)
case let .failure(error):
return .failure(error)
}
}
func fetchAllPhotos (completion: #escaping (Result<[Photo], Error>) -> Void) -> Void {
let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
let sortByDataTaken = NSSortDescriptor(key: #keyPath(Photo.datetaken), ascending: true)
fetchRequest.sortDescriptors = [sortByDataTaken]
let viewContext = persistenContainer.viewContext
viewContext.perform {
do {
let allPhotos = try viewContext.fetch(fetchRequest)
completion(.success(allPhotos))
} catch {
completion(.failure(error))
}
}
}
func fetchRecentPhotos (completion: #escaping (Result<[FlickrPhoto], Error>) -> Void) {
let url = FlickrAPI.interestingPhotoURL
let request = URLRequest(url: url)
let task = session.dataTask(with: request) {
(data, response, error) in
var result = self.processPhotosRequest(data: data, error: error)
if case .success = result {
do {
try self.persistenContainer.viewContext.save()
} catch {
result = .failure(error)
}
}
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
func fetchImage (for photo: Photo, completion: #escaping (Result<UIImage, Error>) -> Void) {
let photoKey = photo.photoID
if let image = imageStore.image(forKey: photoKey!) {
OperationQueue.main.addOperation {
completion(.success(image))
}
return
}
guard let photoURL = photo.urlZ else { return }
guard let requestURL = URL(string: photoURL) else {
completion(.failure(PhotoError.missingImageURL))
return
}
let request = URLRequest(url: requestURL)
let task = session.dataTask(with: request) {
(data, response, error) in
let result = self.processImageRequest(data: data, error: error)
if case let .success(image) = result {
self.imageStore.setImage(image, forKey: photoKey!)
}
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
private func processImageRequest (data: Data?, error: Error?) -> Result<UIImage, Error> {
guard let imageData = data,
let image = UIImage(data: imageData) else {
// Couldn't create an image
if data == nil {
return .failure(error!)
} else {
return .failure(PhotoError.imageCreationError)
}
}
return .success(image)
}
}
enum PhotoError: Error {
case imageCreationError
case missingImageURL
}
A snapshot of the Photorama.xcdatamodeld interface is shown below:
More code will be provided if necessary. Thanks very much!
You could do it using hacky enum byte serialization stored as NSData property in your NSObject.
enum Test {
case testCase
case testCase2
}
func bytes<T>(of value: T) -> [UInt8]{
var value = value
let size = MemoryLayout<T>.size
return withUnsafePointer(to: &value, {
$0.withMemoryRebound(to: UInt8.self,
capacity: size,
{
Array(UnsafeBufferPointer(start: $0, count: size))
})
})
}
let testCase: Test = .testCase
let testCase2: Test = .testCase2
var testBytes = bytes(of: testCase)
var testBytes2 = bytes(of: testCase2)
let data = NSData(bytes: &testBytes, length: testBytes.count)
let data2 = NSData(bytes: &testBytes2, length: testBytes2.count)
let testCaseDeserialized = data.bytes.bindMemory(to: Test.self, capacity: 1).pointee
let testCase2Deserialized = data2.bytes.bindMemory(to: Test.self, capacity: 1).pointee
I tried bridging the flickrPhoto.datatakengranularity variable to NSObject via AnyObject like this:
photo.datetakengranularity = (flickrPhoto.datetakengranularity as AnyObject as! NSObject)
And the compiler made no complaints. This may be because in essence AnyObject is equivalent to NSObject and flickrPhoto.datetakengranularity is convertible to AnyObject. I don't know what happened underneath but it worked anyway. I don't know why.
Thanks to all for your concern and support!
I am new iOS Developer
I want to change the websiteLogo API with a textfield to change the URL.
how can I change the line with the ***
with a var and a textfield in my viewcontroller?
With screenshoot it's will be easier to understand what I want? Thank you !!! Guys. OneDriveLink. 1drv.ms/u/s!AsBvdkER6lq7klAqQMW9jOWQkzfl?e=fyqOeN
private init() {}
**private static var pictureUrl = URL(string: "https://logo.clearbit.com/:http://www.rds.ca")!**
private var task: URLSessionDataTask?
func getQuote(callback: #escaping (Bool, imageLogo?) -> Void) {
let session = URLSession(configuration: .default)
task?.cancel()
task = session.dataTask(with: QuoteService.pictureUrl) { (data, response, error) in
DispatchQueue.main.async {
guard let data = data, error == nil else {
callback(false, nil)
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
callback(false, nil)
return
}
let quote = imageLogo(image: data)
callback(true, quote)
print(data)
}
}
task?.resume()
}
First, please don't use screenshots do show your code. If you want help, others typically copy/paste your code to check whats wrong with it.
There are some minor issues with your code. Some hints from me:
Start your types with a big letter, like ImageLogo not imageLogo:
Avoid statics
Avoid singletons (they are almost statics)
Hand in the pictureUrl into getQuote
struct ImageLogo {
var image:Data
}
class QuoteService {
private var task: URLSessionDataTask?
func getQuote(from pictureUrl:URL, callback: #escaping (Bool, ImageLogo?) -> Void) {
let session = URLSession(configuration: .default)
task?.cancel()
task = session.dataTask(with: pictureUrl) {
(data, response, error) in
DispatchQueue.main.async {
guard let data = data, error == nil else {
callback(false, nil)
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
callback(false, nil)
return
}
let quote = ImageLogo(image: data)
callback(true, quote)
print(data)
}
}
task?.resume()
}
}
Store an instance of QuoteService in your view controller
Call getQuote on that instance, handing in the pictureUrl
class ViewController : UIViewController {
var quoteService:QuoteService!
override func viewDidLoad() {
self.quoteService = QuoteService()
}
func toggleActivityIndicator(shown:Bool) { /* ... */ }
func update(quote:ImageLogo) { /* ... */ }
func presentAlert() { /* ... */ }
func updateconcept() {
guard let url = URL(string:textField.text!) else {
print ("invalid url")
return
}
toggleActivityIndicator(shown:true)
quoteService.getQuote(from:url) {
(success, quote) in
self.toggleActivityIndicator(shown:false)
if success, let quote = quote {
self.update(quote:quote)
} else {
self.presentAlert()
}
}
}
/* ... */
}
Hope it helps.
I think you want to pass textfield Text(URL Enter By user) in Web Services
Add a parameter url_str in getQuote function definition first and pass textfield value on that parameters
fun getQuote(url_str : String, callback : #escaping(Bool, ImgaeLogo/)->void){
}
I have a WKWebView that is used to present the login screen of my OAuth Identity provider.
import UIKit
import WebKit
protocol OAuth2WKWebViewDelegate: class {
func didReceiveAuthorizationCode(_ code: String) -> Void
func didRevokeSession() -> Void
}
class OAuth2WKWebViewController: UIViewController {
let targetUrl: URLComponents
let webView = WKWebView()
weak var delegate: OAuth2WKWebViewDelegate?
init(targetUrl: URLComponents) {
self.targetUrl = targetUrl
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
loadUrl()
}
}
extension OAuth2WKWebViewController: WKNavigationDelegate {
func loadUrl() {
guard let url = targetUrl.url else { return }
view = webView
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
webView.navigationDelegate = self
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
if url.scheme == "appdev", url.absoluteString.range(of: "code") != nil {
let urlParts = url.absoluteString.components(separatedBy: "?")
let code = urlParts[1].components(separatedBy: "code=")[1]
delegate?.didReceiveAuthorizationCode(code)
}
if url.absoluteString == "appdev://oauth-callback-after-sign-out" {
delegate?.didRevokeSession()
}
}
decisionHandler(.allow)
}
}
I also have an IdentityService I use to present this view and respond to it's success / error.
protocol IdentityServiceProtocol {
var hasValidToken: Bool { get }
func initAuthCodeFlow() -> Void
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
func storeOAuthTokens(accessToken: String, refreshToken: String, completion: #escaping () -> Void) -> Void
func renderAuthView() -> Void
}
class IdentityService: IdentityServiceProtocol {
fileprivate var apiClient: APIClient
fileprivate var keyChainService: KeyChainService
init(apiClient: APIClient = APIClient(), keyChainService: KeyChainService = KeyChainService()) {
self.apiClient = apiClient
self.keyChainService = keyChainService
}
var hasValidToken: Bool {
return keyChainService.fetchSingleObject(withKey: "AccessToken") != nil
}
func initAuthCodeFlow() -> Void {
let queryItems = ["response_type": "code", "client_id": clientId, "redirect_uri": redirectUri, "state": state, "scope": scope]
renderOAuthWebView(forService: .auth(company: "benefex"), queryitems: queryItems)
}
func initRevokeSession() -> Void {
guard let refreshToken = keyChainService.fetchSingleObject(withKey: "RefreshToken") else { return }
let queryItems = ["refresh_token": refreshToken]
renderOAuthWebView(forService: .revokeSession(company: "benefex"), queryitems: queryItems)
}
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {
guard let targetUrl = constructURLComponents(endPoint: service, queryItems: queryitems) else { return }
let webView = OAuth2WKWebViewController(targetUrl: targetUrl)
webView.delegate = self
UIApplication.shared.windows.first?.rootViewController = webView
}
func storeOAuthTokens(accessToken: String, refreshToken: String, completion: #escaping ()-> Void) -> Void {
let success = keyChainService.storeManyObjects(["AccessToken": accessToken, "RefreshToken": refreshToken])
guard success == true else { return }
completion()
}
func renderAuthView() -> Void {
UIApplication.shared.windows.first?.rootViewController = UINavigationController.init(rootViewController: AuthenticatedViewController())
}
}
extension IdentityService: OAuth2WKWebViewDelegate {
func didReceiveAuthorizationCode(_ code: String) {
apiClient.call(endpoint: IdentityEndpoint.accessToken(company: "benefex", code: code)) { [weak self] (response: OAuthTokenResponse) in
switch response {
case .success(let payload):
guard let accessToken = payload.accessToken, let refreshToken = payload.refreshToken else { return }
self?.storeOAuthTokens(accessToken: accessToken, refreshToken: refreshToken) { self?.renderAuthView() }
case .error:
// login failed for some reason
print("could not complete request for access token")
}
}
}
func didRevokeSession() {
print("This was called")
}
}
extension IdentityService {
fileprivate var state: String {
return generateState(withLength: 20)
}
fileprivate func constructURLComponents(endPoint: IdentityEndpoint, queryItems: [String: String]) -> URLComponents? {
var url = URLComponents(url: endPoint.baseUrl, resolvingAgainstBaseURL: false)
url?.path = endPoint.path
url?.queryItems = queryItems.map { URLQueryItem(name: $0.key, value: $0.value) }
return url
}
fileprivate func generateState(withLength len: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let length = UInt32(letters.count)
var randomString = ""
for _ in 0..<len {
let rand = arc4random_uniform(length)
let idx = letters.index(letters.startIndex, offsetBy: Int(rand))
let letter = letters[idx]
randomString += String(letter)
}
return randomString
}
}
extension IdentityService {
var clientId: String {
let envVar = ProcessInfo.processInfo.environment
guard let value = envVar["APP_CLIENT_ID"] else { fatalError("Missing APP_CLIENT_ID enviroment variable") }
return value
}
var redirectUri: String {
let envVar = ProcessInfo.processInfo.environment
guard let value = envVar["APP_REDIRECT_URI"] else { fatalError("Missing APP_REDIRECT_URI enviroment variable") }
return value
}
var scope: String {
let envVar = ProcessInfo.processInfo.environment
guard let value = envVar["APP_SCOPES"] else { fatalError("Missing APP_SCOPES enviroment variable") }
return value
}
}
The delegate?.didReceiveAuthorizationCode(code) is working.
However when delegate?.didRevokeSession() is called from the WebView, the identity service does not respond.
I added some console logs and can see my IdentityService is being de - init when I invoke the logout method.
I believe this is causing it to do nothing when the delegate method fires.
How can I ensure the delegate method is called still?
This came up when I was searching for answers to my issue - if you are using the 10.2 or 10.2.1 compiler - an issue was occurring for us when compiling in Release (instead of Debug) - where the delegate functions are not being called
The fix for us was to include #objc before all delegate function calls, IE
#objc func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
If you are in the same scenario - this should help
I want to get the .get data from the access token but It seems impossible, I try since few hours to resolve my bug. How can I fix it? How can I resolve this bug and get response from WebView NSURL Request?
import Foundation
class User {
var id: String?
var userName: String?
var fullName: String?
var profilePicture: String?
var bio: String?
var website: String?
var mediaCount: String?
var followsCount: Int?
var followedByCount: Int?
init(userDict:[String:AnyObject]) {
self.id = userDict["id"] as? String
self.userName = userDict["username"] as? String
self.fullName = userDict["full_name"] as? String
self.profilePicture = userDict["profile_picture"] as? String
self.bio = userDict["bio"] as? String
self.website = userDict["website"] as? String
self.mediaCount = userDict["media"] as? String
if let countsDict = userDict["counts"] as? [String: AnyObject] {
self.followsCount = countsDict["follows"] as? Int
self.followedByCount = countsDict["followed_by"] as? Int
}
}
class func fetchUserInfo(withToken token: String, completionHandler: (User?, NSError?)->()) {
var user: User?
let url = NSURL(string: "https://api.instagram.com/v1/users/self/?access_token=\(token)")!
NSURLSession().dataTaskWithURL(url) { (data, response, error) in
guard error == nil else { return }
do {
if let jsonData = data,
let jsonDataDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject],
let jsonUserDict = jsonDataDict["data"] as? [String: AnyObject] {
user = User(userDict: jsonUserDict)
dispatch_async(dispatch_get_main_queue(), {
completionHandler(user, error)
})
}
} catch let err as NSError {
print(err.debugDescription)
}
}.resume()
}
var user = fetchUserInfo(withToken: "3923891960.a56f59d.7f2376b5acae4abf8f98eaf2a575adXX", completionHandler: updateUI)
func updateUI(user: User?, error: NSError?) {
avatarImage = user.profilePicture
mediaLabel.text = user.mediaCount
followsLabel.text = user.followsCount
followedBy.text = user.followedByCount
username.text = user.userName
full_name.text = user.fullName
bioLabel.text = user.bio
websiteLabel.text = user.website
}
}
//
// TableViewCell.swift
// CodeTaskInstagram
//
//
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet var avatarImage: UIImageView!
#IBOutlet var mediaLabel: UILabel!
#IBOutlet var followsLabel: UILabel!
#IBOutlet var followedBy: UILabel!
#IBOutlet var username: UILabel!
#IBOutlet var full_name: UILabel!
#IBOutlet var bioLabel: UILabel!
#IBOutlet var websiteLabel: UILabel!
#IBOutlet var idLabel: UILabel!
#IBOutlet var labelRecents: UILabel!
}
Instagram authorization with getting access_token in swift sample code:
Before use
Do not forget to fill
let redirectURI = ""
let clientID = ""
let clientSecret = ""
in NetworkManager.swift file
Code
User.swift
import Foundation
class User {
var id: String?
var userName: String?
var fullName: String?
var profilePicture: String?
var bio: String?
var website: String?
var followsCount: Int?
var followedByCount: Int?
init(userDict:[String:AnyObject]) {
self.id = userDict["id"] as? String
self.userName = userDict["username"] as? String
self.fullName = userDict["full_name"] as? String
self.profilePicture = userDict["profile_picture"] as? String
self.bio = userDict["bio"] as? String
self.website = userDict["website"] as? String
if let countsDict = userDict["counts"] as? [String: AnyObject] {
self.followsCount = countsDict["follows"] as? Int
self.followedByCount = countsDict["followed_by"] as? Int
}
}
var description: String {
get {
var result = ""
result += descriptionString("id", value: id)
result += descriptionString("userName", value: userName)
result += descriptionString("fullName", value: fullName)
result += descriptionString("profilePicture", value: profilePicture)
result += descriptionString("bio", value: bio)
result += descriptionString("website", value: website)
result += descriptionInt("followsCount", value: followsCount)
result += descriptionInt("followedByCount", value: followedByCount)
return result
}
}
private func descriptionString(name: String, value: String?) -> String{
var result = "\(name): "
if let _value = value {
result += _value
} else {
result += "nil"
}
return result + "\n"
}
private func descriptionInt(name: String, value: Int?) -> String{
var result = "\(name): "
if let _value = value {
result += "\(_value)"
} else {
result += "nil"
}
return result + "\n"
}
}
NetworkManager.swift
import Foundation
class NetworkManager {
var delegate: NetworkManagerDelegate?
var accessToken: String? = nil
var code: String? = nil
let baseURLString = "https://api.instagram.com"
let clientID = ""
let redirectURI = "https://www.instagram.com/"
let clientSecret = ""
static let InstagramResponseError = "InstagramResponseError"
}
// MARK: Get/Set
extension NetworkManager {
var authentificationUrl: String {
get {
return "\(baseURLString)/oauth/authorize/?client_id=\(clientID)&redirect_uri=\(redirectURI)&response_type=code"
}
}
class var sharedInstance: NetworkManager {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: NetworkManager? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = NetworkManager()
}
return Static.instance!
}
}
// MARK: Errors
extension NetworkManager {
func fetchError(jsonDataDict: [String: AnyObject]) -> NSError? {
var error: NSError? = nil
if let meta = jsonDataDict["meta"] as? [String:AnyObject] {
if let code = meta["code"] as? Int {
switch code {
case 400:
error = NSError(domain: NetworkManager.InstagramResponseError, code: code, userInfo: meta)
default:
break
}
}
}
return error
}
}
// MARK: Load Data
extension NetworkManager {
func newRequest(url: String, HTTPMethod: String, paramString: String?, completionHandler: ([String: AnyObject]?, NSURLResponse?, NSError?) -> Void) {
if let url = NSURL(string:url) {
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = HTTPMethod
if let paramString = paramString {
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)!
}
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
do {
var json: [String: AnyObject]? = nil
var responseError: NSError? = error
if let jsonData = data {
if let jsonDataDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
if let _responseError = self.fetchError(jsonDataDict) {
responseError = _responseError
}
json = jsonDataDict
}
}
completionHandler(json, response, responseError)
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
}
func getAccessToken() {
if let code = self.code {
let urlString = "\(baseURLString)/oauth/access_token"
let paramString = "client_id=\(clientID)&client_secret=\(clientSecret)&grant_type=authorization_code&redirect_uri=\(redirectURI)&code=\(code)&scope=basic+public_content"
newRequest(urlString, HTTPMethod: "POST", paramString: paramString) { (json, response, error) in
if let json = json {
if let access_token = json["access_token"] as? String {
self.accessToken = access_token
NSLog("access_token: \(access_token)")
} else {
self.accessToken = nil
}
}
if let delegate = self.delegate {
delegate.getAccessTokenDidEnd(self.accessToken, error: error)
}
}
}
}
func loadProfile() {
if let accessToken = self.accessToken {
let urlString = "https://api.instagram.com/v1/users/self/?access_token=\(accessToken)"
newRequest(urlString, HTTPMethod: "GET", paramString: nil) { (json, response, error) in
var user: User? = nil
if let json = json {
if let userData = json["data"] as? [String:AnyObject] {
user = User(userDict: userData)
}
}
if let delegate = self.delegate {
delegate.loadProfileDidEnd(user, error: error)
}
}
}
}
}
NetworkManagerDelegate.swift
import Foundation
protocol NetworkManagerDelegate {
func getAccessTokenDidEnd(accessToken: String?, error: NSError?)
func loadProfileDidEnd(user: User?, error: NSError?)
}
ProfileTableViewCell.swift
import UIKit
class ProfileTableViewCell: UITableViewCell {
#IBOutlet var idLabel: UILabel!
#IBOutlet var userNameLabel: UILabel!
#IBOutlet var fullNameLabel: UILabel!
#IBOutlet var profilePictureLabel: UILabel!
#IBOutlet var bioLabel: UILabel!
#IBOutlet var websiteLabel: UILabel!
#IBOutlet var followsCountLabel: UILabel!
#IBOutlet var followedByCountLabel: UILabel!
func setData(user: User) {
setLabel(idLabel, value: user.id)
setLabel(userNameLabel, value: user.userName)
setLabel(fullNameLabel, value: user.fullName)
setLabel(profilePictureLabel, value: user.profilePicture)
setLabel(bioLabel, value: user.bio)
setLabel(websiteLabel, value: user.website)
setLabel(followsCountLabel, value: user.followsCount)
setLabel(followedByCountLabel, value: user.followedByCount)
}
private func setLabel(label: UILabel, value: String?) {
if let _value = value {
label.text = _value
} else {
label.text = "-"
}
}
private func setLabel(label: UILabel, value: Int?) {
if let _value = value {
label.text = "\(_value)"
} else {
label.text = "-"
}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
private var webView: UIWebView!
#IBOutlet var tableView: UITableView!
let instagramManager = NetworkManager.sharedInstance
var user: User? = nil
override func viewDidLoad() {
super.viewDidLoad()
instagramManager.delegate = self
//log out
// deleteChache()
initWebView()
initTableView()
}
private func actionWhenUserLogedIn() {
self.webView.hidden = true
}
func deleteChache() {
NSURLCache.sharedURLCache().removeAllCachedResponses()
if let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies {
for cookie in cookies {
NSHTTPCookieStorage.sharedHTTPCookieStorage().deleteCookie(cookie)
}
}
}
}
// MARK: NetworkManagerDelegate
extension ViewController: NetworkManagerDelegate {
func initNetworkManager() {
instagramManager.delegate = self
}
func getAccessTokenDidEnd(accessToken: String?, error: NSError?) {
instagramManager.loadProfile()
}
func loadProfileDidEnd(user: User?, error: NSError?) {
self.user = user
dispatch_async(dispatch_get_main_queue()) {
self.tableView.hidden = false
self.tableView.reloadData()
}
}
}
// MARK: UIWebView
extension ViewController: UIWebViewDelegate {
private func initWebView() {
webView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: UIScreen.mainScreen().bounds.height))
webView.delegate = self
view.addSubview(webView)
authorizationRequestInWebView()
}
private func authorizationRequestInWebView() {
if let url = NSURL(string: instagramManager.authentificationUrl) {
let request = NSURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0)
webView.loadRequest(request)
}
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let urlString = (request.URL?.absoluteString)!
if let range = urlString.rangeOfString(instagramManager.redirectURI + "?code=") {
let location = range.endIndex
let code = urlString.substringFromIndex(location)
instagramManager.code = code
NSLog("code: \(code)")
instagramManager.getAccessToken()
actionWhenUserLogedIn()
return false
}
return true
}
}
// MARK: UITableView
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func initTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.hidden = true
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ProfileTableViewCell")!
if let profileTableViewCell = cell as? ProfileTableViewCell {
if let user = self.user {
profileTableViewCell.setData(user)
}
}
return cell
}
}
Main.storyboard
I have been using UIImageView+AFNetworking.swift in my application to download images, but after updating to Xcode 7, it gives me an error.
Here is the full code:
import UIKit
#objc public protocol AFImageCacheProtocol:class{
func cachedImageForRequest(request:NSURLRequest) -> UIImage?
func cacheImage(image:UIImage, forRequest request:NSURLRequest);
}
extension UIImageView {
private struct AssociatedKeys {
static var SharedImageCache = "SharedImageCache"
static var RequestImageOperation = "RequestImageOperation"
static var URLRequestImage = "UrlRequestImage"
}
public class func setSharedImageCache(cache:AFImageCacheProtocol?) {
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN))
}
public class func sharedImageCache() -> AFImageCacheProtocol {
struct Static {
static var token : dispatch_once_t = 0
static var defaultImageCache:AFImageCache?
}
dispatch_once(&Static.token, { () -> Void in
Static.defaultImageCache = AFImageCache()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationDidReceiveMemoryWarningNotification, object: nil, queue: NSOperationQueue.mainQueue()) { (NSNotification) -> Void in
Static.defaultImageCache!.removeAllObjects()
}
})
return objc_getAssociatedObject(self, &AssociatedKeys.SharedImageCache) as? AFImageCacheProtocol ?? Static.defaultImageCache!
}
private class func af_sharedImageRequestOperationQueue() -> NSOperationQueue {
struct Static {
static var token:dispatch_once_t = 0
static var queue:NSOperationQueue?
}
dispatch_once(&Static.token, { () -> Void in
Static.queue = NSOperationQueue()
Static.queue!.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
})
return Static.queue!
}
private var af_requestImageOperation:(operation:NSOperation?, request: NSURLRequest?) {
get {
let operation:NSOperation? = objc_getAssociatedObject(self, &AssociatedKeys.RequestImageOperation) as? NSOperation
let request:NSURLRequest? = objc_getAssociatedObject(self, &AssociatedKeys.URLRequestImage) as? NSURLRequest
return (operation, request)
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.RequestImageOperation, newValue.operation, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
objc_setAssociatedObject(self, &AssociatedKeys.URLRequestImage, newValue.request, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
public func setImageWithUrl(url:NSURL, placeHolderImage:UIImage? = nil) {
let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.addValue("image/*", forHTTPHeaderField: "Accept")
self.setImageWithUrlRequest(request, placeHolderImage: placeHolderImage, success: nil, failure: nil)
}
public func setImageWithUrlRequest(request:NSURLRequest, placeHolderImage:UIImage? = nil,
success:((request:NSURLRequest?, response:NSURLResponse?, image:UIImage, fromCache:Bool) -> Void)?,
failure:((request:NSURLRequest?, response:NSURLResponse?, error:NSError) -> Void)?)
{
self.cancelImageRequestOperation()
if let cachedImage = UIImageView.sharedImageCache().cachedImageForRequest(request) {
if success != nil {
success!(request: nil, response:nil, image: cachedImage, fromCache:true)
}
else {
self.image = cachedImage
}
return
}
if placeHolderImage != nil {
self.image = placeHolderImage
}
self.af_requestImageOperation = (NSBlockOperation(block: { () -> Void in
var response:NSURLResponse?
var error:NSError?
let data: NSData?
do {
data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
} catch let error1 as NSError {
error = error1
data = nil
} catch {
fatalError()
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if request.URL!.isEqual(self.af_requestImageOperation.request?.URL) {
let image:UIImage? = (data != nil ? UIImage(data: data!) : nil)
if image != nil {
if success != nil {
success!(request: request, response: response, image: image!, fromCache:false)
}
else {
self.image = image!
}
UIImageView.sharedImageCache().cacheImage(image!, forRequest: request)
}
else {
if failure != nil {
failure!(request: request, response:response, error: error!)
}
}
self.af_requestImageOperation = (nil, nil)
}
})
}), request)
UIImageView.af_sharedImageRequestOperationQueue().addOperation(self.af_requestImageOperation.operation!)
}
private func cancelImageRequestOperation() {
self.af_requestImageOperation.operation?.cancel()
self.af_requestImageOperation = (nil, nil)
}
}
func AFImageCacheKeyFromURLRequest(request:NSURLRequest) -> String {
return request.URL!.absoluteString
}
class AFImageCache: NSCache, AFImageCacheProtocol {
func cachedImageForRequest(request: NSURLRequest) -> UIImage? {
switch request.cachePolicy {
case NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData,
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData:
return nil
default:
break
}
return self.objectForKey(AFImageCacheKeyFromURLRequest(request)) as? UIImage
}
func cacheImage(image: UIImage, forRequest request: NSURLRequest) {
self.setObject(image, forKey: AFImageCacheKeyFromURLRequest(request))
}
}
Error image:
Error 1:
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN))
Error 2:
objc_setAssociatedObject(self, &AssociatedKeys.RequestImageOperation, newValue.operation, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
Error 3:
objc_setAssociatedObject(self, &AssociatedKeys.URLRequestImage, newValue.request, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
Does anyone has any idea how I can fix this?
The reason for this is exactly as the error states; that UInt cannot be created with an argument of type objc_AssociationPolicy.
objc_setAssociatedObject expects the policy parameter to be of type objc_AssociationPolicy, you don't need to convert it to UInt at all.
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)